Merge commit '4f8d9cc38b58363e5810c4734f95350fc9b1e889' into dev-release
diff --git a/.gitignore b/.gitignore
index 080826a..7a9429b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -114,6 +114,8 @@
 third_party/kotlin/kotlin-compiler-1.4.20
 third_party/kotlin/kotlin-compiler-1.5.0.tar.gz
 third_party/kotlin/kotlin-compiler-1.5.0
+third_party/kotlin/kotlin-compiler-dev.tar.gz
+third_party/kotlin/kotlin-compiler-dev
 third_party/kotlinx-coroutines-1.3.6.tar.gz
 third_party/kotlinx-coroutines-1.3.6
 third_party/nest/*
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index c5485c1..ae6487f 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -489,6 +489,14 @@
       }
     }
     builders {
+      name: "linux-kotlin-dev"
+      mixins: "linux"
+      mixins: "normal"
+      recipe {
+        properties_j: "test_options:[\"--no_internal\", \"--runtimes=dex-default:jdk11\", \"--kotlin-compiler-dev\", \"--one_line_per_test\", \"--archive_failures\", \"*kotlin*\"]"
+      }
+    }
+    builders {
       name: "windows"
       mixins: "win"
       recipe {
diff --git a/infra/config/global/generated/cr-buildbucket.cfg b/infra/config/global/generated/cr-buildbucket.cfg
index c105138..3d645b7 100644
--- a/infra/config/global/generated/cr-buildbucket.cfg
+++ b/infra/config/global/generated/cr-buildbucket.cfg
@@ -83,6 +83,48 @@
       service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
     }
     builders {
+      name: "desugared_library_head"
+      swarming_host: "chrome-swarming.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-16.04"
+      dimensions: "pool:luci.r8.ci"
+      recipe {
+        name: "rex"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
+        cipd_version: "refs/heads/master"
+        properties_j: "builder_group:\"internal.client.r8\""
+        properties_j: "test_options:[\"--no_internal\",\"--desugared-library\",\"HEAD\"]"
+      }
+      priority: 26
+      execution_timeout_secs: 43200
+      expiration_secs: 126000
+      build_numbers: YES
+      service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+    }
+    builders {
+      name: "desugared_library_jdk11_head"
+      swarming_host: "chrome-swarming.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-16.04"
+      dimensions: "pool:luci.r8.ci"
+      recipe {
+        name: "rex"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
+        cipd_version: "refs/heads/master"
+        properties_j: "builder_group:\"internal.client.r8\""
+        properties_j: "test_options:[\"--no_internal\",\"--desugared-library\",\"HEAD\",\"--desugared-library-configuration\",\"jdk11\"]"
+      }
+      priority: 26
+      execution_timeout_secs: 43200
+      expiration_secs: 126000
+      build_numbers: YES
+      service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+    }
+    builders {
       name: "linux-android-4.0.4"
       swarming_host: "chrome-swarming.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
@@ -747,6 +789,27 @@
       service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
     }
     builders {
+      name: "linux-kotlin-dev"
+      swarming_host: "chrome-swarming.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-16.04"
+      dimensions: "pool:luci.r8.ci"
+      recipe {
+        name: "rex"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
+        cipd_version: "refs/heads/master"
+        properties_j: "builder_group:\"internal.client.r8\""
+        properties_j: "test_options:[\"--runtimes=dex-default:jdk11\",\"--kotlin-compiler-dev\",\"--one_line_per_test\",\"--archive_failures\",\"--no-internal\",\"*kotlin*\"]"
+      }
+      priority: 26
+      execution_timeout_secs: 43200
+      expiration_secs: 126000
+      build_numbers: YES
+      service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+    }
+    builders {
       name: "linux-none"
       swarming_host: "chrome-swarming.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
@@ -839,6 +902,50 @@
       service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
     }
     builders {
+      name: "linux-run-on-app-dump"
+      swarming_host: "chrome-swarming.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-16.04"
+      dimensions: "pool:luci.r8.ci"
+      recipe {
+        name: "rex"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
+        cipd_version: "refs/heads/master"
+        properties_j: "builder_group:\"internal.client.r8\""
+        properties_j: "test_options:[\"--bot\"]"
+        properties_j: "test_wrapper:\"tools/run_on_app_dump.py\""
+      }
+      priority: 26
+      execution_timeout_secs: 43200
+      expiration_secs: 126000
+      build_numbers: YES
+      service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+    }
+    builders {
+      name: "linux-run-on-app-dump_release"
+      swarming_host: "chrome-swarming.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-16.04"
+      dimensions: "pool:luci.r8.ci"
+      recipe {
+        name: "rex"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
+        cipd_version: "refs/heads/master"
+        properties_j: "builder_group:\"internal.client.r8\""
+        properties_j: "test_options:[\"--bot\"]"
+        properties_j: "test_wrapper:\"tools/run_on_app_dump.py\""
+      }
+      priority: 26
+      execution_timeout_secs: 43200
+      expiration_secs: 126000
+      build_numbers: YES
+      service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+    }
+    builders {
       name: "windows"
       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 7ffcae6..736c8cc 100644
--- a/infra/config/global/generated/luci-milo.cfg
+++ b/infra/config/global/generated/luci-milo.cfg
@@ -31,6 +31,26 @@
     short_name: "r8cf_jctf_release"
   }
   builders {
+    name: "buildbucket/luci.r8.ci/linux-run-on-app-dump"
+    category: "R8"
+    short_name: "dump"
+  }
+  builders {
+    name: "buildbucket/luci.r8.ci/linux-run-on-app-dump_release"
+    category: "R8 release"
+    short_name: "dump_release"
+  }
+  builders {
+    name: "buildbucket/luci.r8.ci/desugared_library_head"
+    category: "R8"
+    short_name: "desugared_library_head"
+  }
+  builders {
+    name: "buildbucket/luci.r8.ci/desugared_library_jdk11_head"
+    category: "R8"
+    short_name: "desugared_library_jdk11_head"
+  }
+  builders {
     name: "buildbucket/luci.r8.ci/archive"
     category: "R8"
     short_name: "archive"
@@ -205,4 +225,9 @@
     category: "R8 release"
     short_name: "windows_release"
   }
+  builders {
+    name: "buildbucket/luci.r8.ci/linux-kotlin-dev"
+    category: "R8"
+    short_name: "dev"
+  }
 }
diff --git a/infra/config/global/generated/luci-notify.cfg b/infra/config/global/generated/luci-notify.cfg
index 25f83b5..8228f3e 100644
--- a/infra/config/global/generated/luci-notify.cfg
+++ b/infra/config/global/generated/luci-notify.cfg
@@ -36,6 +36,30 @@
   }
   builders {
     bucket: "ci"
+    name: "desugared_library_head"
+    repository: "https://r8.googlesource.com/r8"
+  }
+}
+notifiers {
+  notifications {
+    on_failure: true
+    on_new_failure: true
+    notify_blamelist {}
+  }
+  builders {
+    bucket: "ci"
+    name: "desugared_library_jdk11_head"
+    repository: "https://r8.googlesource.com/r8"
+  }
+}
+notifiers {
+  notifications {
+    on_failure: true
+    on_new_failure: true
+    notify_blamelist {}
+  }
+  builders {
+    bucket: "ci"
     name: "linux-android-4.0.4"
     repository: "https://r8.googlesource.com/r8"
   }
@@ -396,6 +420,18 @@
   }
   builders {
     bucket: "ci"
+    name: "linux-kotlin-dev"
+    repository: "https://r8.googlesource.com/r8"
+  }
+}
+notifiers {
+  notifications {
+    on_failure: true
+    on_new_failure: true
+    notify_blamelist {}
+  }
+  builders {
+    bucket: "ci"
     name: "linux-none"
     repository: "https://r8.googlesource.com/r8"
   }
@@ -444,6 +480,30 @@
   }
   builders {
     bucket: "ci"
+    name: "linux-run-on-app-dump"
+    repository: "https://r8.googlesource.com/r8"
+  }
+}
+notifiers {
+  notifications {
+    on_failure: true
+    on_new_failure: true
+    notify_blamelist {}
+  }
+  builders {
+    bucket: "ci"
+    name: "linux-run-on-app-dump_release"
+    repository: "https://r8.googlesource.com/r8"
+  }
+}
+notifiers {
+  notifications {
+    on_failure: true
+    on_new_failure: true
+    notify_blamelist {}
+  }
+  builders {
+    bucket: "ci"
     name: "windows"
     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 e915f53..46abc44 100644
--- a/infra/config/global/generated/luci-scheduler.cfg
+++ b/infra/config/global/generated/luci-scheduler.cfg
@@ -47,6 +47,24 @@
   }
 }
 job {
+  id: "desugared_library_head"
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.r8.ci"
+    builder: "desugared_library_head"
+  }
+}
+job {
+  id: "desugared_library_jdk11_head"
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.r8.ci"
+    builder: "desugared_library_jdk11_head"
+  }
+}
+job {
   id: "linux-android-4.0.4"
   acl_sets: "ci"
   buildbucket {
@@ -327,6 +345,15 @@
   }
 }
 job {
+  id: "linux-kotlin-dev"
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.r8.ci"
+    builder: "linux-kotlin-dev"
+  }
+}
+job {
   id: "linux-none"
   acl_sets: "ci"
   buildbucket {
@@ -363,6 +390,24 @@
   }
 }
 job {
+  id: "linux-run-on-app-dump"
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.r8.ci"
+    builder: "linux-run-on-app-dump"
+  }
+}
+job {
+  id: "linux-run-on-app-dump_release"
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.r8.ci"
+    builder: "linux-run-on-app-dump_release"
+  }
+}
+job {
   id: "windows"
   acl_sets: "ci"
   buildbucket {
@@ -401,6 +446,7 @@
   triggers: "linux-jdk9_release"
   triggers: "linux-none_release"
   triggers: "linux-r8cf_jctf_release"
+  triggers: "linux-run-on-app-dump_release"
   triggers: "windows_release"
   gitiles {
     repo: "https://r8.googlesource.com/r8"
@@ -412,6 +458,8 @@
   id: "main-gitiles-trigger"
   acl_sets: "ci"
   triggers: "archive"
+  triggers: "desugared_library_head"
+  triggers: "desugared_library_jdk11_head"
   triggers: "linux-android-4.0.4"
   triggers: "linux-android-4.4.4"
   triggers: "linux-android-5.1.1"
@@ -427,12 +475,14 @@
   triggers: "linux-jdk11"
   triggers: "linux-jdk8"
   triggers: "linux-jdk9"
+  triggers: "linux-kotlin-dev"
   triggers: "linux-none"
   triggers: "linux-r8cf_jctf"
+  triggers: "linux-run-on-app-dump"
   triggers: "windows"
   gitiles {
     repo: "https://r8.googlesource.com/r8"
-    refs: "regexp:refs/heads/master"
+    refs: "regexp:refs/heads/main"
   }
 }
 acl_sets {
diff --git a/infra/config/global/luci-milo.cfg b/infra/config/global/luci-milo.cfg
index 3c2ad37..2dd3fcb 100644
--- a/infra/config/global/luci-milo.cfg
+++ b/infra/config/global/luci-milo.cfg
@@ -101,6 +101,11 @@
     short_name: "cf-jctf"
   }
   builders {
+    name: "buildbucket/luci.r8.ci/linux-kotlin-dev"
+    category: "R8"
+    short_name: "kotlin-dev"
+  }
+  builders {
     name: "buildbucket/luci.r8.ci/windows"
     category: "win"
     short_name: "win"
diff --git a/infra/config/global/luci-notify.cfg b/infra/config/global/luci-notify.cfg
index 1e0eb27..2ed0fc7 100644
--- a/infra/config/global/luci-notify.cfg
+++ b/infra/config/global/luci-notify.cfg
@@ -174,6 +174,11 @@
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
+    name: "linux-kotlin-dev"
+    bucket: "ci"
+    repository: "https://r8.googlesource.com/r8"
+  }
+  builders {
     name: "windows"
     bucket: "ci"
     repository: "https://r8.googlesource.com/r8"
diff --git a/infra/config/global/luci-scheduler.cfg b/infra/config/global/luci-scheduler.cfg
index f585e8d..6af1a32 100644
--- a/infra/config/global/luci-scheduler.cfg
+++ b/infra/config/global/luci-scheduler.cfg
@@ -44,6 +44,7 @@
   triggers: "linux-internal"
   triggers: "linux-jctf"
   triggers: "r8cf-linux-jctf"
+  triggers: "linux-kotlin-dev"
   triggers: "windows"
   triggers: "desugared_library_head"
   triggers: "desugared_library_jdk11_head"
@@ -687,6 +688,20 @@
 }
 
 job {
+  id: "linux-kotlin-dev"
+  acl_sets: "default"
+  triggering_policy: {
+    kind: GREEDY_BATCHING
+    max_concurrent_invocations: 2
+  }
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.r8.ci"
+    builder: "linux-kotlin-dev"
+  }
+}
+
+job {
   id: "windows"
   triggering_policy: {
     kind: GREEDY_BATCHING
diff --git a/infra/config/global/main.star b/infra/config/global/main.star
index beda63e..5e790b5 100755
--- a/infra/config/global/main.star
+++ b/infra/config/global/main.star
@@ -169,6 +169,41 @@
       )
 jctf()
 
+def app_dump():
+  for release in ["", "_release"]:
+      properties = {
+          "builder_group" : "internal.client.r8",
+          "test_options" : ["--bot"],
+          "test_wrapper" : "tools/run_on_app_dump.py"
+      }
+      name = "linux-run-on-app-dump" + release
+      r8_builder(
+          name,
+          dimensions = get_dimensions(),
+          execution_timeout = time.hour * 12,
+          expiration_timeout = time.hour * 35,
+          properties = properties,
+      )
+app_dump()
+
+def desugared_library():
+  for name in ["head", "jdk11_head"]:
+    test_options = ["--no_internal", "--desugared-library", "HEAD"]
+    if "jdk11" in name:
+      test_options = test_options + ["--desugared-library-configuration", "jdk11"]
+    properties = {
+       "builder_group" : "internal.client.r8",
+       "test_options" : test_options,
+    }
+    name = "desugared_library_" + name
+    r8_builder(
+        name,
+        dimensions = get_dimensions(),
+        execution_timeout = time.hour * 12,
+        expiration_timeout = time.hour * 35,
+        properties = properties,
+    )
+desugared_library()
 
 def archivers():
   for name in ["archive", "archive_release", "archive_lib_desugar"]:
@@ -242,3 +277,14 @@
 
 r8_tester_with_default("windows", ["--all_tests"],
     dimensions=get_dimensions(windows=True))
+
+r8_builder(
+    "linux-kotlin-dev",
+    dimensions = get_dimensions(),
+    execution_timeout = time.hour * 12,
+    expiration_timeout = time.hour * 35,
+    properties = {
+      "builder_group" : "internal.client.r8",
+      "test_options" : ["--runtimes=dex-default:jdk11", "--kotlin-compiler-dev", "--one_line_per_test", "--archive_failures", "--no-internal", "*kotlin*"]
+    }
+)
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 02ef891..65a9a2f 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -57,8 +57,6 @@
 import com.android.tools.r8.ir.optimize.SwitchMapCollector;
 import com.android.tools.r8.ir.optimize.UninstantiatedTypeOptimization;
 import com.android.tools.r8.ir.optimize.UninstantiatedTypeOptimization.UninstantiatedTypeOptimizationGraphLens;
-import com.android.tools.r8.ir.optimize.UnusedArgumentsCollector;
-import com.android.tools.r8.ir.optimize.UnusedArgumentsCollector.UnusedArgumentsGraphLens;
 import com.android.tools.r8.ir.optimize.enums.EnumUnboxingCfMethods;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
 import com.android.tools.r8.ir.optimize.templates.CfUtilityMethodsForCodeOptimizations;
@@ -512,32 +510,15 @@
         }
         assert appView.verticallyMergedClasses() != null;
 
-        if (options.enableArgumentRemoval) {
-          SubtypingInfo subtypingInfo = appViewWithLiveness.appInfo().computeSubtypingInfo();
-          {
-            timing.begin("UnusedArgumentRemoval");
-            UnusedArgumentsGraphLens lens =
-                new UnusedArgumentsCollector(
-                        appViewWithLiveness,
-                        new MethodPoolCollection(appViewWithLiveness, subtypingInfo))
-                    .run(executorService, timing);
-            assert lens == null || getDirectApp(appView).verifyNothingToRewrite(appView, lens);
-            appView.rewriteWithLens(lens);
-            timing.end();
-          }
-          if (options.enableUninstantiatedTypeOptimization) {
-            timing.begin("UninstantiatedTypeOptimization");
-            UninstantiatedTypeOptimizationGraphLens lens =
-                new UninstantiatedTypeOptimization(appViewWithLiveness)
-                    .strenghtenOptimizationInfo()
-                    .run(
-                        new MethodPoolCollection(appViewWithLiveness, subtypingInfo),
-                        executorService,
-                        timing);
-            assert lens == null || getDirectApp(appView).verifyNothingToRewrite(appView, lens);
-            appView.rewriteWithLens(lens);
-            timing.end();
-          }
+        if (options.enableUninstantiatedTypeOptimization) {
+          timing.begin("UninstantiatedTypeOptimization");
+          UninstantiatedTypeOptimizationGraphLens lens =
+              new UninstantiatedTypeOptimization(appViewWithLiveness)
+                  .strenghtenOptimizationInfo()
+                  .run(new MethodPoolCollection(appViewWithLiveness), executorService, timing);
+          assert lens == null || getDirectApp(appView).verifyNothingToRewrite(appView, lens);
+          appView.rewriteWithLens(lens);
+          timing.end();
         }
 
         HorizontalClassMerger.createForInitialClassMerging(appViewWithLiveness)
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
index 71bc104..227270d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
@@ -28,6 +28,7 @@
     INVOKE_DIRECT((short) 0x07),
     INVOKE_INTERFACE((short) 0x08),
     // Internal method handle needed by lambda desugaring.
+    // TODO(b/200254463): Remove this now that lambda desugaring is CF/CF.
     INVOKE_SUPER((short) 0x09);
 
     private final short value;
diff --git a/src/main/java/com/android/tools/r8/graph/InvalidCode.java b/src/main/java/com/android/tools/r8/graph/InvalidCode.java
index 418de86..5363897 100644
--- a/src/main/java/com/android/tools/r8/graph/InvalidCode.java
+++ b/src/main/java/com/android/tools/r8/graph/InvalidCode.java
@@ -44,7 +44,11 @@
 
   @Override
   public String toString(DexEncodedMethod method, ClassNameMapper naming) {
-    return toString();
+    StringBuilder builder = new StringBuilder();
+    if (method != null) {
+      builder.append(method.toSourceString()).append("\n");
+    }
+    return builder.append(this).toString();
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
index 1133b65..a4a886f 100644
--- a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
+++ b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
@@ -18,9 +18,9 @@
 import com.android.tools.r8.utils.IteratorUtils;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Ordering;
-import it.unimi.dsi.fastutil.ints.Int2ReferenceMap.Entry;
-import it.unimi.dsi.fastutil.ints.Int2ReferenceRBTreeMap;
-import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap.Entry;
+import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectSortedMap;
 import it.unimi.dsi.fastutil.ints.IntArrayList;
 import it.unimi.dsi.fastutil.ints.IntBidirectionalIterator;
 import it.unimi.dsi.fastutil.ints.IntCollection;
@@ -31,14 +31,35 @@
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Objects;
 import java.util.function.Consumer;
 
 public class RewrittenPrototypeDescription {
 
-  public interface ArgumentInfo {
+  public abstract static class ArgumentInfo {
+
+    static final ArgumentInfo NO_INFO =
+        new ArgumentInfo() {
+
+          @Override
+          public ArgumentInfo combine(ArgumentInfo info) {
+            assert false : "ArgumentInfo NO_INFO should not be combined";
+            return info;
+          }
+
+          @Override
+          public boolean equals(Object obj) {
+            return obj == this;
+          }
+
+          @Override
+          public int hashCode() {
+            return System.identityHashCode(this);
+          }
+        };
 
     @SuppressWarnings("ConstantConditions")
-    static ArgumentInfo combine(ArgumentInfo arg1, ArgumentInfo arg2) {
+    public static ArgumentInfo combine(ArgumentInfo arg1, ArgumentInfo arg2) {
       if (arg1 == null) {
         assert arg2 != null;
         return arg2;
@@ -50,33 +71,33 @@
       return arg1.combine(arg2);
     }
 
-    ArgumentInfo NO_INFO =
-        info -> {
-          assert false : "ArgumentInfo NO_INFO should not be combined";
-          return info;
-        };
-
-    default boolean isRemovedArgumentInfo() {
+    public boolean isRemovedArgumentInfo() {
       return false;
     }
 
-    default RemovedArgumentInfo asRemovedArgumentInfo() {
+    public RemovedArgumentInfo asRemovedArgumentInfo() {
       return null;
     }
 
-    default boolean isRewrittenTypeInfo() {
+    public boolean isRewrittenTypeInfo() {
       return false;
     }
 
-    default RewrittenTypeInfo asRewrittenTypeInfo() {
+    public RewrittenTypeInfo asRewrittenTypeInfo() {
       return null;
     }
 
     // ArgumentInfo are combined with `this` first, and the `info` argument second.
-    ArgumentInfo combine(ArgumentInfo info);
+    public abstract ArgumentInfo combine(ArgumentInfo info);
+
+    @Override
+    public abstract boolean equals(Object obj);
+
+    @Override
+    public abstract int hashCode();
   }
 
-  public static class RemovedArgumentInfo implements ArgumentInfo {
+  public static class RemovedArgumentInfo extends ArgumentInfo {
 
     public static class Builder {
 
@@ -142,9 +163,23 @@
       assert false : "Once the argument is removed one cannot modify it any further.";
       return this;
     }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (obj == null || getClass() != obj.getClass()) {
+        return false;
+      }
+      RemovedArgumentInfo other = (RemovedArgumentInfo) obj;
+      return type == other.type && Objects.equals(singleValue, other.singleValue);
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(singleValue, type);
+    }
   }
 
-  public static class RewrittenTypeInfo implements ArgumentInfo {
+  public static class RewrittenTypeInfo extends ArgumentInfo {
 
     private final DexType oldType;
     private final DexType newType;
@@ -201,6 +236,20 @@
       return new RewrittenTypeInfo(oldType, rewrittenTypeInfo.newType);
     }
 
+    @Override
+    public boolean equals(Object obj) {
+      if (obj == null || getClass() != obj.getClass()) {
+        return false;
+      }
+      RewrittenTypeInfo other = (RewrittenTypeInfo) obj;
+      return oldType == other.oldType && newType == other.newType;
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(oldType, newType);
+    }
+
     public boolean verifyIsDueToUnboxing(DexItemFactory dexItemFactory) {
       assert oldType.toBaseType(dexItemFactory).isClassType();
       assert newType.toBaseType(dexItemFactory).isIntType();
@@ -212,14 +261,14 @@
 
     private static final ArgumentInfoCollection EMPTY = new ArgumentInfoCollection();
 
-    private final Int2ReferenceSortedMap<ArgumentInfo> argumentInfos;
+    private final Int2ObjectSortedMap<ArgumentInfo> argumentInfos;
 
     // Specific constructor for empty.
     private ArgumentInfoCollection() {
-      this.argumentInfos = new Int2ReferenceRBTreeMap<>();
+      this.argumentInfos = new Int2ObjectRBTreeMap<>();
     }
 
-    private ArgumentInfoCollection(Int2ReferenceSortedMap<ArgumentInfo> argumentInfos) {
+    private ArgumentInfoCollection(Int2ObjectSortedMap<ArgumentInfo> argumentInfos) {
       assert argumentInfos != null : "should use empty.";
       assert !argumentInfos.isEmpty() : "should use empty.";
       this.argumentInfos = argumentInfos;
@@ -230,7 +279,7 @@
     }
 
     public void forEach(IntObjConsumer<ArgumentInfo> consumer) {
-      for (Entry<ArgumentInfo> entry : argumentInfos.int2ReferenceEntrySet()) {
+      for (Entry<ArgumentInfo> entry : argumentInfos.int2ObjectEntrySet()) {
         consumer.accept(entry.getIntKey(), entry.getValue());
       }
     }
@@ -267,7 +316,7 @@
     }
 
     public Iterator<Entry<ArgumentInfo>> iterator() {
-      return argumentInfos.int2ReferenceEntrySet().iterator();
+      return argumentInfos.int2ObjectEntrySet().iterator();
     }
 
     public boolean hasRemovedArguments() {
@@ -301,17 +350,31 @@
       return argumentInfos.size();
     }
 
+    @Override
+    public boolean equals(Object obj) {
+      if (obj == null || getClass() != obj.getClass()) {
+        return false;
+      }
+      ArgumentInfoCollection other = (ArgumentInfoCollection) obj;
+      return argumentInfos.equals(other.argumentInfos);
+    }
+
+    @Override
+    public int hashCode() {
+      return argumentInfos.hashCode();
+    }
+
     public static Builder builder() {
       return new Builder();
     }
 
     public static class Builder {
 
-      private Int2ReferenceSortedMap<ArgumentInfo> argumentInfos;
+      private Int2ObjectSortedMap<ArgumentInfo> argumentInfos;
 
       public Builder addArgumentInfo(int argIndex, ArgumentInfo argInfo) {
         if (argumentInfos == null) {
-          argumentInfos = new Int2ReferenceRBTreeMap<>();
+          argumentInfos = new Int2ObjectRBTreeMap<>();
         }
         assert !argumentInfos.containsKey(argIndex);
         argumentInfos.put(argIndex, argInfo);
@@ -377,7 +440,7 @@
         }
       }
 
-      Int2ReferenceSortedMap<ArgumentInfo> newArgInfos = new Int2ReferenceRBTreeMap<>();
+      Int2ObjectSortedMap<ArgumentInfo> newArgInfos = new Int2ObjectRBTreeMap<>();
       newArgInfos.putAll(argumentInfos);
       IntBidirectionalIterator iterator = argumentInfos.keySet().iterator();
       int offset = 0;
@@ -477,7 +540,7 @@
     assert !isEmpty();
   }
 
-  private static RewrittenPrototypeDescription create(
+  public static RewrittenPrototypeDescription create(
       List<ExtraParameter> extraParameters,
       RewrittenTypeInfo rewrittenReturnInfo,
       ArgumentInfoCollection argumentsInfo) {
@@ -507,6 +570,11 @@
     return NONE;
   }
 
+  public Consumer<DexEncodedMethod.Builder> createParameterAnnotationsRemover(
+      DexEncodedMethod method) {
+    return getArgumentInfoCollection().createParameterAnnotationsRemover(method);
+  }
+
   public MethodOptimizationInfoFixer createMethodOptimizationInfoFixer() {
     return new RewrittenPrototypeDescriptionMethodOptimizationInfoFixer(this);
   }
@@ -661,4 +729,20 @@
     return new RewrittenPrototypeDescription(
         newExtraParameters, rewrittenReturnInfo, argumentInfoCollection);
   }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null || getClass() != obj.getClass()) {
+      return false;
+    }
+    RewrittenPrototypeDescription other = (RewrittenPrototypeDescription) obj;
+    return extraParameters.equals(other.extraParameters)
+        && Objects.equals(rewrittenReturnInfo, other.rewrittenReturnInfo)
+        && argumentInfoCollection.equals(other.argumentInfoCollection);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(extraParameters, rewrittenReturnInfo, argumentInfoCollection);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index 83dfa77..0f2e98a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -46,6 +46,10 @@
         || result == null;
   }
 
+  public static Builder builder() {
+    return new Builder();
+  }
+
   @Override
   public int opcode() {
     return Opcodes.INVOKE_DIRECT;
@@ -210,4 +214,17 @@
 
     return LibraryMethodReadSetModeling.getModeledReadSetOrUnknown(appView, this);
   }
+
+  public static class Builder extends InvokeMethod.Builder<Builder, InvokeDirect> {
+
+    @Override
+    public InvokeDirect build() {
+      return amend(new InvokeDirect(method, outValue, arguments));
+    }
+
+    @Override
+    public Builder self() {
+      return this;
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
index 5d49406..fa59829 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer.D8CfInstructionDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.itf.InterfaceProcessor;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Sets;
@@ -26,18 +27,28 @@
   protected final AppView<?> appView;
   private final IRConverter converter;
   private final D8MethodProcessor methodProcessor;
+  private final InterfaceProcessor interfaceProcessor;
 
-  ClassConverter(AppView<?> appView, IRConverter converter, D8MethodProcessor methodProcessor) {
+  ClassConverter(
+      AppView<?> appView,
+      IRConverter converter,
+      D8MethodProcessor methodProcessor,
+      InterfaceProcessor interfaceProcessor) {
     this.appView = appView;
     this.converter = converter;
     this.methodProcessor = methodProcessor;
+    this.interfaceProcessor = interfaceProcessor;
   }
 
   public static ClassConverter create(
-      AppView<?> appView, IRConverter converter, D8MethodProcessor methodProcessor) {
+      AppView<?> appView,
+      IRConverter converter,
+      D8MethodProcessor methodProcessor,
+      InterfaceProcessor interfaceProcessor) {
     return appView.options().desugarSpecificOptions().allowAllDesugaredInput
-        ? new LibraryDesugaredClassConverter(appView, converter, methodProcessor)
-        : new DefaultClassConverter(appView, converter, methodProcessor);
+        ? new LibraryDesugaredClassConverter(
+            appView, converter, methodProcessor, interfaceProcessor)
+        : new DefaultClassConverter(appView, converter, methodProcessor, interfaceProcessor);
   }
 
   public ClassConverterResult convertClasses(ExecutorService executorService)
@@ -125,7 +136,7 @@
 
   void convertMethods(
       DexProgramClass clazz, D8CfInstructionDesugaringEventConsumer desugaringEventConsumer) {
-    converter.convertMethods(clazz, desugaringEventConsumer, methodProcessor);
+    converter.convertMethods(clazz, desugaringEventConsumer, methodProcessor, interfaceProcessor);
   }
 
   abstract void notifyAllClassesConverted();
@@ -133,8 +144,11 @@
   static class DefaultClassConverter extends ClassConverter {
 
     DefaultClassConverter(
-        AppView<?> appView, IRConverter converter, D8MethodProcessor methodProcessor) {
-      super(appView, converter, methodProcessor);
+        AppView<?> appView,
+        IRConverter converter,
+        D8MethodProcessor methodProcessor,
+        InterfaceProcessor interfaceProcessor) {
+      super(appView, converter, methodProcessor, interfaceProcessor);
     }
 
     @Override
@@ -154,8 +168,11 @@
     private final Set<DexType> alreadyLibraryDesugared = Sets.newConcurrentHashSet();
 
     LibraryDesugaredClassConverter(
-        AppView<?> appView, IRConverter converter, D8MethodProcessor methodProcessor) {
-      super(appView, converter, methodProcessor);
+        AppView<?> appView,
+        IRConverter converter,
+        D8MethodProcessor methodProcessor,
+        InterfaceProcessor interfaceProcessor) {
+      super(appView, converter, methodProcessor, interfaceProcessor);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ExtraConstantIntParameter.java b/src/main/java/com/android/tools/r8/ir/conversion/ExtraConstantIntParameter.java
index d698e7a..b0544d9 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/ExtraConstantIntParameter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ExtraConstantIntParameter.java
@@ -10,7 +10,8 @@
 import com.android.tools.r8.ir.analysis.value.SingleNumberValue;
 
 public class ExtraConstantIntParameter extends ExtraParameter {
-  long value;
+
+  private final long value;
 
   public ExtraConstantIntParameter(long value) {
     this.value = value;
@@ -26,4 +27,18 @@
   public SingleNumberValue getValue(AppView<?> appView) {
     return appView.abstractValueFactory().createSingleNumberValue(value);
   }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null || getClass() != obj.getClass()) {
+      return false;
+    }
+    ExtraConstantIntParameter other = (ExtraConstantIntParameter) obj;
+    return value == other.value;
+  }
+
+  @Override
+  public int hashCode() {
+    return Long.hashCode(value);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ExtraParameter.java b/src/main/java/com/android/tools/r8/ir/conversion/ExtraParameter.java
index 93f52df..6211fa3 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/ExtraParameter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ExtraParameter.java
@@ -10,7 +10,14 @@
 import com.android.tools.r8.ir.analysis.value.SingleNumberValue;
 
 public abstract class ExtraParameter {
+
   public abstract TypeElement getTypeElement(AppView<?> appView, DexType argType);
 
   public abstract SingleNumberValue getValue(AppView<?> appView);
+
+  @Override
+  public abstract boolean equals(Object obj);
+
+  @Override
+  public abstract int hashCode();
 }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ExtraUnusedNullParameter.java b/src/main/java/com/android/tools/r8/ir/conversion/ExtraUnusedNullParameter.java
index f478661..a543a0c 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/ExtraUnusedNullParameter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ExtraUnusedNullParameter.java
@@ -21,4 +21,14 @@
   public SingleNumberValue getValue(AppView<?> appView) {
     return appView.abstractValueFactory().createNullValue();
   }
+
+  @Override
+  public boolean equals(Object obj) {
+    return obj != null && getClass() == obj.getClass();
+  }
+
+  @Override
+  public int hashCode() {
+    return 0;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index a165209..ee74491 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -57,6 +57,7 @@
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
 import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceApplicationRewriter;
 import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
+import com.android.tools.r8.ir.desugar.itf.InterfaceProcessor;
 import com.android.tools.r8.ir.desugar.lambda.LambdaDeserializationMethodRemover;
 import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
 import com.android.tools.r8.ir.optimize.AssertionsRewriter;
@@ -94,6 +95,7 @@
 import com.android.tools.r8.ir.regalloc.RegisterAllocator;
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.naming.IdentifierNameStringMarker;
+import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagator;
 import com.android.tools.r8.position.MethodPosition;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.LibraryMethodOverrideAnalysis;
@@ -362,17 +364,21 @@
         executor, OptimizationFeedbackIgnore.getInstance());
     DexApplication application = appView.appInfo().app();
     D8MethodProcessor methodProcessor = new D8MethodProcessor(this, executor);
+    InterfaceProcessor interfaceProcessor =
+        appView.options().isInterfaceMethodDesugaringEnabled()
+            ? new InterfaceProcessor(appView)
+            : null;
 
     timing.begin("IR conversion");
 
-    convertClasses(methodProcessor, executor);
+    convertClasses(methodProcessor, interfaceProcessor, executor);
 
     reportNestDesugarDependencies();
     clearNestAttributes();
 
     application = commitPendingSyntheticItemsD8(appView, application);
 
-    postProcessingDesugaringForD8(methodProcessor, executor);
+    postProcessingDesugaringForD8(methodProcessor, interfaceProcessor, executor);
 
     application = commitPendingSyntheticItemsD8(appView, application);
 
@@ -424,13 +430,16 @@
   }
 
   private void postProcessingDesugaringForD8(
-      D8MethodProcessor methodProcessor, ExecutorService executorService)
+      D8MethodProcessor methodProcessor,
+      InterfaceProcessor interfaceProcessor,
+      ExecutorService executorService)
       throws ExecutionException {
     D8CfPostProcessingDesugaringEventConsumer eventConsumer =
         CfPostProcessingDesugaringEventConsumer.createForD8(methodProcessor, instructionDesugaring);
     methodProcessor.newWave();
     InterfaceMethodProcessorFacade interfaceDesugaring =
-        instructionDesugaring.getInterfaceMethodPostProcessingDesugaringD8(ExcludeDexResources);
+        instructionDesugaring.getInterfaceMethodPostProcessingDesugaringD8(
+            ExcludeDexResources, interfaceProcessor);
     CfPostProcessingDesugaringCollection.create(
             appView, interfaceDesugaring, instructionDesugaring.getRetargetingInfo())
         .postProcessingDesugaring(
@@ -439,10 +448,14 @@
     eventConsumer.finalizeDesugaring();
   }
 
-  private void convertClasses(D8MethodProcessor methodProcessor, ExecutorService executorService)
+  private void convertClasses(
+      D8MethodProcessor methodProcessor,
+      InterfaceProcessor interfaceProcessor,
+      ExecutorService executorService)
       throws ExecutionException {
     ClassConverterResult classConverterResult =
-        ClassConverter.create(appView, this, methodProcessor).convertClasses(executorService);
+        ClassConverter.create(appView, this, methodProcessor, interfaceProcessor)
+            .convertClasses(executorService);
 
     // The synthesis of accessibility bridges in nest based access desugaring will schedule and
     // await the processing of synthesized methods.
@@ -475,7 +488,8 @@
   void convertMethods(
       DexProgramClass clazz,
       D8CfInstructionDesugaringEventConsumer desugaringEventConsumer,
-      D8MethodProcessor methodProcessor) {
+      D8MethodProcessor methodProcessor,
+      InterfaceProcessor interfaceProcessor) {
     boolean isReachabilitySensitive = clazz.hasReachabilitySensitiveAnnotation(options.itemFactory);
     // When converting all methods on a class always convert <clinit> first.
     ProgramMethod classInitializer = clazz.getProgramClassInitializer();
@@ -499,6 +513,9 @@
         DexEncodedMethod definition = method.getDefinition();
         definition.getMutableOptimizationInfo().setReachabilitySensitive(isReachabilitySensitive);
         methodProcessor.processMethod(method, desugaringEventConsumer);
+        if (interfaceProcessor != null) {
+          interfaceProcessor.processMethod(method, desugaringEventConsumer);
+        }
       }
     }
 
@@ -835,6 +852,7 @@
     if (options.enableFieldAssignmentTracker) {
       fieldAccessAnalysis.fieldAssignmentTracker().waveDone(wave, delayedOptimizationFeedback);
     }
+    appView.withArgumentPropagator(ArgumentPropagator::publishDelayedReprocessingCriteria);
     if (appView.options().protoShrinking().enableRemoveProtoEnumSwitchMap()) {
       appView.protoShrinker().protoEnumSwitchMapRemover.updateVisibleStaticFieldValues();
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 9b2a145..2e9fcab 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -75,6 +75,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
     if (!instruction.isInvoke()) {
       return null;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BufferCovariantReturnTypeRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BufferCovariantReturnTypeRewriter.java
index ea1cd46..4d9ec0e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BufferCovariantReturnTypeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BufferCovariantReturnTypeRewriter.java
@@ -38,6 +38,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
     if (!isInvokeCandidate(instruction)) {
       return null;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java
index 513a9d5..810812a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java
@@ -40,6 +40,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory);
 
   /**
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
index ada494f..8328e9b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.ir.desugar;
 
 import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
+import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -15,6 +16,7 @@
 import com.android.tools.r8.ir.desugar.itf.InterfaceProcessor;
 import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
 import com.android.tools.r8.utils.ThrowingConsumer;
+import java.util.Collection;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 
@@ -56,6 +58,15 @@
       MethodProcessingContext methodProcessingContext,
       CfInstructionDesugaringEventConsumer eventConsumer);
 
+  /** Selective desugaring of a single invoke instruction assuming a given context. */
+  public abstract Collection<CfInstruction> desugarInstruction(
+      CfInstruction instruction,
+      FreshLocalProvider freshLocalProvider,
+      LocalStackAllocator localStackAllocator,
+      CfInstructionDesugaringEventConsumer eventConsumer,
+      ProgramMethod context,
+      MethodProcessingContext methodProcessingContext);
+
   public boolean isEmpty() {
     return false;
   }
@@ -67,7 +78,7 @@
       ThrowingConsumer<D8NestBasedAccessDesugaring, T> consumer) throws T;
 
   public abstract InterfaceMethodProcessorFacade getInterfaceMethodPostProcessingDesugaringD8(
-      Flavor flavor);
+      Flavor flavor, InterfaceProcessor interfaceProcessor);
 
   public abstract InterfaceMethodProcessorFacade getInterfaceMethodPostProcessingDesugaringR8(
       Flavor flavor, Predicate<ProgramMethod> isLiveMethod, InterfaceProcessor processor);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
index 8f5178e..31b8c3c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
@@ -92,16 +92,6 @@
     }
 
     @Override
-    public void acceptCompanionClassClinit(ProgramMethod method) {
-      addMethodToReprocess(method);
-    }
-
-    @Override
-    public void acceptCompanionMethod(ProgramMethod method, ProgramMethod companion) {
-      // Intentionally empty. The method must be processed on the interface definition.
-    }
-
-    @Override
     public void finalizeDesugaring() throws ExecutionException {
       assert methodProcessor.verifyNoPendingMethodProcessing();
       methodProcessor.newWave();
@@ -170,18 +160,6 @@
     }
 
     @Override
-    public void acceptCompanionClassClinit(ProgramMethod method) {
-      // Generation of this method must have been done during enqueuing.
-      assert false;
-    }
-
-    @Override
-    public void acceptCompanionMethod(ProgramMethod method, ProgramMethod companion) {
-      // Generation of this method must have been done during enqueuing.
-      assert false;
-    }
-
-    @Override
     public void acceptAPIConversionCallback(ProgramMethod method) {
       assert !desugaring.needsDesugaring(method);
       additions.addLiveMethod(method);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
index 027b27c..27b646f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.desugar;
 
+import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
@@ -13,6 +14,7 @@
 import com.android.tools.r8.ir.desugar.itf.InterfaceProcessor;
 import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
 import com.android.tools.r8.utils.ThrowingConsumer;
+import java.util.Collection;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 
@@ -47,6 +49,18 @@
   }
 
   @Override
+  public Collection<CfInstruction> desugarInstruction(
+      CfInstruction instruction,
+      FreshLocalProvider freshLocalProvider,
+      LocalStackAllocator localStackAllocator,
+      CfInstructionDesugaringEventConsumer eventConsumer,
+      ProgramMethod context,
+      MethodProcessingContext methodProcessingContext) {
+    // Nothing to desugar.
+    return null;
+  }
+
+  @Override
   public boolean isEmpty() {
     return true;
   }
@@ -64,7 +78,7 @@
 
   @Override
   public InterfaceMethodProcessorFacade getInterfaceMethodPostProcessingDesugaringD8(
-      Flavor flavor) {
+      Flavor flavor, InterfaceProcessor interfaceProcessor) {
     return null;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InvokeToPrivateRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InvokeToPrivateRewriter.java
index 7fd31a8..65f23dc 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InvokeToPrivateRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InvokeToPrivateRewriter.java
@@ -35,6 +35,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
     if (!instruction.isInvokeVirtual() && !instruction.isInvokeInterface()) {
       return null;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index afedd24..bbc30e8 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -33,6 +33,7 @@
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.desugar.lambda.ForcefullyMovedLambdaMethodConsumer;
 import com.android.tools.r8.ir.desugar.lambda.LambdaInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.lambda.LambdaInstructionDesugaring.DesugarInvoke;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
 import com.android.tools.r8.synthesis.SyntheticProgramClassBuilder;
@@ -40,6 +41,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.function.Consumer;
+import org.objectweb.asm.Opcodes;
 
 /**
  * Represents lambda class generated for a lambda descriptor in context of lambda instantiation
@@ -81,7 +83,8 @@
       AppView<?> appView,
       LambdaInstructionDesugaring desugaring,
       ProgramMethod accessedFrom,
-      LambdaDescriptor descriptor) {
+      LambdaDescriptor descriptor,
+      DesugarInvoke desugarInvoke) {
     assert desugaring != null;
     assert descriptor != null;
     this.type = builder.getType();
@@ -105,7 +108,7 @@
         !stateless ? null : factory.createField(type, type, factory.lambdaInstanceFieldName);
 
     // Synthesize the program class once all fields are set.
-    synthesizeLambdaClass(builder);
+    synthesizeLambdaClass(builder, desugarInvoke);
   }
 
   public final DexProgramClass getLambdaProgramClass() {
@@ -124,12 +127,13 @@
     this.clazz = clazz;
   }
 
-  private void synthesizeLambdaClass(SyntheticProgramClassBuilder builder) {
+  private void synthesizeLambdaClass(
+      SyntheticProgramClassBuilder builder, DesugarInvoke desugarInvoke) {
     builder.setInterfaces(descriptor.interfaces);
     synthesizeStaticFields(builder);
     synthesizeInstanceFields(builder);
     synthesizeDirectMethods(builder);
-    synthesizeVirtualMethods(builder);
+    synthesizeVirtualMethods(builder, desugarInvoke);
   }
 
   final DexField getCaptureField(int index) {
@@ -146,7 +150,8 @@
   }
 
   // Synthesize virtual methods.
-  private void synthesizeVirtualMethods(SyntheticProgramClassBuilder builder) {
+  private void synthesizeVirtualMethods(
+      SyntheticProgramClassBuilder builder, DesugarInvoke desugarInvoke) {
     DexMethod mainMethod =
         appView.dexItemFactory().createMethod(type, descriptor.erasedProto, descriptor.name);
 
@@ -159,7 +164,7 @@
             .setAccessFlags(
                 MethodAccessFlags.fromSharedAccessFlags(
                     Constants.ACC_PUBLIC | Constants.ACC_FINAL, false))
-            .setCode(LambdaMainMethodSourceCode.build(this, mainMethod))
+            .setCode(LambdaMainMethodSourceCode.build(this, mainMethod, desugarInvoke))
             // The api level is computed when tracing.
             .disableAndroidApiLevelCheck()
             .build());
@@ -261,6 +266,21 @@
     }
   }
 
+  public static int getAsmOpcodeForInvokeType(MethodHandleType type) {
+    switch (type) {
+      case INVOKE_INTERFACE:
+        return Opcodes.INVOKEINTERFACE;
+      case INVOKE_STATIC:
+        return Opcodes.INVOKESTATIC;
+      case INVOKE_DIRECT:
+        return Opcodes.INVOKESPECIAL;
+      case INVOKE_INSTANCE:
+        return Opcodes.INVOKEVIRTUAL;
+      default:
+        throw new Unreachable("Unexpected method handle type: " + type);
+    }
+  }
+
   // Creates a delegation target for this particular lambda class. Note that we
   // should always be able to create targets for the lambdas we support.
   private Target createTarget(ProgramMethod accessedFrom) {
@@ -287,12 +307,22 @@
   }
 
   private boolean doesNotNeedAccessor(ProgramMethod accessedFrom) {
-    return canAccessModifyLambdaImplMethod() || !descriptor.needsAccessor(accessedFrom);
+    return canAccessModifyLambdaImplMethod()
+        || isPrivateOrStaticInterfaceMethodInvokeThatWillBeDesugared()
+        || !descriptor.needsAccessor(accessedFrom);
+  }
+
+  private boolean isPrivateOrStaticInterfaceMethodInvokeThatWillBeDesugared() {
+    return appView.options().isInterfaceMethodDesugaringEnabled()
+        && descriptor.implHandle.isInterface
+        && (descriptor.implHandle.type.isInvokeDirect()
+            || descriptor.implHandle.type.isInvokeStatic());
   }
 
   private boolean canAccessModifyLambdaImplMethod() {
     MethodHandleType invokeType = descriptor.implHandle.type;
     return appView.options().canAccessModifyLambdaImplementationMethods(appView)
+        && !isPrivateOrStaticInterfaceMethodInvokeThatWillBeDesugared()
         && (invokeType.isInvokeDirect() || invokeType.isInvokeStatic())
         && descriptor.delegatesToLambdaImplMethod(appView.dexItemFactory())
         && !desugaring.isDirectTargetedLambdaImplementationMethod(descriptor.implHandle);
@@ -328,7 +358,7 @@
 
     assert implHandle.type.isInvokeDirect();
     // If the lambda$ method is an instance-private method on an interface we convert it into a
-    // public static method as it will be placed on the companion class.
+    // public static method so it is accessible.
     if (appView.definitionFor(implMethod.holder).isInterface()) {
       DexProto implProto = implMethod.proto;
       DexType[] implParams = implProto.parameters.values;
@@ -367,8 +397,13 @@
 
     if (doesNotNeedAccessor(accessedFrom)) {
       return new NoAccessorMethodTarget(
-          descriptor.implHandle.asMethod(), Type.VIRTUAL, descriptor.implHandle.isInterface);
+          descriptor.implHandle.asMethod(),
+          descriptor.implHandle.type.isInvokeDirect()
+              ? Type.DIRECT
+              : descriptor.implHandle.type.isInvokeStatic() ? Type.STATIC : Type.VIRTUAL,
+          descriptor.implHandle.isInterface);
     }
+
     // We need to generate an accessor method in `accessedFrom` class/interface
     // for accessing the original instance impl-method. Note that impl-method's
     // holder does not have to be the same as `accessedFrom`.
@@ -523,7 +558,7 @@
   }
 
   // Used for targeting methods referenced directly without creating accessors.
-  private static final class NoAccessorMethodTarget extends Target {
+  public static final class NoAccessorMethodTarget extends Target {
 
     NoAccessorMethodTarget(DexMethod method, Type invokeType, boolean isInterface) {
       super(method, invokeType, isInterface);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
index e0a9c07..acfee7b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
@@ -25,13 +25,18 @@
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.code.NumericType;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.desugar.LambdaClass.InvalidLambdaImplTarget;
+import com.android.tools.r8.ir.desugar.LambdaClass.NoAccessorMethodTarget;
+import com.android.tools.r8.ir.desugar.lambda.LambdaInstructionDesugaring.DesugarInvoke;
+import com.android.tools.r8.utils.IntBox;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableList.Builder;
 import com.google.common.collect.Lists;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import org.objectweb.asm.Opcodes;
 
@@ -166,7 +171,8 @@
     }
   }
 
-  public static CfCode build(LambdaClass lambda, DexMethod mainMethod) {
+  public static CfCode build(
+      LambdaClass lambda, DexMethod mainMethod, DesugarInvoke desugarInvoke) {
     DexItemFactory factory = lambda.appView.dexItemFactory();
     LambdaClass.Target target = lambda.target;
     if (target instanceof InvalidLambdaImplTarget) {
@@ -184,11 +190,13 @@
 
     // Only constructor call should use direct invoke type since super
     // and private methods require accessor methods.
-    boolean constructorTarget = target.invokeType == Invoke.Type.DIRECT;
-    assert !constructorTarget || methodToCall.name == factory.constructorMethodName;
+    boolean constructorTarget = methodToCall.name == factory.constructorMethodName;
+    assert !constructorTarget || target.invokeType == Type.DIRECT;
 
     boolean targetWithReceiver =
-        target.invokeType == Invoke.Type.VIRTUAL || target.invokeType == Invoke.Type.INTERFACE;
+        target.invokeType == Invoke.Type.VIRTUAL
+            || target.invokeType == Invoke.Type.INTERFACE
+            || (target.invokeType == Type.DIRECT && !constructorTarget);
     List<DexType> implReceiverAndArgs = new ArrayList<>();
     if (targetWithReceiver) {
       implReceiverAndArgs.add(methodToCall.holder);
@@ -241,8 +249,24 @@
               erasedParams[i], enforcedParams[i], expectedParamType, instructions, factory);
     }
 
-    instructions.add(
-        new CfInvoke(target.invokeType.getCfOpcode(), methodToCall, target.isInterface()));
+    CfInvoke invoke =
+        new CfInvoke(target.invokeType.getCfOpcode(), methodToCall, target.isInterface());
+    if (target instanceof NoAccessorMethodTarget) {
+      IntBox locals = new IntBox();
+      IntBox stack = new IntBox();
+      Collection<CfInstruction> is =
+          desugarInvoke.desugarInvoke(invoke, locals::getAndIncrement, stack::getAndIncrement);
+      if (is != null) {
+        instructions.addAll(is);
+        maxLocals += locals.get();
+        maxStack += stack.get();
+      } else {
+        instructions.add(invoke);
+      }
+    } else {
+      instructions.add(invoke);
+    }
+
     DexType methodToCallReturnType = methodToCall.getReturnType();
     if (!methodToCallReturnType.isVoidType()) {
       maxStack =
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index da0a96a..62b65d4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -254,7 +254,8 @@
     return true;
   }
 
-  private Collection<CfInstruction> desugarInstruction(
+  @Override
+  public Collection<CfInstruction> desugarInstruction(
       CfInstruction instruction,
       FreshLocalProvider freshLocalProvider,
       LocalStackAllocator localStackAllocator,
@@ -273,6 +274,7 @@
               eventConsumer,
               context,
               methodProcessingContext,
+              this,
               appView.dexItemFactory());
       if (replacement != null) {
         assert desugaring.needsDesugaring(instruction, context);
@@ -346,9 +348,9 @@
 
   @Override
   public InterfaceMethodProcessorFacade getInterfaceMethodPostProcessingDesugaringD8(
-      Flavor flavor) {
+      Flavor flavor, InterfaceProcessor interfaceProcessor) {
     return interfaceMethodRewriter != null
-        ? interfaceMethodRewriter.getPostProcessingDesugaringD8(flavor)
+        ? interfaceMethodRewriter.getPostProcessingDesugaringD8(flavor, interfaceProcessor)
         : null;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicInstructionDesugaring.java
index 8ef2e18..1d55e7e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicInstructionDesugaring.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
@@ -46,6 +47,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
     if (instruction.isConstDynamic()) {
       return desugarConstDynamicInstruction(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverter.java
index 4634977..2b6c9d4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverter.java
@@ -30,6 +30,7 @@
 import com.android.tools.r8.ir.code.MemberType;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
@@ -97,6 +98,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
     if (needsDesugaring(instruction, context)) {
       assert instruction.isInvoke();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeter.java
index 73d7f8f..5c6d6c1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeter.java
@@ -20,6 +20,7 @@
 import com.android.tools.r8.graph.MethodResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
@@ -69,6 +70,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
     InvokeRetargetingResult invokeRetargetingResult = computeNewInvokeTarget(instruction, context);
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java
index 28b6981..5c89290 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.DesugarDescription;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
@@ -47,6 +48,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
     return computeDesugarDescription(instruction)
         .desugarInstruction(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/invokespecial/InvokeSpecialToSelfDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/invokespecial/InvokeSpecialToSelfDesugaring.java
index dfb797c..428bdd0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/invokespecial/InvokeSpecialToSelfDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/invokespecial/InvokeSpecialToSelfDesugaring.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
@@ -73,6 +74,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
     if (instruction.isInvokeSpecial()) {
       return desugarInvokeInstruction(instruction.asInvoke(), eventConsumer, context);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
index d32b3d3..e1462ea 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
@@ -65,7 +65,7 @@
  * In other words, the traversal is in top-down (edges from type to its subtypes) topological order.
  * The traversal is lazy, starting from the unordered set of program classes.
  */
-final class ClassProcessor implements InterfaceDesugaringProcessor {
+final class ClassProcessor {
 
   // Collection for method signatures that may cause forwarding methods to be created.
   private static class MethodSignatures {
@@ -402,7 +402,6 @@
     return !needsLibraryInfo;
   }
 
-  @Override
   public void process(
       DexProgramClass clazz, InterfaceProcessingDesugaringEventConsumer eventConsumer) {
     if (!clazz.isInterface()) {
@@ -417,7 +416,6 @@
 
   // We introduce forwarding methods only once all desugaring has been performed to avoid
   // confusing the look-up with inserted forwarding methods.
-  @Override
   public final void finalizeProcessing(InterfaceProcessingDesugaringEventConsumer eventConsumer) {
     newSyntheticMethods.forEach(
         (clazz, newForwardingMethods) -> {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringProcessor.java
deleted file mode 100644
index 4f4ba32..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringProcessor.java
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.desugar.itf;
-
-import com.android.tools.r8.graph.DexProgramClass;
-
-public interface InterfaceDesugaringProcessor {
-
-  // The process phase can be performed before, concurrently or after code desugaring. It can be
-  // executed concurrently on all classes. Each processing may require to read other classes,
-  // so this phase cannot modify the classes themselves (for example insertion/removal of methods).
-  // The phase can insert new classes with new methods, such as emulated interface dispatch classes
-  // or companion classes with their methods.
-  void process(DexProgramClass clazz, InterfaceProcessingDesugaringEventConsumer eventConsumer);
-
-  // The finalization phase is done at a join point, after all code desugaring have been performed.
-  // All finalization phases of all desugaring processors are performed sequentially.
-  // Complex computations should be avoided if possible here and be moved to the concurrent phase.
-  // Classes may be mutated here (new methods can be inserted, etc.).
-  void finalizeProcessing(InterfaceProcessingDesugaringEventConsumer eventConsumer);
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java
index 94a4e60..41668bd 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java
@@ -11,10 +11,8 @@
 import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.Flavor;
 import com.android.tools.r8.utils.ThreadUtils;
-import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import java.util.Collection;
-import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.function.Predicate;
@@ -23,14 +21,8 @@
 
   private final AppView<?> appView;
   private final Flavor flavour;
-  private final List<InterfaceDesugaringProcessor> interfaceDesugaringProcessors;
-
-  InterfaceMethodProcessorFacade(
-      AppView<?> appView, Flavor flavour, Predicate<ProgramMethod> isLiveMethod) {
-    this.appView = appView;
-    this.flavour = flavour;
-    interfaceDesugaringProcessors = instantiateInterfaceDesugaringProcessors(appView, isLiveMethod);
-  }
+  private final InterfaceProcessor interfaceProcessor;
+  private final ClassProcessor classProcessor;
 
   InterfaceMethodProcessorFacade(
       AppView<?> appView,
@@ -39,25 +31,8 @@
       InterfaceProcessor interfaceProcessor) {
     this.appView = appView;
     this.flavour = flavour;
-    interfaceDesugaringProcessors =
-        ImmutableList.of(new ClassProcessor(appView, isLiveMethod), interfaceProcessor);
-  }
-
-  private List<InterfaceDesugaringProcessor> instantiateInterfaceDesugaringProcessors(
-      AppView<?> appView, Predicate<ProgramMethod> isLiveMethod) {
-
-    // Process all classes first. Add missing forwarding methods to
-    // replace desugared default interface methods.
-    ClassProcessor classProcessor = new ClassProcessor(appView, isLiveMethod);
-
-    // Process interfaces, create companion or dispatch class if needed, move static
-    // methods to companion class, copy default interface methods to companion classes,
-    // make original default methods abstract, remove bridge methods, create dispatch
-    // classes if needed.
-    InterfaceProcessor interfaceProcessor = new InterfaceProcessor(appView);
-
-    // The processors can be listed in any order.
-    return ImmutableList.of(classProcessor, interfaceProcessor);
+    this.interfaceProcessor = interfaceProcessor;
+    this.classProcessor = new ClassProcessor(appView, isLiveMethod);
   }
 
   private boolean shouldProcess(DexProgramClass clazz, Flavor flavour) {
@@ -72,17 +47,14 @@
       InterfaceProcessingDesugaringEventConsumer eventConsumer,
       ExecutorService executorService)
       throws ExecutionException {
+    // TODO(b/183998768): Would be nice to use the ClassProcessing for the processing of classes,
+    //  and do here only the finalization.
     ThreadUtils.processItems(
         Iterables.filter(programClasses, (DexProgramClass clazz) -> shouldProcess(clazz, flavour)),
-        clazz -> {
-          for (InterfaceDesugaringProcessor processor : interfaceDesugaringProcessors) {
-            processor.process(clazz, eventConsumer);
-          }
-        },
+        clazz -> classProcessor.process(clazz, eventConsumer),
         executorService);
-    for (InterfaceDesugaringProcessor processor : interfaceDesugaringProcessors) {
-      processor.finalizeProcessing(eventConsumer);
-    }
+    classProcessor.finalizeProcessing(eventConsumer);
+    interfaceProcessor.finalizeProcessing();
   }
 
   @Override
@@ -91,8 +63,6 @@
       CfPostProcessingDesugaringEventConsumer eventConsumer,
       ExecutorService executorService)
       throws ExecutionException {
-    // TODO(b/183998768): Would be nice to use the ClassProcessing for the processing of classes,
-    //  and do here only the finalization.
     processClassesConcurrently(programClasses, eventConsumer, executorService);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index 87e0d65..6ab7f62 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -30,6 +30,7 @@
 import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.DesugarDescription;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
@@ -248,6 +249,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
     assert !isSyntheticMethodThatShouldNotBeDoubleProcessed(context);
     return computeDescription(instruction, context)
@@ -907,8 +909,9 @@
     return singleCandidate != null ? singleCandidate : method;
   }
 
-  public InterfaceMethodProcessorFacade getPostProcessingDesugaringD8(Flavor flavour) {
-    return new InterfaceMethodProcessorFacade(appView, flavour, m -> true);
+  public InterfaceMethodProcessorFacade getPostProcessingDesugaringD8(
+      Flavor flavour, InterfaceProcessor interfaceProcessor) {
+    return new InterfaceMethodProcessorFacade(appView, flavour, m -> true, interfaceProcessor);
   }
 
   public InterfaceMethodProcessorFacade getPostProcessingDesugaringR8(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessingDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessingDesugaringEventConsumer.java
index 49d843d..8115626 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessingDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessingDesugaringEventConsumer.java
@@ -9,11 +9,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
 
-// TODO(b/183998768): Consider forcing the processing of interface methods in D8 akin to R8.
-//  That would avoid the need to reiterate the interface methods to collect info and this
-//  could avoid the "base" methods.
-public interface InterfaceProcessingDesugaringEventConsumer
-    extends InterfaceMethodDesugaringBaseEventConsumer {
+public interface InterfaceProcessingDesugaringEventConsumer {
 
   void acceptForwardingMethod(ProgramMethod method);
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
index dd4a7a9..7513257 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
@@ -51,7 +51,7 @@
 // a companion class. Removes bridge default methods.
 //
 // Also moves static interface methods into a companion class.
-public final class InterfaceProcessor implements InterfaceDesugaringProcessor {
+public final class InterfaceProcessor {
 
   private final AppView<?> appView;
   private final InterfaceDesugaringSyntheticHelper helper;
@@ -67,36 +67,23 @@
     return helper;
   }
 
-  @Override
-  public void process(
-      DexProgramClass iface, InterfaceProcessingDesugaringEventConsumer eventConsumer) {
-    if (appView.enableWholeProgramOptimizations()) {
-      // R8 populates all info as part of enqueuing.
-      return;
-    }
-    if (!iface.isInterface()) {
-      return;
-    }
-    analyzeBridges(iface);
-    ensureCompanionClassMethods(iface, eventConsumer);
-  }
-
-  private void analyzeBridges(DexProgramClass iface) {
+  public void processMethod(
+      ProgramMethod method, InterfaceMethodDesugaringBaseEventConsumer eventConsumer) {
     assert !appView.enableWholeProgramOptimizations();
-    for (ProgramMethod method : iface.virtualProgramMethods()) {
-      if (!interfaceMethodRemovalChangesApi(method, iface)) {
-        getPostProcessingInterfaceInfo(iface).setHasBridgesToRemove();
-        return;
+    if (!method.getHolder().isInterface()) {
+      return;
+    }
+    if (method.getDefinition().belongsToDirectPool()) {
+      processDirectInterfaceMethod(method, eventConsumer);
+    } else {
+      assert method.getDefinition().belongsToVirtualPool();
+      processVirtualInterfaceMethod(method);
+      if (!interfaceMethodRemovalChangesApi(method)) {
+        getPostProcessingInterfaceInfo(method.getHolder()).setHasBridgesToRemove();
       }
     }
   }
 
-  private void ensureCompanionClassMethods(
-      DexProgramClass iface, InterfaceProcessingDesugaringEventConsumer eventConsumer) {
-    processVirtualInterfaceMethods(iface);
-    processDirectInterfaceMethods(iface, eventConsumer);
-  }
-
   static ProgramMethod ensureCompanionMethod(
       DexProgramClass iface,
       DexString methodName,
@@ -124,29 +111,21 @@
             newMethodCallback);
   }
 
-  private void processVirtualInterfaceMethods(DexProgramClass iface) {
-    assert !appView.enableWholeProgramOptimizations();
-    for (ProgramMethod method : iface.virtualProgramMethods()) {
-      DexEncodedMethod virtual = method.getDefinition();
-      if (helper.isCompatibleDefaultMethod(virtual)) {
-        // Create a new method in a companion class to represent default method implementation.
-        ProgramMethod companion = helper.ensureDefaultAsMethodOfProgramCompanionClassStub(method);
-        finalizeMoveToCompanionMethod(method, companion);
-      }
+  private void processVirtualInterfaceMethod(ProgramMethod method) {
+    if (helper.isCompatibleDefaultMethod(method.getDefinition())) {
+      // Create a new method in a companion class to represent default method implementation.
+      ProgramMethod companion = helper.ensureDefaultAsMethodOfProgramCompanionClassStub(method);
+      finalizeMoveToCompanionMethod(method, companion);
     }
   }
 
-  private void processDirectInterfaceMethods(
-      DexProgramClass iface, InterfaceProcessingDesugaringEventConsumer eventConsumer) {
-    assert !appView.enableWholeProgramOptimizations();
-    for (ProgramMethod method : iface.directProgramMethods()) {
-      DexEncodedMethod definition = method.getDefinition();
-      if (!definition.isClassInitializer()) {
-        getPostProcessingInterfaceInfo(iface).setHasNonClinitDirectMethods();
-        ProgramMethod companion =
-            helper.ensureMethodOfProgramCompanionClassStub(method, eventConsumer);
-        finalizeMoveToCompanionMethod(method, companion);
-      }
+  private void processDirectInterfaceMethod(
+      ProgramMethod method, InterfaceMethodDesugaringBaseEventConsumer eventConsumer) {
+    if (!method.getDefinition().isClassInitializer()) {
+      getPostProcessingInterfaceInfo(method.getHolder()).setHasNonClinitDirectMethods();
+      ProgramMethod companion =
+          helper.ensureMethodOfProgramCompanionClassStub(method, eventConsumer);
+      finalizeMoveToCompanionMethod(method, companion);
     }
   }
 
@@ -238,8 +217,9 @@
   // implementation to the companion class of [iface]. This is always the case for non-bridge
   // methods. Bridge methods that does not override an implementation in a super-interface must
   // also be kept (such a situation can happen if the vertical class merger merges two interfaces).
-  private boolean interfaceMethodRemovalChangesApi(ProgramMethod method, DexClass iface) {
+  private boolean interfaceMethodRemovalChangesApi(ProgramMethod method) {
     assert !appView.enableWholeProgramOptimizations();
+    DexProgramClass iface = method.getHolder();
     if (method.getAccessFlags().isBridge()) {
       if (appView.options().cfToCfDesugar) {
         // TODO(b/187176895): Find the compilation causing this to not be removed.
@@ -309,7 +289,7 @@
     List<DexEncodedMethod> newVirtualMethods = new ArrayList<>();
     for (ProgramMethod method : iface.virtualProgramMethods()) {
       // Remove bridge methods.
-      if (interfaceMethodRemovalChangesApi(method, iface)) {
+      if (interfaceMethodRemovalChangesApi(method)) {
         newVirtualMethods.add(method.getDefinition());
       }
     }
@@ -325,8 +305,7 @@
     }
   }
 
-  @Override
-  public void finalizeProcessing(InterfaceProcessingDesugaringEventConsumer eventConsumer) {
+  public void finalizeProcessing() {
     // TODO(b/196337368): Simplify this fix-up to be specific for the move of companion methods
     //  rather than be based on a graph lens.
     InterfaceProcessorNestedGraphLens graphLens = postProcessInterfaces();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
index 71010c3..206c276 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
@@ -24,6 +24,7 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
 import com.android.tools.r8.ir.desugar.LambdaClass;
@@ -75,6 +76,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
     if (instruction.isInvokeDynamic()) {
       return desugarInvokeDynamicInstruction(
@@ -83,19 +85,36 @@
           localStackAllocator,
           eventConsumer,
           context,
-          methodProcessingContext);
+          methodProcessingContext,
+          (invoke, localProvider, stackAllocator) ->
+              desugaringCollection.desugarInstruction(
+                  invoke,
+                  localProvider,
+                  stackAllocator,
+                  eventConsumer,
+                  context,
+                  methodProcessingContext));
     }
     return null;
   }
 
+  public interface DesugarInvoke {
+    Collection<CfInstruction> desugarInvoke(
+        CfInvoke invoke,
+        FreshLocalProvider freshLocalProvider,
+        LocalStackAllocator localStackAllocator);
+  }
+
   private Collection<CfInstruction> desugarInvokeDynamicInstruction(
       CfInvokeDynamic invoke,
       FreshLocalProvider freshLocalProvider,
       LocalStackAllocator localStackAllocator,
       LambdaDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
-      MethodProcessingContext methodProcessingContext) {
-    LambdaClass lambdaClass = createLambdaClass(invoke, context, methodProcessingContext);
+      MethodProcessingContext methodProcessingContext,
+      DesugarInvoke desugarInvoke) {
+    LambdaClass lambdaClass =
+        createLambdaClass(invoke, context, methodProcessingContext, desugarInvoke);
     if (lambdaClass == null) {
       return null;
     }
@@ -134,7 +153,8 @@
   private LambdaClass createLambdaClass(
       CfInvokeDynamic invoke,
       ProgramMethod context,
-      MethodProcessingContext methodProcessingContext) {
+      MethodProcessingContext methodProcessingContext,
+      DesugarInvoke desugarInvoke) {
     LambdaDescriptor descriptor =
         LambdaDescriptor.tryInfer(invoke.getCallSite(), appView.appInfoForDesugaring(), context);
     if (descriptor == null) {
@@ -149,7 +169,10 @@
                 SyntheticNaming.SyntheticKind.LAMBDA,
                 methodProcessingContext.createUniqueContext(),
                 appView,
-                builder -> box.set(new LambdaClass(builder, appView, this, context, descriptor)));
+                builder ->
+                    box.set(
+                        new LambdaClass(
+                            builder, appView, this, context, descriptor, desugarInvoke)));
     // Immediately set the actual program class on the lambda.
     LambdaClass lambdaClass = box.get();
     lambdaClass.setClass(clazz);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
index 87adb53..f9ca2e9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
@@ -31,6 +31,7 @@
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
@@ -268,6 +269,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
     if (instruction.isFieldInstruction()) {
       return desugarFieldInstruction(instruction.asFieldInstruction(), context, eventConsumer);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordCfMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordCfMethods.java
index adab36a..ed388b8 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordCfMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordCfMethods.java
@@ -123,7 +123,7 @@
                     new int[] {0, 1, 2},
                     new FrameType[] {
                       FrameType.initialized(options.itemFactory.createType("[Ljava/lang/Object;")),
-                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.classType),
                       FrameType.initialized(options.itemFactory.stringType)
                     }),
                 new ArrayDeque<>(Arrays.asList())),
@@ -144,7 +144,7 @@
                     new int[] {0, 1, 2},
                     new FrameType[] {
                       FrameType.initialized(options.itemFactory.createType("[Ljava/lang/Object;")),
-                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.classType),
                       FrameType.initialized(options.itemFactory.stringType)
                     }),
                 new ArrayDeque<>(
@@ -169,6 +169,13 @@
             new CfInvoke(
                 182,
                 options.itemFactory.createMethod(
+                    options.itemFactory.classType,
+                    options.itemFactory.createProto(options.itemFactory.stringType),
+                    options.itemFactory.createString("getSimpleName")),
+                false),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
                     options.itemFactory.stringBuilderType,
                     options.itemFactory.createProto(
                         options.itemFactory.stringBuilderType, options.itemFactory.stringType),
@@ -193,7 +200,7 @@
                     new int[] {0, 1, 2, 3, 4, 5},
                     new FrameType[] {
                       FrameType.initialized(options.itemFactory.createType("[Ljava/lang/Object;")),
-                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.classType),
                       FrameType.initialized(options.itemFactory.stringType),
                       FrameType.initialized(options.itemFactory.createType("[Ljava/lang/String;")),
                       FrameType.initialized(options.itemFactory.stringBuilderType),
@@ -263,7 +270,7 @@
                     new int[] {0, 1, 2, 3, 4, 5},
                     new FrameType[] {
                       FrameType.initialized(options.itemFactory.createType("[Ljava/lang/Object;")),
-                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.classType),
                       FrameType.initialized(options.itemFactory.stringType),
                       FrameType.initialized(options.itemFactory.createType("[Ljava/lang/String;")),
                       FrameType.initialized(options.itemFactory.stringBuilderType),
@@ -278,7 +285,7 @@
                     new int[] {0, 1, 2, 3, 4},
                     new FrameType[] {
                       FrameType.initialized(options.itemFactory.createType("[Ljava/lang/Object;")),
-                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.classType),
                       FrameType.initialized(options.itemFactory.stringType),
                       FrameType.initialized(options.itemFactory.createType("[Ljava/lang/String;")),
                       FrameType.initialized(options.itemFactory.stringBuilderType)
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java
index 1f16c1b..f508ca6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java
@@ -7,6 +7,7 @@
 import static com.android.tools.r8.cf.code.CfStackInstruction.Opcode.Dup;
 import static com.android.tools.r8.cf.code.CfStackInstruction.Opcode.Swap;
 
+import com.android.tools.r8.cf.code.CfConstClass;
 import com.android.tools.r8.cf.code.CfConstString;
 import com.android.tools.r8.cf.code.CfFieldInstruction;
 import com.android.tools.r8.cf.code.CfInstruction;
@@ -39,6 +40,7 @@
 import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaring;
 import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaring;
 import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
@@ -50,7 +52,6 @@
 import com.android.tools.r8.ir.synthetic.RecordCfCodeProvider.RecordEqualsCfCodeProvider;
 import com.android.tools.r8.ir.synthetic.RecordCfCodeProvider.RecordGetFieldsAsObjectsCfCodeProvider;
 import com.android.tools.r8.ir.synthetic.SyntheticCfCodeProvider;
-import com.android.tools.r8.naming.dexitembasedstring.ClassNameComputationInfo;
 import com.android.tools.r8.synthesis.SyntheticNaming;
 import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.ImmutableList;
@@ -89,7 +90,7 @@
     factory = appView.dexItemFactory();
     recordToStringHelperProto =
         factory.createProto(
-            factory.stringType, factory.objectArrayType, factory.stringType, factory.stringType);
+            factory.stringType, factory.objectArrayType, factory.classType, factory.stringType);
     recordHashCodeHelperProto =
         factory.createProto(factory.intType, factory.classType, factory.objectArrayType);
   }
@@ -167,6 +168,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
     assert !instruction.isInitClass();
     if (!needsDesugaring(instruction, context)) {
@@ -252,7 +254,6 @@
       return desugarInvokeRecordToString(
           recordInvokeDynamic,
           localStackAllocator,
-          context,
           eventConsumer,
           methodProcessingContext);
     }
@@ -382,21 +383,15 @@
   private List<CfInstruction> desugarInvokeRecordToString(
       RecordInvokeDynamic recordInvokeDynamic,
       LocalStackAllocator localStackAllocator,
-      ProgramMethod context,
       RecordInstructionDesugaringEventConsumer eventConsumer,
       MethodProcessingContext methodProcessingContext) {
-    DexString simpleName =
-        ClassNameComputationInfo.ClassNameMapping.SIMPLE_NAME.map(
-            recordInvokeDynamic.getRecordClass().type.toDescriptorString(),
-            context.getHolder(),
-            factory);
     localStackAllocator.allocateLocalStack(2);
     DexMethod getFieldsAsObjects =
         getFieldsAsObjectsMethod(recordInvokeDynamic.getRecordClass().type);
     assert recordInvokeDynamic.getRecordClass().lookupProgramMethod(getFieldsAsObjects) != null;
     ArrayList<CfInstruction> instructions = new ArrayList<>();
     instructions.add(new CfInvoke(Opcodes.INVOKESPECIAL, getFieldsAsObjects, false));
-    instructions.add(new CfConstString(simpleName));
+    instructions.add(new CfConstClass(recordInvokeDynamic.getRecordClass().type));
     instructions.add(new CfConstString(recordInvokeDynamic.getFieldNames()));
     ProgramMethod programMethod =
         synthesizeRecordHelper(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/stringconcat/StringConcatInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/stringconcat/StringConcatInstructionDesugaring.java
index 607b088..065fc4c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/stringconcat/StringConcatInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/stringconcat/StringConcatInstructionDesugaring.java
@@ -29,6 +29,7 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
@@ -76,6 +77,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
     if (instruction.isInvokeDynamic()) {
       // We are interested in bootstrap methods StringConcatFactory::makeConcat
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrInstructionDesugaring.java
index ee71bc0..716b6e2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrInstructionDesugaring.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
@@ -57,6 +58,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
     if (!instruction.isInvoke()) {
       return null;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 7bf0e38..d36206d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -1095,8 +1095,7 @@
 
           if (inlineeMayHaveInvokeMethod && options.applyInliningToInlinee) {
             if (inlineeStack.size() + 1 > options.applyInliningToInlineeMaxDepth
-                && appView.appInfo().hasNoAlwaysInlineMethods()
-                && appView.appInfo().hasNoForceInlineMethods()) {
+                && appView.appInfo().hasNoAlwaysInlineMethods()) {
               continue;
             }
             // Record that we will be inside the inlinee until the next block.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index 9608eed..9e8d052 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -17,11 +17,13 @@
 import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
 import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.analysis.value.SingleValue;
 import com.android.tools.r8.ir.analysis.value.UnknownValue;
+import com.android.tools.r8.ir.code.ArrayGet;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.FieldInstruction;
 import com.android.tools.r8.ir.code.IRCode;
@@ -63,6 +65,63 @@
     this.reporter = appView.options().reporter;
   }
 
+  private void rewriteArrayGet(
+      IRCode code,
+      ProgramMethod context,
+      Set<Value> affectedValues,
+      ListIterator<BasicBlock> blocks,
+      InstructionListIterator iterator,
+      ArrayGet arrayGet) {
+    TypeElement arrayType = arrayGet.array().getType();
+    if (!arrayType.isArrayType()) {
+      // Does not type check.
+      return;
+    }
+
+    TypeElement memberType = arrayType.asArrayType().getMemberType();
+    if (!memberType.isClassType()) {
+      // We don't know what the value of the element is.
+      return;
+    }
+
+    boolean isAlwaysNull = false;
+    ClassTypeElement memberClassType = memberType.asClassType();
+    if (memberClassType.getClassType().isAlwaysNull(appView)) {
+      isAlwaysNull = true;
+    } else if (memberClassType.getInterfaces().hasSingleKnownInterface()) {
+      isAlwaysNull =
+          memberClassType.getInterfaces().getSingleKnownInterface().isAlwaysNull(appView);
+    }
+
+    if (!isAlwaysNull) {
+      // We don't know what the value of the element is.
+      return;
+    }
+
+    BasicBlock block = arrayGet.getBlock();
+    Position position = arrayGet.getPosition();
+
+    // All usages are replaced by the replacement value.
+    Instruction replacement =
+        appView
+            .abstractValueFactory()
+            .createNullValue()
+            .createMaterializingInstruction(appView, code, arrayGet);
+    affectedValues.addAll(arrayGet.outValue().affectedValues());
+    arrayGet.outValue().replaceUsers(replacement.outValue());
+
+    // Insert the definition of the replacement.
+    replacement.setPosition(position);
+    if (block.hasCatchHandlers()) {
+      iterator
+          .splitCopyCatchHandlers(code, blocks, appView.options())
+          .listIterator(code)
+          .add(replacement);
+    } else {
+      iterator.add(replacement);
+    }
+  }
+
   private boolean mayPropagateValueFor(DexClassAndField field) {
     if (field.isProgramField()) {
       return appView.appInfo().mayPropagateValueFor(field.getReference());
@@ -472,7 +531,10 @@
       InstructionListIterator iterator = block.listIterator(code);
       while (iterator.hasNext()) {
         Instruction current = iterator.next();
-        if (current.isInvokeMethod()) {
+        if (current.isArrayGet()) {
+          rewriteArrayGet(
+              code, context, affectedValues, blockIterator, iterator, current.asArrayGet());
+        } else if (current.isInvokeMethod()) {
           rewriteInvokeMethodWithConstantValues(
               code, context, affectedValues, blockIterator, iterator, current.asInvokeMethod());
         } else if (current.isFieldGet()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MethodPoolCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/MethodPoolCollection.java
index 652c185..2b439a6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MethodPoolCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MethodPoolCollection.java
@@ -34,6 +34,10 @@
 
   private final Predicate<DexEncodedMethod> methodTester;
 
+  public MethodPoolCollection(AppView<AppInfoWithLiveness> appView) {
+    this(appView, appView.appInfo().computeSubtypingInfo());
+  }
+
   public MethodPoolCollection(AppView<AppInfoWithLiveness> appView, SubtypingInfo subtypingInfo) {
     this(appView, subtypingInfo, Predicates.alwaysTrue());
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
deleted file mode 100644
index 052e196..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
+++ /dev/null
@@ -1,333 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.optimize;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.ArgumentUse;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.NestedGraphLens;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
-import com.android.tools.r8.ir.optimize.MemberPoolCollection.MemberPool;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.MethodSignatureEquivalence;
-import com.android.tools.r8.utils.SymbolGenerationUtils;
-import com.android.tools.r8.utils.SymbolGenerationUtils.MixedCasing;
-import com.android.tools.r8.utils.ThreadUtils;
-import com.android.tools.r8.utils.Timing;
-import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
-import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap;
-import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap;
-import com.google.common.base.Equivalence.Wrapper;
-import com.google.common.collect.Streams;
-import java.util.BitSet;
-import java.util.HashSet;
-import java.util.IdentityHashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.stream.Collectors;
-
-public class UnusedArgumentsCollector {
-
-  private static final MethodSignatureEquivalence equivalence = MethodSignatureEquivalence.get();
-
-  private final AppView<AppInfoWithLiveness> appView;
-  private final MethodPoolCollection methodPoolCollection;
-
-  private final MutableBidirectionalOneToOneMap<DexMethod, DexMethod> methodMapping =
-      new BidirectionalOneToOneHashMap<>();
-  private final Map<DexMethod, ArgumentInfoCollection> removedArguments = new IdentityHashMap<>();
-
-  public static class UnusedArgumentsGraphLens extends NestedGraphLens {
-
-    private final Map<DexMethod, ArgumentInfoCollection> removedArguments;
-
-    UnusedArgumentsGraphLens(
-        AppView<?> appView,
-        BidirectionalOneToOneMap<DexMethod, DexMethod> methodMap,
-        Map<DexMethod, ArgumentInfoCollection> removedArguments) {
-      super(appView, EMPTY_FIELD_MAP, methodMap, EMPTY_TYPE_MAP);
-      this.removedArguments = removedArguments;
-    }
-
-    @Override
-    protected RewrittenPrototypeDescription internalDescribePrototypeChanges(
-        RewrittenPrototypeDescription prototypeChanges, DexMethod method) {
-      return prototypeChanges.withRemovedArguments(
-          removedArguments.getOrDefault(method, ArgumentInfoCollection.empty()));
-    }
-  }
-
-  public UnusedArgumentsCollector(
-      AppView<AppInfoWithLiveness> appView, MethodPoolCollection methodPoolCollection) {
-    this.appView = appView;
-    this.methodPoolCollection = methodPoolCollection;
-  }
-
-  public UnusedArgumentsGraphLens run(ExecutorService executorService, Timing timing)
-      throws ExecutionException {
-    ThreadUtils.awaitFutures(
-        Streams.stream(appView.appInfo().classes())
-            .map(this::runnableForClass)
-            .map(executorService::submit)
-            // Materialize list such that all runnables are submitted to the executor service
-            // before calling awaitFutures().
-            .collect(Collectors.toList()));
-
-    // Build method pool collection to enable unused argument removal for virtual methods.
-    methodPoolCollection.buildAll(executorService, timing);
-
-    // Visit classes in deterministic order to ensure deterministic output.
-    appView.appInfo().classesWithDeterministicOrder().forEach(this::processVirtualMethods);
-
-    if (!methodMapping.isEmpty()) {
-      return new UnusedArgumentsGraphLens(appView, methodMapping, removedArguments);
-    }
-
-    return null;
-  }
-
-  private class UsedSignatures {
-
-    private final MethodSignatureEquivalence equivalence = MethodSignatureEquivalence.get();
-    private final Set<Wrapper<DexMethod>> usedSignatures = new HashSet<>();
-
-    private boolean isMethodSignatureAvailable(DexMethod method) {
-      return !usedSignatures.contains(equivalence.wrap(method));
-    }
-
-    private void markSignatureAsUsed(DexMethod method) {
-      usedSignatures.add(equivalence.wrap(method));
-    }
-
-    DexMethod getNewSignature(DexEncodedMethod method, DexProto newProto) {
-      DexMethod newSignature;
-      int count = 0;
-      DexString newName = null;
-      do {
-        if (newName == null) {
-          newName = method.getReference().name;
-        } else if (!appView.dexItemFactory().isConstructor(method.getReference())) {
-          newName =
-              appView
-                  .dexItemFactory()
-                  .createString(
-                      SymbolGenerationUtils.numberToIdentifier(
-                          count,
-                          MixedCasing.USE_MIXED_CASE,
-                          method.getReference().name.toSourceString().toCharArray()));
-        } else {
-          // Constructors must be named `<init>`.
-          return null;
-        }
-        newSignature =
-            appView.dexItemFactory().createMethod(method.getHolderType(), newProto, newName);
-        count++;
-      } while (!isMethodSignatureAvailable(newSignature));
-      return newSignature;
-    }
-
-    DexEncodedMethod removeArguments(
-        DexEncodedMethod method, DexMethod newSignature, ArgumentInfoCollection unused) {
-      boolean removed = usedSignatures.remove(equivalence.wrap(method.getReference()));
-      assert removed;
-
-      markSignatureAsUsed(newSignature);
-
-      return method.toTypeSubstitutedMethod(
-          newSignature, unused.createParameterAnnotationsRemover(method));
-    }
-  }
-
-  private class GloballyUsedSignatures {
-
-    private final MemberPool<DexMethod> methodPool;
-
-    GloballyUsedSignatures(MemberPool<DexMethod> methodPool) {
-      this.methodPool = methodPool;
-    }
-
-    DexMethod getNewSignature(DexEncodedMethod method, DexProto newProto) {
-      DexMethod newSignature;
-      int count = 0;
-      DexString newName = null;
-      do {
-        if (newName == null) {
-          newName = method.getReference().name;
-        } else if (!appView.dexItemFactory().isConstructor(method.getReference())) {
-          newName =
-              appView
-                  .dexItemFactory()
-                  .createString(method.getReference().name.toSourceString() + count);
-        } else {
-          // Constructors must be named `<init>`.
-          return null;
-        }
-        newSignature =
-            appView.dexItemFactory().createMethod(method.getHolderType(), newProto, newName);
-        count++;
-      } while (methodPool.hasSeen(equivalence.wrap(newSignature)));
-      return newSignature;
-    }
-
-    DexEncodedMethod removeArguments(
-        DexEncodedMethod method, DexMethod newSignature, ArgumentInfoCollection unused) {
-      methodPool.seen(equivalence.wrap(newSignature));
-      return method.toTypeSubstitutedMethod(
-          newSignature, unused.createParameterAnnotationsRemover(method));
-    }
-  }
-
-  private Runnable runnableForClass(DexProgramClass clazz) {
-    return () -> this.processDirectMethods(clazz);
-  }
-
-  private void processDirectMethods(DexProgramClass clazz) {
-    UsedSignatures signatures = new UsedSignatures();
-    for (DexEncodedMethod method : clazz.methods()) {
-      signatures.markSignatureAsUsed(method.getReference());
-    }
-
-    clazz
-        .getMethodCollection()
-        .replaceDirectMethods(
-            method -> {
-
-              // If this is a method with known resolution issues, then don't remove any unused
-              // arguments.
-              if (appView.appInfo().isFailedResolutionTarget(method.getReference())) {
-                return method;
-              }
-
-              ArgumentInfoCollection unused = collectUnusedArguments(method);
-              if (unused != null && unused.hasRemovedArguments()) {
-                DexProto newProto = createProtoWithRemovedArguments(method, unused);
-                DexMethod newSignature = signatures.getNewSignature(method, newProto);
-                if (newSignature == null) {
-                  assert appView.dexItemFactory().isConstructor(method.getReference());
-                  return method;
-                }
-                DexEncodedMethod newMethod =
-                    signatures.removeArguments(method, newSignature, unused);
-                synchronized (this) {
-                  methodMapping.put(method.getReference(), newMethod.getReference());
-                  removedArguments.put(newMethod.getReference(), unused);
-                }
-                return newMethod;
-              }
-              return method;
-            });
-  }
-
-  private void processVirtualMethods(DexProgramClass clazz) {
-    MemberPool<DexMethod> methodPool = methodPoolCollection.get(clazz);
-    GloballyUsedSignatures signatures = new GloballyUsedSignatures(methodPool);
-
-    clazz
-        .getMethodCollection()
-        .replaceVirtualMethods(
-            method -> {
-              ArgumentInfoCollection unused = collectUnusedArguments(method, methodPool);
-              if (unused != null && unused.hasRemovedArguments()) {
-                DexProto newProto = createProtoWithRemovedArguments(method, unused);
-                DexMethod newSignature = signatures.getNewSignature(method, newProto);
-
-                // Double-check that the new method signature is in fact available.
-                assert !methodPool.hasSeenStrictlyAbove(equivalence.wrap(newSignature));
-                assert !methodPool.hasSeenStrictlyBelow(equivalence.wrap(newSignature));
-
-                DexEncodedMethod newMethod =
-                    signatures.removeArguments(
-                        method, signatures.getNewSignature(method, newProto), unused);
-
-                methodMapping.put(method.getReference(), newMethod.getReference());
-                removedArguments.put(newMethod.getReference(), unused);
-                return newMethod;
-              }
-              return method;
-            });
-  }
-
-  private ArgumentInfoCollection collectUnusedArguments(DexEncodedMethod method) {
-    return collectUnusedArguments(method, null);
-  }
-
-  private ArgumentInfoCollection collectUnusedArguments(
-      DexEncodedMethod method, MemberPool<DexMethod> methodPool) {
-    if (ArgumentRemovalUtils.isPinned(method, appView)
-        || appView.appInfo().isKeepUnusedArgumentsMethod(method.getReference())) {
-      return null;
-    }
-    // Only process classfile code objects.
-    if (method.getCode() == null || !method.getCode().isCfCode()) {
-      return null;
-    }
-    if (method.isNonPrivateVirtualMethod()) {
-      // Abort if the method overrides another method, or if the method is overridden. In both cases
-      // an unused argument cannot be removed unless it is unused in all of the related methods in
-      // the hierarchy.
-      assert methodPool != null;
-      Wrapper<DexMethod> wrapper = equivalence.wrap(method.getReference());
-      if (methodPool.hasSeenStrictlyAbove(wrapper) || methodPool.hasSeenStrictlyBelow(wrapper)) {
-        return null;
-      }
-    }
-    int offset = method.getFirstNonReceiverArgumentIndex();
-    int argumentCount = method.getReference().proto.parameters.size() + offset;
-    CollectUsedArguments collector = new CollectUsedArguments();
-    if (!method.accessFlags.isStatic()) {
-      // TODO(65810338): The receiver cannot be removed without transforming the method to being
-      // static.
-      collector.register(0);
-    }
-    method.getCode().registerArgumentReferences(method, collector);
-    BitSet used = collector.getUsedArguments();
-    if (used.cardinality() < argumentCount) {
-      ArgumentInfoCollection.Builder argInfosBuilder = ArgumentInfoCollection.builder();
-      for (int argumentIndex = 0; argumentIndex < argumentCount; argumentIndex++) {
-        if (!used.get(argumentIndex)) {
-          RemovedArgumentInfo removedArg =
-              RemovedArgumentInfo.builder()
-                  .setType(method.getReference().proto.parameters.values[argumentIndex - offset])
-                  .build();
-          argInfosBuilder.addArgumentInfo(argumentIndex, removedArg);
-        }
-      }
-      return argInfosBuilder.build();
-    }
-    return null;
-  }
-
-  private DexProto createProtoWithRemovedArguments(
-      DexEncodedMethod encodedMethod, ArgumentInfoCollection unused) {
-    DexType[] parameters = unused.rewriteParameters(encodedMethod);
-    return appView
-        .dexItemFactory()
-        .createProto(encodedMethod.getReference().proto.returnType, parameters);
-  }
-
-  private static class CollectUsedArguments extends ArgumentUse {
-
-    private final BitSet used = new BitSet();
-
-    BitSet getUsedArguments() {
-      return used;
-    }
-
-    @Override
-    public boolean register(int argument) {
-      used.set(argument);
-      return true;
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 65f0d3f..1f4064f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -316,10 +316,9 @@
             return user; // Not eligible.
           }
 
-          if (AccessControl.isClassAccessible(singleProgramTarget.getHolder(), method, appView)
-              .isPossiblyFalse()) {
-            return user; // Not eligible.
-          }
+          // The target access is checked in isEligibleSingleTarget above.
+          assert AccessControl.isClassAccessible(singleProgramTarget.getHolder(), method, appView)
+              .isTrue();
 
           // Eligible constructor call (for new instance roots only).
           if (user.isInvokeConstructor(dexItemFactory)) {
@@ -1215,6 +1214,10 @@
     if (methodProcessor.isProcessedConcurrently(singleTarget)) {
       return false;
     }
+    if (AccessControl.isMemberAccessible(singleTarget, singleTarget.getHolder(), method, appView)
+        .isPossiblyFalse()) {
+      return false;
+    }
     if (!singleTarget
         .getDefinition()
         .isInliningCandidate(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EmptyEnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EmptyEnumUnboxer.java
index 2fb3b5a..9320e54 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EmptyEnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EmptyEnumUnboxer.java
@@ -16,7 +16,7 @@
 import com.android.tools.r8.ir.conversion.PostMethodProcessor.Builder;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import java.util.Collections;
+import com.google.common.collect.Sets;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 
@@ -47,8 +47,7 @@
 
   @Override
   public Set<Phi> rewriteCode(IRCode code, MethodProcessor methodProcessor) {
-    // Intentionally empty.
-    return Collections.emptySet();
+    return Sets.newIdentityHashSet();
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
index 8a97aef..d2a73ed 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -20,6 +20,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexValue;
 import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
+import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -56,13 +57,14 @@
 import com.android.tools.r8.utils.collections.ProgramMethodMap;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
-import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.IdentityHashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.ListIterator;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
@@ -241,13 +243,22 @@
     // Rewrite enum instantiations + remove static-puts to pruned fields.
     IRCode code = classInitializer.buildIR(appView);
     ListIterator<BasicBlock> blockIterator = code.listIterator();
-    Set<Instruction> instructionsToRemove = Sets.newIdentityHashSet();
+
+    // A mapping from instructions-to-be-removed from the IR to their lens-rewritten
+    // instruction (if any). If an instruction-to-be-removed has a lens-rewritten instruction, the
+    // lens-rewritten instruction must also be detached from the IR.
+    Map<Instruction, Optional<Instruction>> instructionsToRemove = new IdentityHashMap<>();
     while (blockIterator.hasNext()) {
       BasicBlock block = blockIterator.next();
       InstructionListIterator instructionIterator = block.listIterator(code);
       while (instructionIterator.hasNext()) {
         Instruction instruction = instructionIterator.next();
-        if (instructionsToRemove.remove(instruction)) {
+        if (instructionsToRemove.containsKey(instruction)) {
+          Optional<Instruction> rewrittenInstruction = instructionsToRemove.remove(instruction);
+          if (rewrittenInstruction.isPresent()) {
+            instructionIterator.replaceCurrentInstruction(rewrittenInstruction.get());
+            instructionIterator.previous();
+          }
           instructionIterator.removeOrReplaceByDebugLocalRead();
           continue;
         }
@@ -294,8 +305,47 @@
                 newInstance.getUniqueConstructorInvoke(appView.dexItemFactory());
             assert constructorInvoke != null;
 
+            DexMethod invokedMethod = constructorInvoke.getInvokedMethod();
+
+            // Rewrite the constructor invoke in case there are any removed arguments. This is
+            // required since we find the argument index of the ordinal value below, and use this to
+            // find the ordinal of the current enum instance.
+            MethodLookupResult lookupResult =
+                appView.graphLens().lookupInvokeDirect(invokedMethod, classInitializer);
+            if (lookupResult.getReference() != invokedMethod) {
+              List<Value> rewrittenArguments =
+                  new ArrayList<>(constructorInvoke.arguments().size());
+              for (int i = 0; i < constructorInvoke.arguments().size(); i++) {
+                Value argument = constructorInvoke.getArgument(i);
+                if (!lookupResult
+                    .getPrototypeChanges()
+                    .getArgumentInfoCollection()
+                    .isArgumentRemoved(i)) {
+                  rewrittenArguments.add(argument);
+                }
+              }
+              InvokeDirect originalConstructorInvoke = constructorInvoke;
+              constructorInvoke =
+                  InvokeDirect.builder()
+                      .setArguments(rewrittenArguments)
+                      .setMethod(lookupResult.getReference())
+                      .build();
+
+              // Record that the original constructor invoke has been rewritten into the new
+              // constructor invoke, and that these instructions need to be removed from the IR.
+              // Note that although the rewritten constructor invoke has not been inserted into the
+              // IR, the creation of it has added it as a user of each of the operands. To undo this
+              // we replace the original constructor invoke by the rewritten constructor invoke and
+              // then remove the rewritten constructor invoke from the IR.
+              instructionsToRemove.put(originalConstructorInvoke, Optional.of(constructorInvoke));
+            } else {
+              assert lookupResult.getPrototypeChanges().isEmpty();
+              // Record that the constructor invoke needs to be removed.
+              instructionsToRemove.put(constructorInvoke, Optional.empty());
+            }
+
             ProgramMethod constructor =
-                unboxedEnum.lookupProgramMethod(constructorInvoke.getInvokedMethod());
+                unboxedEnum.lookupProgramMethod(lookupResult.getReference());
             assert constructor != null;
 
             InstanceFieldInitializationInfo ordinalInitializationInfo =
@@ -334,8 +384,6 @@
                     code.createValue(
                         ClassTypeElement.create(
                             unboxedEnum.getType(), definitelyNotNull(), appView))));
-
-            instructionsToRemove.add(constructorInvoke);
           }
         } else if (instruction.isStaticPut()) {
           StaticPut staticPut = instruction.asStaticPut();
@@ -358,7 +406,13 @@
     if (!instructionsToRemove.isEmpty()) {
       InstructionListIterator instructionIterator = code.instructionListIterator();
       while (instructionIterator.hasNext()) {
-        if (instructionsToRemove.remove(instructionIterator.next())) {
+        Instruction instruction = instructionIterator.next();
+        if (instructionsToRemove.containsKey(instruction)) {
+          Optional<Instruction> rewrittenInstruction = instructionsToRemove.get(instruction);
+          if (rewrittenInstruction.isPresent()) {
+            instructionIterator.replaceCurrentInstruction(rewrittenInstruction.get());
+            instructionIterator.previous();
+          }
           instructionIterator.removeOrReplaceByDebugLocalRead();
         }
       }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java
index e6b5afe..c6b0aa4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java
@@ -34,9 +34,7 @@
       InvokeMethod invoke, ProgramMethod target, ProgramMethod context) {
     DexEncodedMethod targetMethod = target.getDefinition();
     DexMethod targetReference = target.getReference();
-    if (targetMethod.getOptimizationInfo().forceInline()
-        || (appView.appInfo().hasLiveness()
-            && appView.withLiveness().appInfo().isForceInlineMethod(targetReference))) {
+    if (targetMethod.getOptimizationInfo().forceInline()) {
       assert !appView.appInfo().isNeverInlineMethod(targetReference);
       return Reason.FORCE;
     }
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
index 7a0a20c..00028ff 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
@@ -8,8 +8,10 @@
 import com.android.tools.r8.naming.MemberNaming.Signature;
 import com.android.tools.r8.naming.MemberNaming.Signature.SignatureKind;
 import com.android.tools.r8.naming.mappinginformation.MappingInformation;
+import com.android.tools.r8.naming.mappinginformation.RewriteFrameMappingInformation;
 import com.android.tools.r8.utils.ChainableStringConsumer;
 import com.android.tools.r8.utils.ThrowingConsumer;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
 import java.util.ArrayList;
@@ -453,6 +455,16 @@
       return false;
     }
 
+    public List<RewriteFrameMappingInformation> getRewriteFrameMappingInformation() {
+      ImmutableList.Builder<RewriteFrameMappingInformation> builder = ImmutableList.builder();
+      for (MappingInformation mappingInformation : additionalMappingInfo) {
+        if (mappingInformation.isRewriteFrameMappingInformation()) {
+          builder.add(mappingInformation.asRewriteFrameMappingInformation());
+        }
+      }
+      return builder.build();
+    }
+
     public int getOriginalLineNumber(int lineNumberAfterMinification) {
       if (minifiedRange == null) {
         // General mapping without concrete line numbers: "a() -> b"
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
index 13bcfe9..9232500 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
@@ -30,6 +30,10 @@
     return false;
   }
 
+  public boolean isRewriteFrameMappingInformation() {
+    return false;
+  }
+
   public boolean isCompilerSynthesizedMappingInformation() {
     return false;
   }
@@ -50,6 +54,10 @@
     return null;
   }
 
+  public RewriteFrameMappingInformation asRewriteFrameMappingInformation() {
+    return null;
+  }
+
   public abstract boolean allowOther(MappingInformation information);
 
   public static void fromJsonObject(
@@ -102,6 +110,9 @@
       case CompilerSynthesizedMappingInformation.ID:
         CompilerSynthesizedMappingInformation.deserialize(version, onMappingInfo);
         return;
+      case RewriteFrameMappingInformation.ID:
+        RewriteFrameMappingInformation.deserialize(version, object, onMappingInfo);
+        return;
       default:
         diagnosticsHandler.info(MappingInformationDiagnostics.noHandlerFor(lineNumber, id));
         UnknownJsonMappingInformation.deserialize(id, object, onMappingInfo);
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/RewriteFrameMappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/RewriteFrameMappingInformation.java
new file mode 100644
index 0000000..8dccf93
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/RewriteFrameMappingInformation.java
@@ -0,0 +1,262 @@
+// Copyright (c) 2021, 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.mappinginformation;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.naming.MapVersion;
+import com.android.tools.r8.retrace.internal.RetraceStackTraceContextImpl;
+import com.android.tools.r8.retrace.internal.RetraceStackTraceCurrentEvaluationInformation;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableList;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+import java.util.List;
+import java.util.function.Consumer;
+
+public class RewriteFrameMappingInformation extends MappingInformation {
+
+  public static final MapVersion SUPPORTED_VERSION = MapVersion.MAP_VERSION_EXPERIMENTAL;
+  public static final String ID = "com.android.tools.r8.rewriteFrame";
+  private static final String CONDITIONS_KEY = "conditions";
+  private static final String ACTIONS_KEY = "actions";
+
+  private final List<Condition> conditions;
+  private final List<RewriteAction> actions;
+
+  private RewriteFrameMappingInformation(List<Condition> conditions, List<RewriteAction> actions) {
+    this.conditions = conditions;
+    this.actions = actions;
+  }
+
+  public List<Condition> getConditions() {
+    return conditions;
+  }
+
+  public List<RewriteAction> getActions() {
+    return actions;
+  }
+
+  @Override
+  public String getId() {
+    return ID;
+  }
+
+  @Override
+  public String serialize() {
+    JsonObject object = new JsonObject();
+    object.add(MAPPING_ID_KEY, new JsonPrimitive(ID));
+    JsonArray conditionsArray = new JsonArray();
+    conditions.forEach(condition -> conditionsArray.add(condition.serialize()));
+    object.add(CONDITIONS_KEY, conditionsArray);
+    JsonArray actionsArray = new JsonArray();
+    actions.forEach(action -> actionsArray.add(action.serialize()));
+    object.add(ACTIONS_KEY, actionsArray);
+    return object.toString();
+  }
+
+  public static boolean isSupported(MapVersion version) {
+    return version.isGreaterThanOrEqualTo(SUPPORTED_VERSION);
+  }
+
+  @Override
+  public boolean allowOther(MappingInformation information) {
+    return !information.isRewriteFrameMappingInformation();
+  }
+
+  public static void deserialize(
+      MapVersion mapVersion, JsonObject object, Consumer<MappingInformation> onMappingInfo) {
+    if (!isSupported(mapVersion)) {
+      return;
+    }
+    ImmutableList.Builder<Condition> conditions = ImmutableList.builder();
+    object
+        .get(CONDITIONS_KEY)
+        .getAsJsonArray()
+        .forEach(
+            element -> {
+              conditions.add(Condition.deserialize(element));
+            });
+    ImmutableList.Builder<RewriteAction> actions = ImmutableList.builder();
+    object
+        .get(ACTIONS_KEY)
+        .getAsJsonArray()
+        .forEach(element -> actions.add(RewriteAction.deserialize(element)));
+    onMappingInfo.accept(new RewriteFrameMappingInformation(conditions.build(), actions.build()));
+  }
+
+  @Override
+  public boolean isRewriteFrameMappingInformation() {
+    return true;
+  }
+
+  @Override
+  public RewriteFrameMappingInformation asRewriteFrameMappingInformation() {
+    return this;
+  }
+
+  public abstract static class Condition {
+
+    protected abstract JsonPrimitive serialize();
+
+    private static Condition deserialize(JsonElement element) {
+      String elementString = element.getAsString();
+      int argIndex = elementString.indexOf('(');
+      if (argIndex < 1 || !elementString.endsWith(")")) {
+        throw new CompilationError("Invalid formatted condition: " + elementString);
+      }
+      String functionName = elementString.substring(0, argIndex);
+      String contents = elementString.substring(argIndex + 1, elementString.length() - 1);
+      if (ThrowsCondition.FUNCTION_NAME.equals(functionName)) {
+        return ThrowsCondition.deserialize(contents);
+      }
+      throw new Unimplemented("Unexpected condition: " + elementString);
+    }
+
+    public boolean isThrowsCondition() {
+      return false;
+    }
+
+    public ThrowsCondition asThrowsCondition() {
+      return null;
+    }
+
+    public abstract boolean evaluate(RetraceStackTraceContextImpl context);
+  }
+
+  public static class ThrowsCondition extends Condition {
+
+    static final String FUNCTION_NAME = "throws";
+
+    private final String descriptor;
+
+    private ThrowsCondition(String descriptor) {
+      this.descriptor = descriptor;
+    }
+
+    @Override
+    protected JsonPrimitive serialize() {
+      return new JsonPrimitive(FUNCTION_NAME + "(" + asThrowsCondition().getDescriptor() + ")");
+    }
+
+    @Override
+    public boolean isThrowsCondition() {
+      return true;
+    }
+
+    public String getDescriptor() {
+      return descriptor;
+    }
+
+    @Override
+    public ThrowsCondition asThrowsCondition() {
+      return this;
+    }
+
+    @Override
+    public boolean evaluate(RetraceStackTraceContextImpl context) {
+      return context.getSeenException() != null
+          && context.getSeenException().getDescriptor().equals(descriptor);
+    }
+
+    public static ThrowsCondition deserialize(String conditionString) {
+      if (!DescriptorUtils.isClassDescriptor(conditionString)) {
+        throw new CompilationError("Unexpected throws-descriptor: " + conditionString);
+      }
+      return new ThrowsCondition(conditionString);
+    }
+  }
+
+  public abstract static class RewriteAction {
+
+    static final String REMOVE_INNER_FRAMES_SERIALIZED_NAME = "removeInnerFrames";
+
+    private static final String FUNCTION_KEY = "function";
+    private static final String ARGUMENTS_KEY = "arguments";
+
+    abstract String serializeName();
+
+    abstract JsonArray getArguments();
+
+    JsonElement serialize() {
+      JsonObject jsonObject = new JsonObject();
+      jsonObject.add(FUNCTION_KEY, new JsonPrimitive(serializeName()));
+      jsonObject.add(ARGUMENTS_KEY, getArguments());
+      return jsonObject;
+    }
+
+    private static RewriteAction deserialize(JsonElement element) {
+      String functionString = element.getAsString();
+      int startArgsIndex = functionString.indexOf("(");
+      int endArgsIndex = functionString.indexOf(")");
+      if (endArgsIndex <= startArgsIndex) {
+        throw new Unimplemented("Unexpected action: " + functionString);
+      }
+      String functionName = functionString.substring(0, startArgsIndex);
+      String args = functionString.substring(startArgsIndex + 1, endArgsIndex);
+      if (REMOVE_INNER_FRAMES_SERIALIZED_NAME.equals(functionName)) {
+        return RemoveInnerFramesAction.create(args);
+      }
+      assert false : "Unknown function " + functionName;
+      throw new Unimplemented("Unexpected action: " + functionName);
+    }
+
+    public boolean isRemoveInnerFramesAction() {
+      return false;
+    }
+
+    public RemoveInnerFramesAction asRemoveInnerFramesRewriteAction() {
+      return null;
+    }
+
+    public abstract void evaluate(RetraceStackTraceCurrentEvaluationInformation.Builder builder);
+  }
+
+  public static class RemoveInnerFramesAction extends RewriteAction {
+
+    private final int numberOfFrames;
+
+    public RemoveInnerFramesAction(int numberOfFrames) {
+      this.numberOfFrames = numberOfFrames;
+    }
+
+    public int getNumberOfFrames() {
+      return numberOfFrames;
+    }
+
+    @Override
+    String serializeName() {
+      return REMOVE_INNER_FRAMES_SERIALIZED_NAME;
+    }
+
+    @Override
+    JsonArray getArguments() {
+      JsonArray arguments = new JsonArray();
+      arguments.add(numberOfFrames);
+      return arguments;
+    }
+
+    static RemoveInnerFramesAction create(String args) {
+      return new RemoveInnerFramesAction(Integer.parseInt(args));
+    }
+
+    @Override
+    public boolean isRemoveInnerFramesAction() {
+      return true;
+    }
+
+    @Override
+    public RemoveInnerFramesAction asRemoveInnerFramesRewriteAction() {
+      return this;
+    }
+
+    @Override
+    public void evaluate(RetraceStackTraceCurrentEvaluationInformation.Builder builder) {
+      builder.incrementRemoveInnerFramesCount(numberOfFrames);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
index cabc28f..645bac0 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
@@ -67,8 +67,8 @@
     timing.begin("Argument propagator");
     timing.begin("Initialize code scanner");
 
-    codeScanner = new ArgumentPropagatorCodeScanner(appView);
     reprocessingCriteriaCollection = new ArgumentPropagatorReprocessingCriteriaCollection(appView);
+    codeScanner = new ArgumentPropagatorCodeScanner(appView, reprocessingCriteriaCollection);
 
     ImmediateProgramSubtypingInfo immediateSubtypingInfo =
         ImmediateProgramSubtypingInfo.create(appView);
@@ -110,6 +110,11 @@
     }
   }
 
+  public void publishDelayedReprocessingCriteria() {
+    assert reprocessingCriteriaCollection != null;
+    reprocessingCriteriaCollection.publishDelayedReprocessingCriteria();
+  }
+
   public void transferArgumentInformation(ProgramMethod from, ProgramMethod to) {
     assert codeScanner != null;
     MethodStateCollectionByReference methodStates = codeScanner.getMethodStates();
@@ -125,6 +130,8 @@
       Timing timing)
       throws ExecutionException {
     assert !appView.getSyntheticItems().hasPendingSyntheticClasses();
+    assert reprocessingCriteriaCollection.verifyNoDelayedReprocessingCriteria();
+
     timing.begin("Argument propagator");
 
     // Compute the strongly connected program components for parallel execution.
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
index beb3003..890cb48 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.MethodCollection;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
@@ -66,12 +66,12 @@
           return method.toTypeSubstitutedMethod(
               methodReferenceAfterParameterRemoval,
               builder -> {
-                ArgumentInfoCollection removedParameters =
-                    graphLens.getRemovedParameters(methodReferenceAfterParameterRemoval);
+                RewrittenPrototypeDescription prototypeChanges =
+                    graphLens.getPrototypeChanges(methodReferenceAfterParameterRemoval);
                 builder
-                    .apply(removedParameters.createParameterAnnotationsRemover(method))
+                    .apply(prototypeChanges.createParameterAnnotationsRemover(method))
                     .fixupOptimizationInfo(
-                        appView, removedParameters.createMethodOptimizationInfoFixer());
+                        appView, prototypeChanges.createMethodOptimizationInfoFixer());
               });
         });
   }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
index a61d0b4..4d19a71 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
@@ -40,6 +40,9 @@
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ParameterState;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.UnknownMethodState;
+import com.android.tools.r8.optimize.argumentpropagation.reprocessingcriteria.ArgumentPropagatorReprocessingCriteriaCollection;
+import com.android.tools.r8.optimize.argumentpropagation.reprocessingcriteria.MethodReprocessingCriteria;
+import com.android.tools.r8.optimize.argumentpropagation.reprocessingcriteria.ParameterReprocessingCriteria;
 import com.android.tools.r8.optimize.argumentpropagation.utils.WideningUtils;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.Timing;
@@ -68,6 +71,8 @@
 
   private final Set<DexMethod> monomorphicVirtualMethods = Sets.newIdentityHashSet();
 
+  private final ArgumentPropagatorReprocessingCriteriaCollection reprocessingCriteriaCollection;
+
   /**
    * Maps each non-private virtual method to the upper most method in the class hierarchy with the
    * same method signature. Virtual methods that do not override other virtual methods are mapped to
@@ -82,8 +87,11 @@
   private final MethodStateCollectionByReference methodStates =
       MethodStateCollectionByReference.createConcurrent();
 
-  ArgumentPropagatorCodeScanner(AppView<AppInfoWithLiveness> appView) {
+  ArgumentPropagatorCodeScanner(
+      AppView<AppInfoWithLiveness> appView,
+      ArgumentPropagatorReprocessingCriteriaCollection reprocessingCriteriaCollection) {
     this.appView = appView;
+    this.reprocessingCriteriaCollection = reprocessingCriteriaCollection;
   }
 
   public synchronized void addMonomorphicVirtualMethods(Set<DexMethod> extension) {
@@ -259,7 +267,11 @@
       assert existingMethodState.isBottom() || existingMethodState.isMonomorphic();
       result =
           computeMonomorphicMethodState(
-              invoke, resolvedMethod, context, existingMethodState.asMonomorphicOrBottom());
+              invoke,
+              resolvedMethod,
+              invoke.lookupSingleProgramTarget(appView, context),
+              context,
+              existingMethodState.asMonomorphicOrBottom());
     }
     timing.end();
     return result;
@@ -276,9 +288,10 @@
     DynamicType dynamicReceiverType = invoke.getReceiver().getDynamicType(appView);
     assert !dynamicReceiverType.getDynamicUpperBoundType().nullability().isDefinitelyNull();
 
+    ProgramMethod singleTarget = invoke.lookupSingleProgramTarget(appView, context);
     DynamicType bounds =
         computeBoundsForPolymorphicMethodState(
-            invoke, resolvedMethod, context, dynamicReceiverType);
+            invoke, resolvedMethod, singleTarget, context, dynamicReceiverType);
     MethodState existingMethodStateForBounds =
         existingMethodState.isPolymorphic()
             ? existingMethodState.asPolymorphic().getMethodStateForBounds(bounds)
@@ -299,6 +312,7 @@
         computeMonomorphicMethodState(
             invoke,
             resolvedMethod,
+            singleTarget,
             context,
             existingMethodStateForBounds.asMonomorphicOrBottom(),
             dynamicReceiverType);
@@ -308,9 +322,9 @@
   private DynamicType computeBoundsForPolymorphicMethodState(
       InvokeMethodWithReceiver invoke,
       ProgramMethod resolvedMethod,
+      ProgramMethod singleTarget,
       ProgramMethod context,
       DynamicType dynamicReceiverType) {
-    ProgramMethod singleTarget = invoke.lookupSingleProgramTarget(appView, context);
     DynamicType bounds =
         singleTarget != null
             ? DynamicType.createExact(
@@ -343,11 +357,13 @@
   private ConcreteMonomorphicMethodStateOrUnknown computeMonomorphicMethodState(
       InvokeMethod invoke,
       ProgramMethod resolvedMethod,
+      ProgramMethod singleTarget,
       ProgramMethod context,
       ConcreteMonomorphicMethodStateOrBottom existingMethodState) {
     return computeMonomorphicMethodState(
         invoke,
         resolvedMethod,
+        singleTarget,
         context,
         existingMethodState,
         invoke.isInvokeMethodWithReceiver()
@@ -358,11 +374,17 @@
   private ConcreteMonomorphicMethodStateOrUnknown computeMonomorphicMethodState(
       InvokeMethod invoke,
       ProgramMethod resolvedMethod,
+      ProgramMethod singleTarget,
       ProgramMethod context,
       ConcreteMonomorphicMethodStateOrBottom existingMethodState,
       DynamicType dynamicReceiverType) {
     List<ParameterState> parameterStates = new ArrayList<>(invoke.arguments().size());
 
+    MethodReprocessingCriteria methodReprocessingCriteria =
+        singleTarget != null
+            ? reprocessingCriteriaCollection.getReprocessingCriteria(singleTarget)
+            : MethodReprocessingCriteria.alwaysReprocess();
+
     int argumentIndex = 0;
     if (invoke.isInvokeMethodWithReceiver()) {
       assert dynamicReceiverType != null;
@@ -371,7 +393,8 @@
               invoke.asInvokeMethodWithReceiver(),
               resolvedMethod,
               dynamicReceiverType,
-              existingMethodState));
+              existingMethodState,
+              methodReprocessingCriteria.getParameterReprocessingCriteria(0)));
       argumentIndex++;
     }
 
@@ -382,7 +405,8 @@
               argumentIndex,
               invoke.getArgument(argumentIndex),
               context,
-              existingMethodState));
+              existingMethodState,
+              methodReprocessingCriteria.getParameterReprocessingCriteria(argumentIndex)));
     }
 
     // If all parameter states are unknown, then return a canonicalized unknown method state that
@@ -402,13 +426,20 @@
       InvokeMethodWithReceiver invoke,
       ProgramMethod resolvedMethod,
       DynamicType dynamicReceiverType,
-      ConcreteMonomorphicMethodStateOrBottom existingMethodState) {
+      ConcreteMonomorphicMethodStateOrBottom existingMethodState,
+      ParameterReprocessingCriteria parameterReprocessingCriteria) {
     // Don't compute a state for this parameter if the stored state is already unknown.
     if (existingMethodState.isMonomorphic()
         && existingMethodState.asMonomorphic().getParameterState(0).isUnknown()) {
       return ParameterState.unknown();
     }
 
+    // For receivers we only track the dynamic type. Therefore, if there is no need to track the
+    // dynamic type of the receiver of the targeted method, then just return unknown.
+    if (!parameterReprocessingCriteria.shouldReprocessDueToDynamicType()) {
+      return ParameterState.unknown();
+    }
+
     DynamicType widenedDynamicReceiverType =
         WideningUtils.widenDynamicReceiverType(appView, resolvedMethod, dynamicReceiverType);
     return widenedDynamicReceiverType.isUnknown()
@@ -421,7 +452,8 @@
       int argumentIndex,
       Value argument,
       ProgramMethod context,
-      ConcreteMonomorphicMethodStateOrBottom existingMethodState) {
+      ConcreteMonomorphicMethodStateOrBottom existingMethodState,
+      ParameterReprocessingCriteria parameterReprocessingCriteria) {
     // Don't compute a state for this parameter if the stored state is already unknown.
     if (existingMethodState.isMonomorphic()
         && existingMethodState.asMonomorphic().getParameterState(argumentIndex).isUnknown()) {
@@ -469,6 +501,12 @@
     // then use UnknownParameterState.
     if (parameterTypeElement.isClassType()) {
       DynamicType dynamicType = argument.getDynamicType(appView);
+      if (!parameterReprocessingCriteria.shouldReprocessDueToDynamicType()) {
+        dynamicType =
+            parameterReprocessingCriteria.widenDynamicClassType(
+                appView, dynamicType, parameterTypeElement.asClassType());
+      }
+
       DynamicType widenedDynamicType =
           WideningUtils.widenDynamicNonReceiverType(appView, dynamicType, parameterType);
       return abstractValue.isUnknown() && widenedDynamicType.isUnknown()
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
index a00c016..c3fe41b 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.NestedGraphLens;
 import com.android.tools.r8.graph.RewrittenPrototypeDescription;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
 import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap;
@@ -18,23 +17,23 @@
 
 public class ArgumentPropagatorGraphLens extends NestedGraphLens {
 
-  private final Map<DexMethod, ArgumentInfoCollection> removedParameters;
+  private final Map<DexMethod, RewrittenPrototypeDescription> prototypeChanges;
 
   ArgumentPropagatorGraphLens(
       AppView<AppInfoWithLiveness> appView,
       BidirectionalOneToOneMap<DexMethod, DexMethod> methodMap,
-      Map<DexMethod, ArgumentInfoCollection> removedParameters) {
+      Map<DexMethod, RewrittenPrototypeDescription> prototypeChanges) {
     super(appView, EMPTY_FIELD_MAP, methodMap, EMPTY_TYPE_MAP);
-    this.removedParameters = removedParameters;
+    this.prototypeChanges = prototypeChanges;
   }
 
   public static Builder builder(AppView<AppInfoWithLiveness> appView) {
     return new Builder(appView);
   }
 
-  public ArgumentInfoCollection getRemovedParameters(DexMethod method) {
+  public RewrittenPrototypeDescription getPrototypeChanges(DexMethod method) {
     assert method != internalGetPreviousMethodSignature(method);
-    return removedParameters.getOrDefault(method, ArgumentInfoCollection.empty());
+    return prototypeChanges.getOrDefault(method, RewrittenPrototypeDescription.none());
   }
 
   @Override
@@ -42,10 +41,10 @@
       RewrittenPrototypeDescription prototypeChanges, DexMethod method) {
     DexMethod previous = internalGetPreviousMethodSignature(method);
     if (previous == method) {
-      assert !removedParameters.containsKey(method);
+      assert !this.prototypeChanges.containsKey(method);
       return prototypeChanges;
     }
-    return prototypeChanges.withRemovedArguments(getRemovedParameters(method));
+    return prototypeChanges.combine(getPrototypeChanges(method));
   }
 
   @Override
@@ -63,7 +62,7 @@
     private final AppView<AppInfoWithLiveness> appView;
     private final MutableBidirectionalOneToOneMap<DexMethod, DexMethod> newMethodSignatures =
         new BidirectionalOneToOneHashMap<>();
-    private final Map<DexMethod, ArgumentInfoCollection> removedParameters =
+    private final Map<DexMethod, RewrittenPrototypeDescription> prototypeChanges =
         new IdentityHashMap<>();
 
     Builder(AppView<AppInfoWithLiveness> appView) {
@@ -77,16 +76,16 @@
     public ArgumentPropagatorGraphLens.Builder mergeDisjoint(
         ArgumentPropagatorGraphLens.Builder partialGraphLensBuilder) {
       newMethodSignatures.putAll(partialGraphLensBuilder.newMethodSignatures);
-      removedParameters.putAll(partialGraphLensBuilder.removedParameters);
+      prototypeChanges.putAll(partialGraphLensBuilder.prototypeChanges);
       return this;
     }
 
     public Builder recordMove(
-        DexMethod from, DexMethod to, ArgumentInfoCollection removedParametersForMethod) {
+        DexMethod from, DexMethod to, RewrittenPrototypeDescription prototypeChangesForMethod) {
       assert from != to;
       newMethodSignatures.put(from, to);
-      if (!removedParametersForMethod.isEmpty()) {
-        removedParameters.put(to, removedParametersForMethod);
+      if (!prototypeChangesForMethod.isEmpty()) {
+        prototypeChanges.put(to, prototypeChangesForMethod);
       }
       return this;
     }
@@ -94,7 +93,7 @@
     public ArgumentPropagatorGraphLens build() {
       return isEmpty()
           ? null
-          : new ArgumentPropagatorGraphLens(appView, newMethodSignatures, removedParameters);
+          : new ArgumentPropagatorGraphLens(appView, newMethodSignatures, prototypeChanges);
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
index 6f97143..ae2a8aa 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteClassTypeParameterState;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMethodState;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodStateOrUnknown;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteParameterState;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcretePrimitiveTypeParameterState;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodState;
@@ -159,6 +160,11 @@
       return;
     }
 
+    // Do not optimize @KeepConstantArgument methods.
+    if (appView.appInfo().isKeepConstantArgumentsMethod(method)) {
+      methodState = MethodState.unknown();
+    }
+
     methodState = getMethodStateAfterUnusedParameterRemoval(method, methodState);
 
     if (methodState.isUnknown()) {
@@ -208,12 +214,9 @@
     // before reenqueing.
     MethodReprocessingCriteria reprocessingCriteria =
         reprocessingCriteriaCollection.getReprocessingCriteria(method);
-    if (!reprocessingCriteria.shouldReprocess(appView, method, monomorphicMethodState)) {
-      return;
-    }
-
-    // Do not optimize @KeepConstantArgument methods.
-    if (appView.appInfo().isKeepConstantArgumentsMethod(method)) {
+    ConcreteMonomorphicMethodStateOrUnknown widenedMethodState =
+        reprocessingCriteria.widenMethodState(appView, method, monomorphicMethodState);
+    if (widenedMethodState.isUnknown()) {
       return;
     }
 
@@ -221,7 +224,7 @@
         .getDefinition()
         .setCallSiteOptimizationInfo(
             ConcreteCallSiteOptimizationInfo.fromMethodState(
-                appView, method, monomorphicMethodState));
+                appView, method, widenedMethodState.asMonomorphic()));
   }
 
   private MethodState getMethodStateAfterUnusedParameterRemoval(
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
index 5f14c9d..055013f 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
@@ -16,6 +16,7 @@
 import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
 import com.android.tools.r8.graph.ObjectAllocationInfoCollection;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription;
 import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
 import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -54,7 +55,8 @@
   private final AppView<AppInfoWithLiveness> appView;
   private final ImmediateProgramSubtypingInfo immediateSubtypingInfo;
 
-  private final Map<DexClass, DexMethodSignatureSet> libraryMethods = new ConcurrentHashMap<>();
+  private final Map<DexClass, DexMethodSignatureSet> libraryVirtualMethods =
+      new ConcurrentHashMap<>();
 
   public ArgumentPropagatorProgramOptimizer(
       AppView<AppInfoWithLiveness> appView, ImmediateProgramSubtypingInfo immediateSubtypingInfo) {
@@ -88,25 +90,25 @@
     return graphLens;
   }
 
-  private DexMethodSignatureSet getOrComputeLibraryMethods(DexClass clazz) {
-    DexMethodSignatureSet libraryMethodsOnClass = libraryMethods.get(clazz);
+  private DexMethodSignatureSet getOrComputeLibraryVirtualMethods(DexClass clazz) {
+    DexMethodSignatureSet libraryMethodsOnClass = libraryVirtualMethods.get(clazz);
     if (libraryMethodsOnClass != null) {
       return libraryMethodsOnClass;
     }
-    return computeLibraryMethods(clazz);
+    return computeLibraryVirtualMethods(clazz);
   }
 
-  private DexMethodSignatureSet computeLibraryMethods(DexClass clazz) {
+  private DexMethodSignatureSet computeLibraryVirtualMethods(DexClass clazz) {
     DexMethodSignatureSet libraryMethodsOnClass = DexMethodSignatureSet.create();
     immediateSubtypingInfo.forEachImmediateSuperClassMatching(
         clazz,
         (supertype, superclass) -> superclass != null,
         (supertype, superclass) ->
-            libraryMethodsOnClass.addAll(getOrComputeLibraryMethods(superclass)));
+            libraryMethodsOnClass.addAll(getOrComputeLibraryVirtualMethods(superclass)));
     clazz.forEachClassMethodMatching(
         DexEncodedMethod::belongsToVirtualPool,
         method -> libraryMethodsOnClass.add(method.getMethodSignature()));
-    libraryMethods.put(clazz, libraryMethodsOnClass);
+    libraryVirtualMethods.put(clazz, libraryMethodsOnClass);
     return libraryMethodsOnClass;
   }
 
@@ -182,7 +184,8 @@
       DexMethodSignatureSet pinnedMethodSignatures = DexMethodSignatureSet.create();
       Set<DexClass> seenLibraryClasses = Sets.newIdentityHashSet();
       for (DexProgramClass clazz : stronglyConnectedProgramClasses) {
-        clazz.forEachProgramMethod(
+        clazz.forEachProgramMethodMatching(
+            method -> !method.isInstanceInitializer(),
             method -> {
               if (!appView.getKeepInfo(method).isShrinkingAllowed(options)) {
                 pinnedMethodSignatures.add(method.getMethodSignature());
@@ -195,7 +198,7 @@
                     && !superclass.isProgramClass()
                     && seenLibraryClasses.add(superclass),
             (supertype, superclass) ->
-                pinnedMethodSignatures.addAll(getOrComputeLibraryMethods(superclass)));
+                pinnedMethodSignatures.addAll(getOrComputeLibraryVirtualMethods(superclass)));
       }
       pinnedMethodSignatures.forEach(
           signature -> reserveMethodSignature(signature, signature, IntSets.EMPTY_SET));
@@ -266,6 +269,7 @@
     private boolean isParameterRemovalAllowed(ProgramMethod method) {
       return appView.getKeepInfo(method).isParameterRemovalAllowed(options)
           && !method.getDefinition().isLibraryMethodOverride().isPossiblyTrue()
+          && !appView.appInfo().isBootstrapMethod(method)
           && !appView.appInfo().isMethodTargetedByInvokeDynamic(method);
     }
 
@@ -306,16 +310,19 @@
     private boolean visitClass(
         DexProgramClass clazz, ArgumentPropagatorGraphLens.Builder partialGraphLensBuilder) {
       BooleanBox affected = new BooleanBox();
+      DexMethodSignatureSet instanceInitializerSignatures = DexMethodSignatureSet.create();
+      clazz.forEachProgramInstanceInitializer(instanceInitializerSignatures::add);
       clazz.forEachProgramMethod(
           method -> {
-            ArgumentInfoCollection removableParameters =
+            RewrittenPrototypeDescription prototypeChanges =
                 method.getDefinition().belongsToDirectPool()
-                    ? computeRemovableParametersFromDirectMethod(method)
+                    ? computeRemovableParametersFromDirectMethod(
+                        method, instanceInitializerSignatures)
                     : computeRemovableParametersFromVirtualMethod(method);
-            DexMethod newMethodSignature = getNewMethodSignature(method, removableParameters);
+            DexMethod newMethodSignature = getNewMethodSignature(method, prototypeChanges);
             if (newMethodSignature != method.getReference()) {
               partialGraphLensBuilder.recordMove(
-                  method.getReference(), newMethodSignature, removableParameters);
+                  method.getReference(), newMethodSignature, prototypeChanges);
               affected.set();
             }
           });
@@ -323,30 +330,32 @@
     }
 
     private DexMethod getNewMethodSignature(
-        ProgramMethod method, ArgumentInfoCollection removableParameters) {
-      DexMethodSignature methodSignatureWithoutParametersRemoved = method.getMethodSignature();
-      IntSet removableParameterIndices = removableParameters.getKeys();
+        ProgramMethod method, RewrittenPrototypeDescription prototypeChanges) {
+      DexMethodSignature methodSignatureWithoutPrototypeChanges = method.getMethodSignature();
+      IntSet removableParameterIndices = prototypeChanges.getArgumentInfoCollection().getKeys();
 
       // Check if there is a reserved signature for this already.
       DexMethodSignature reservedSignature =
           newMethodSignatures
-              .getOrDefault(methodSignatureWithoutParametersRemoved, Collections.emptyMap())
+              .getOrDefault(methodSignatureWithoutPrototypeChanges, Collections.emptyMap())
               .get(removableParameterIndices);
       if (reservedSignature != null) {
         return reservedSignature.withHolder(method.getHolderType(), dexItemFactory);
       }
 
       DexMethod methodReferenceWithParametersRemoved =
-          removableParameters.rewriteMethod(method, dexItemFactory);
+          prototypeChanges.getArgumentInfoCollection().rewriteMethod(method, dexItemFactory);
       DexMethodSignature methodSignatureWithParametersRemoved =
           methodReferenceWithParametersRemoved.getSignature();
 
       // Find a method signature. First check if the current signature is available.
       if (!occupiedMethodSignatures.containsKey(methodSignatureWithParametersRemoved)) {
-        reserveMethodSignature(
-            methodSignatureWithParametersRemoved,
-            methodSignatureWithoutParametersRemoved,
-            removableParameterIndices);
+        if (!method.getDefinition().isInstanceInitializer()) {
+          reserveMethodSignature(
+              methodSignatureWithParametersRemoved,
+              methodSignatureWithoutPrototypeChanges,
+              removableParameterIndices);
+        }
         return methodReferenceWithParametersRemoved;
       }
 
@@ -354,7 +363,7 @@
           occupiedMethodSignatures.get(methodSignatureWithParametersRemoved);
       // In this case we should have found a reserved method signature above.
       assert !(occupant.getFirst().equals(removableParameterIndices)
-          && occupant.getSecond().equals(methodSignatureWithoutParametersRemoved));
+          && occupant.getSecond().equals(methodSignatureWithoutPrototypeChanges));
 
       // We need to find a new name for this method, since the signature is already occupied.
       // TODO(b/190154391): Instead of generating a new name, we could also try permuting the order
@@ -371,45 +380,55 @@
                   return true;
                 }
                 return candidateOccupant.getFirst().equals(removableParameterIndices)
-                    && candidateOccupant
-                        .getSecond()
-                        .equals(methodSignatureWithoutParametersRemoved);
+                    && candidateOccupant.getSecond().equals(methodSignatureWithoutPrototypeChanges);
               });
 
       // Reserve the newly generated method signature.
-      reserveMethodSignature(
-          newMethod.getSignature(),
-          methodSignatureWithoutParametersRemoved,
-          removableParameterIndices);
+      if (!method.getDefinition().isInstanceInitializer()) {
+        reserveMethodSignature(
+            newMethod.getSignature(),
+            methodSignatureWithoutPrototypeChanges,
+            removableParameterIndices);
+      }
 
       return newMethod;
     }
 
-    private ArgumentInfoCollection computeRemovableParametersFromDirectMethod(
-        ProgramMethod method) {
+    private RewrittenPrototypeDescription computeRemovableParametersFromDirectMethod(
+        ProgramMethod method, DexMethodSignatureSet instanceInitializerSignatures) {
       assert method.getDefinition().belongsToDirectPool();
-      // TODO(b/190154391): Allow parameter removal from initializers. We need to guarantee absence
-      //  of collisions since initializers can't be renamed.
-      if (!isParameterRemovalAllowed(method) || method.getDefinition().isInstanceInitializer()) {
-        return ArgumentInfoCollection.empty();
+      if (!isParameterRemovalAllowed(method)) {
+        return RewrittenPrototypeDescription.none();
       }
       // TODO(b/199864962): Allow parameter removal from check-not-null classified methods.
       if (method
           .getOptimizationInfo()
           .getEnumUnboxerMethodClassification()
           .isCheckNotNullClassification()) {
-        return ArgumentInfoCollection.empty();
+        return RewrittenPrototypeDescription.none();
       }
-      return computeRemovableParametersFromMethod(method);
+      RewrittenPrototypeDescription prototypeChanges = computePrototypeChangesForMethod(method);
+      if (prototypeChanges.isEmpty()) {
+        return prototypeChanges;
+      }
+      if (method.getDefinition().isInstanceInitializer()) {
+        DexMethod rewrittenMethod =
+            prototypeChanges.getArgumentInfoCollection().rewriteMethod(method, dexItemFactory);
+        assert rewrittenMethod != method.getReference();
+        if (!instanceInitializerSignatures.add(rewrittenMethod)) {
+          return RewrittenPrototypeDescription.none();
+        }
+      }
+      return prototypeChanges;
     }
 
-    private ArgumentInfoCollection computeRemovableParametersFromVirtualMethod(
+    private RewrittenPrototypeDescription computeRemovableParametersFromVirtualMethod(
         ProgramMethod method) {
       IntSet removableParameterIndices =
           removableVirtualMethodParameters.getOrDefault(
               method.getMethodSignature(), IntSets.EMPTY_SET);
       if (removableParameterIndices.isEmpty()) {
-        return ArgumentInfoCollection.empty();
+        return RewrittenPrototypeDescription.none();
       }
 
       if (method.getAccessFlags().isAbstract()) {
@@ -423,25 +442,27 @@
                   .setType(method.getArgumentType(removableParameterIndex))
                   .build());
         }
-        return removableParametersBuilder.build();
+        return RewrittenPrototypeDescription.create(
+            Collections.emptyList(), null, removableParametersBuilder.build());
       }
 
-      ArgumentInfoCollection removableParameters =
-          computeRemovableParametersFromMethod(method, removableParameterIndices::contains);
-      assert removableParameters.size() == removableParameterIndices.size();
-      return removableParameters;
+      RewrittenPrototypeDescription prototypeChanges =
+          computePrototypeChangesForMethod(method, removableParameterIndices::contains);
+      assert prototypeChanges.getArgumentInfoCollection().size()
+          == removableParameterIndices.size();
+      return prototypeChanges;
     }
 
-    private ArgumentInfoCollection computeRemovableParametersFromMethod(ProgramMethod method) {
-      return computeRemovableParametersFromMethod(method, parameterIndex -> true);
+    private RewrittenPrototypeDescription computePrototypeChangesForMethod(ProgramMethod method) {
+      return computePrototypeChangesForMethod(method, parameterIndex -> true);
     }
 
-    private ArgumentInfoCollection computeRemovableParametersFromMethod(
+    private RewrittenPrototypeDescription computePrototypeChangesForMethod(
         ProgramMethod method, IntPredicate removableParameterIndices) {
       ConcreteCallSiteOptimizationInfo optimizationInfo =
           method.getDefinition().getCallSiteOptimizationInfo().asConcreteCallSiteOptimizationInfo();
       if (optimizationInfo == null) {
-        return ArgumentInfoCollection.empty();
+        return RewrittenPrototypeDescription.none();
       }
 
       ArgumentInfoCollection.Builder removableParametersBuilder = ArgumentInfoCollection.builder();
@@ -462,7 +483,8 @@
                   .build());
         }
       }
-      return removableParametersBuilder.build();
+      return RewrittenPrototypeDescription.create(
+          Collections.emptyList(), null, removableParametersBuilder.build());
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ArgumentPropagatorReprocessingCriteriaCollection.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ArgumentPropagatorReprocessingCriteriaCollection.java
index 68191f9..90e9ead 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ArgumentPropagatorReprocessingCriteriaCollection.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ArgumentPropagatorReprocessingCriteriaCollection.java
@@ -27,6 +27,7 @@
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
+import java.util.IdentityHashMap;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -35,6 +36,9 @@
   private final AppView<AppInfoWithLiveness> appView;
 
   private final Map<DexMethod, MethodReprocessingCriteria> reproccessingCriteria =
+      new IdentityHashMap<>();
+
+  private final Map<DexMethod, MethodReprocessingCriteria> delayedReproccessingCriteria =
       new ConcurrentHashMap<>();
 
   public ArgumentPropagatorReprocessingCriteriaCollection(AppView<AppInfoWithLiveness> appView) {
@@ -43,7 +47,12 @@
 
   public MethodReprocessingCriteria getReprocessingCriteria(ProgramMethod method) {
     return reproccessingCriteria.getOrDefault(
-        method.getReference(), MethodReprocessingCriteria.empty());
+        method.getReference(), MethodReprocessingCriteria.alwaysReprocess());
+  }
+
+  public void publishDelayedReprocessingCriteria() {
+    reproccessingCriteria.putAll(delayedReproccessingCriteria);
+    delayedReproccessingCriteria.clear();
   }
 
   /**
@@ -69,7 +78,7 @@
     // optimization info, then record this information. If the map is empty, then the method should
     // always be reprocessed if we find non-trivial optimization info for some of the parameters.
     if (!methodReprocessingCriteria.isEmpty()) {
-      reproccessingCriteria.put(
+      delayedReproccessingCriteria.put(
           method.getReference(), new MethodReprocessingCriteria(methodReprocessingCriteria));
     }
   }
@@ -142,4 +151,9 @@
 
     return builder.build();
   }
+
+  public boolean verifyNoDelayedReprocessingCriteria() {
+    assert delayedReproccessingCriteria.isEmpty();
+    return true;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/MethodReprocessingCriteria.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/MethodReprocessingCriteria.java
index 527ee9e..658383f 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/MethodReprocessingCriteria.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/MethodReprocessingCriteria.java
@@ -8,14 +8,19 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodStateOrUnknown;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodState;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ParameterState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.UnknownParameterState;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.Iterables;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
 
 public class MethodReprocessingCriteria {
 
-  public static final MethodReprocessingCriteria EMPTY = new MethodReprocessingCriteria();
+  public static final MethodReprocessingCriteria ALWAYS_REPROCESS =
+      new MethodReprocessingCriteria();
 
   private final Int2ReferenceMap<ParameterReprocessingCriteria> reproccesingCriteria;
 
@@ -29,8 +34,8 @@
     this.reproccesingCriteria = reproccesingCriteria;
   }
 
-  public static MethodReprocessingCriteria empty() {
-    return EMPTY;
+  public static MethodReprocessingCriteria alwaysReprocess() {
+    return ALWAYS_REPROCESS;
   }
 
   public ParameterReprocessingCriteria getParameterReprocessingCriteria(int parameterIndex) {
@@ -38,7 +43,7 @@
         parameterIndex, ParameterReprocessingCriteria.alwaysReprocess());
   }
 
-  public boolean shouldReprocess(
+  public ConcreteMonomorphicMethodStateOrUnknown widenMethodState(
       AppView<AppInfoWithLiveness> appView,
       ProgramMethod method,
       ConcreteMonomorphicMethodState methodState) {
@@ -49,14 +54,24 @@
         continue;
       }
 
+      if (parameterState.getAbstractValue(appView).isSingleValue()) {
+        // Don't widen when we have information that can be used for parameter removal.
+        continue;
+      }
+
       ParameterReprocessingCriteria parameterReprocessingCriteria =
           getParameterReprocessingCriteria(parameterIndex);
       DexType parameterType = method.getArgumentType(parameterIndex);
       if (parameterReprocessingCriteria.shouldReprocess(
           appView, parameterState.asConcrete(), parameterType)) {
-        return true;
+        continue;
       }
+
+      methodState.setParameterState(parameterIndex, UnknownParameterState.get());
     }
-    return false;
+
+    return Iterables.all(methodState.getParameterStates(), ParameterState::isUnknown)
+        ? MethodState.unknown()
+        : methodState;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ParameterReprocessingCriteria.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ParameterReprocessingCriteria.java
index 9022f81..d7c8890 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ParameterReprocessingCriteria.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ParameterReprocessingCriteria.java
@@ -6,6 +6,8 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteParameterState;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
@@ -42,6 +44,14 @@
 
   public abstract boolean shouldReprocessDueToNullability();
 
+  public final DynamicType widenDynamicClassType(
+      AppView<AppInfoWithLiveness> appView, DynamicType dynamicType, ClassTypeElement staticType) {
+    if (dynamicType.getNullability().isMaybeNull()) {
+      return DynamicType.unknown();
+    }
+    return DynamicType.create(appView, staticType.getOrCreateVariant(dynamicType.getNullability()));
+  }
+
   public static class Builder {
 
     private boolean reprocessDueToAbstractValue;
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceClassElement.java b/src/main/java/com/android/tools/r8/retrace/RetraceClassElement.java
index 43bf109..6e61118 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceClassElement.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceClassElement.java
@@ -31,4 +31,6 @@
   RetraceFrameResult lookupFrame(Optional<Integer> position, MethodReference methodReference);
 
   RetraceUnknownJsonMappingInformationResult getUnknownJsonMappingInformation();
+
+  RetraceStackTraceContext getContextWhereClassWasThrown();
 }
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceElement.java b/src/main/java/com/android/tools/r8/retrace/RetraceElement.java
index 63d40db..55de558 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceElement.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceElement.java
@@ -16,6 +16,4 @@
   R getRetraceResultContext();
 
   boolean isCompilerSynthesized();
-
-  RetraceStackTraceContext getContext();
 }
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceFrameElement.java b/src/main/java/com/android/tools/r8/retrace/RetraceFrameElement.java
index b114240..f2ad4e7 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceFrameElement.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceFrameElement.java
@@ -18,9 +18,12 @@
 
   void visitAllFrames(BiConsumer<RetracedMethodReference, Integer> consumer);
 
-  void visitNonCompilerSynthesizedFrames(BiConsumer<RetracedMethodReference, Integer> consumer);
+  void visitRewrittenFrames(
+      RetraceStackTraceContext context, BiConsumer<RetracedMethodReference, Integer> consumer);
 
   RetracedSourceFile getSourceFile(RetracedClassMemberReference frame);
 
   List<? extends RetracedMethodReference> getOuterFrames();
+
+  RetraceStackTraceContext getContext();
 }
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceInvalidRewriteFrameDiagnostics.java b/src/main/java/com/android/tools/r8/retrace/RetraceInvalidRewriteFrameDiagnostics.java
new file mode 100644
index 0000000..ace1b86
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceInvalidRewriteFrameDiagnostics.java
@@ -0,0 +1,46 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+
+@Keep
+public class RetraceInvalidRewriteFrameDiagnostics implements Diagnostic {
+
+  private final int numberOfFramesToRemove;
+  private final String method;
+
+  private RetraceInvalidRewriteFrameDiagnostics(int numberOfFramesToRemove, String method) {
+    this.numberOfFramesToRemove = numberOfFramesToRemove;
+    this.method = method;
+  }
+
+  @Override
+  public Origin getOrigin() {
+    return Origin.unknown();
+  }
+
+  @Override
+  public Position getPosition() {
+    return Position.UNKNOWN;
+  }
+
+  @Override
+  public String getDiagnosticMessage() {
+    return "Cannot remove "
+        + numberOfFramesToRemove
+        + " frames from the retraced output of "
+        + method
+        + " because it exceeds the number of retraced frames";
+  }
+
+  public static RetraceInvalidRewriteFrameDiagnostics create(
+      int numberOfFramesToRemove, String method) {
+    return new RetraceInvalidRewriteFrameDiagnostics(numberOfFramesToRemove, method);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceStackTraceContext.java b/src/main/java/com/android/tools/r8/retrace/RetraceStackTraceContext.java
index 9004cc1..e87ff95 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceStackTraceContext.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceStackTraceContext.java
@@ -11,6 +11,6 @@
 public interface RetraceStackTraceContext {
 
   static RetraceStackTraceContext getInitialContext() {
-    return new RetraceStackTraceContextImpl();
+    return RetraceStackTraceContextImpl.builder().build();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/retrace/Retracer.java b/src/main/java/com/android/tools/r8/retrace/Retracer.java
index c609e44..bd865a8 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retracer.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retracer.java
@@ -33,4 +33,9 @@
       ProguardMapProducer proguardMapProducer, DiagnosticsHandler diagnosticsHandler) {
     return RetracerImpl.create(proguardMapProducer, diagnosticsHandler, false);
   }
+
+  static Retracer createExperimental(
+      ProguardMapProducer proguardMapProducer, DiagnosticsHandler diagnosticsHandler) {
+    return RetracerImpl.create(proguardMapProducer, diagnosticsHandler, true);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/retrace/StringRetrace.java b/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
index 0296d71..ff5b970 100644
--- a/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.Keep;
 import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy;
 import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.StringUtils;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
@@ -78,21 +79,27 @@
     List<String> retracedStrings = new ArrayList<>();
     List<List<String>> retracedStackTraces =
         removeDuplicateStackTraces(retraceStackTrace(stackTrace));
-    if (retracedStackTraces.size() > 1) {
-      retracedStrings.add(
-          "There are "
-              + retracedStackTraces.size()
-              + " ambiguous stack traces."
-              + (isVerbose ? "" : " Use --verbose to have all listed."));
+    if (retracedStackTraces.size() > 1 && isVerbose) {
+      retracedStrings.add("There are " + retracedStackTraces.size() + " ambiguous stack traces.");
     }
     for (int i = 0; i < retracedStackTraces.size(); i++) {
-      if (i > 0) {
-        retracedStrings.add("< OR >");
+      List<String> result = retracedStackTraces.get(i);
+      if (i > 0 && !result.isEmpty()) {
+        // We are reporting an ambiguous frame. To support retracing tools that retrace line by line
+        // we have to emit <OR> at the point of the first ' at ' if we can find it.
+        String firstLine = result.get(0);
+        int indexToInsertOr = firstLine.indexOf(" at ");
+        boolean hasSpace = indexToInsertOr >= 0;
+        if (indexToInsertOr < 0) {
+          indexToInsertOr = Math.max(StringUtils.firstNonWhitespaceCharacter(firstLine), 0);
+        }
+        result.set(
+            0,
+            firstLine.substring(0, indexToInsertOr)
+                + (hasSpace ? "<OR>" : "<OR> ")
+                + firstLine.substring(indexToInsertOr));
       }
-      retracedStrings.addAll(retracedStackTraces.get(i));
-      if (!isVerbose) {
-        break;
-      }
+      retracedStrings.addAll(result);
     }
     return retracedStrings;
   }
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceClassResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceClassResultImpl.java
index 54c90e6..0f165cf 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceClassResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceClassResultImpl.java
@@ -20,7 +20,6 @@
 import com.android.tools.r8.retrace.RetraceStackTraceContext;
 import com.android.tools.r8.retrace.RetraceUnknownJsonMappingInformationResult;
 import com.android.tools.r8.retrace.RetracedSourceFile;
-import com.android.tools.r8.retrace.Retracer;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.Pair;
 import com.google.common.collect.ImmutableList;
@@ -220,7 +219,7 @@
         RetraceClassResultImpl classResult,
         List<Pair<RetraceClassElementImpl, T>> mappings,
         D definition,
-        Retracer retracer);
+        RetracerImpl retracer);
   }
 
   public static class RetraceClassElementImpl implements RetraceClassElement {
@@ -273,9 +272,10 @@
     }
 
     @Override
-    public RetraceStackTraceContext getContext() {
-      // TODO(b/197936862): Extend the context to enable tracking information.
-      return RetraceStackTraceContext.getInitialContext();
+    public RetraceStackTraceContext getContextWhereClassWasThrown() {
+      return RetraceStackTraceContextImpl.builder()
+          .setSeenException(getRetracedClass().getClassReference())
+          .build();
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java
index b536bea..38667c4 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.retrace.RetraceFieldElement;
 import com.android.tools.r8.retrace.RetraceFieldResult;
-import com.android.tools.r8.retrace.RetraceStackTraceContext;
 import com.android.tools.r8.retrace.RetracedSourceFile;
 import com.android.tools.r8.retrace.Retracer;
 import com.android.tools.r8.retrace.internal.RetraceClassResultImpl.RetraceClassElementImpl;
@@ -114,11 +113,6 @@
     }
 
     @Override
-    public RetraceStackTraceContext getContext() {
-      return RetraceStackTraceContext.getInitialContext();
-    }
-
-    @Override
     public boolean isUnknown() {
       return fieldReference.isUnknown();
     }
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java
index 2f79d8e..9bee685 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java
@@ -6,16 +6,17 @@
 
 import static com.android.tools.r8.retrace.internal.RetraceUtils.methodReferenceFromMappedRange;
 
+import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRange;
 import com.android.tools.r8.naming.Range;
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.retrace.RetraceFrameElement;
 import com.android.tools.r8.retrace.RetraceFrameResult;
+import com.android.tools.r8.retrace.RetraceInvalidRewriteFrameDiagnostics;
 import com.android.tools.r8.retrace.RetraceStackTraceContext;
 import com.android.tools.r8.retrace.RetracedClassMemberReference;
 import com.android.tools.r8.retrace.RetracedMethodReference;
 import com.android.tools.r8.retrace.RetracedSourceFile;
-import com.android.tools.r8.retrace.Retracer;
 import com.android.tools.r8.retrace.internal.RetraceClassResultImpl.RetraceClassElementImpl;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.OptionalBool;
@@ -35,7 +36,7 @@
   private final MethodDefinition methodDefinition;
   private final Optional<Integer> obfuscatedPosition;
   private final List<Pair<RetraceClassElementImpl, List<MappedRange>>> mappedRanges;
-  private final Retracer retracer;
+  private final RetracerImpl retracer;
 
   private OptionalBool isAmbiguousCache = OptionalBool.UNKNOWN;
 
@@ -44,7 +45,7 @@
       List<Pair<RetraceClassElementImpl, List<MappedRange>>> mappedRanges,
       MethodDefinition methodDefinition,
       Optional<Integer> obfuscatedPosition,
-      Retracer retracer) {
+      RetracerImpl retracer) {
     this.classResult = classResult;
     this.methodDefinition = methodDefinition;
     this.obfuscatedPosition = obfuscatedPosition;
@@ -93,7 +94,8 @@
                             methodDefinition.substituteHolder(
                                 classElement.getRetracedClass().getClassReference())),
                         ImmutableList.of(),
-                        obfuscatedPosition));
+                        obfuscatedPosition,
+                        retracer));
               }
               // Iterate over mapped ranges that may have different positions than specified.
               List<ElementImpl> ambiguousFrames = new ArrayList<>();
@@ -125,7 +127,8 @@
         classElement,
         getRetracedMethod(methodReference, topFrame, obfuscatedPosition),
         mappedRangesForElement,
-        obfuscatedPosition);
+        obfuscatedPosition,
+        retracer);
   }
 
   private RetracedMethodReferenceImpl getRetracedMethod(
@@ -156,18 +159,21 @@
     private final RetraceClassElementImpl classElement;
     private final List<MappedRange> mappedRanges;
     private final Optional<Integer> obfuscatedPosition;
+    private final RetracerImpl retracer;
 
-    private ElementImpl(
+    ElementImpl(
         RetraceFrameResultImpl retraceFrameResult,
         RetraceClassElementImpl classElement,
         RetracedMethodReferenceImpl methodReference,
         List<MappedRange> mappedRanges,
-        Optional<Integer> obfuscatedPosition) {
+        Optional<Integer> obfuscatedPosition,
+        RetracerImpl retracer) {
       this.methodReference = methodReference;
       this.retraceFrameResult = retraceFrameResult;
       this.classElement = classElement;
       this.mappedRanges = mappedRanges;
       this.obfuscatedPosition = obfuscatedPosition;
+      this.retracer = retracer;
     }
 
     private boolean isOuterMostFrameCompilerSynthesized() {
@@ -188,11 +194,6 @@
     }
 
     @Override
-    public RetraceStackTraceContext getContext() {
-      return RetraceStackTraceContext.getInitialContext();
-    }
-
-    @Override
     public RetraceFrameResult getRetraceResultContext() {
       return retraceFrameResult;
     }
@@ -222,17 +223,32 @@
     }
 
     @Override
-    public void visitNonCompilerSynthesizedFrames(
-        BiConsumer<RetracedMethodReference, Integer> consumer) {
+    public void visitRewrittenFrames(
+        RetraceStackTraceContext context, BiConsumer<RetracedMethodReference, Integer> consumer) {
+      RetraceStackTraceContextImpl contextImpl = (RetraceStackTraceContextImpl) context;
+      RetraceStackTraceCurrentEvaluationInformation currentFrameInformation =
+          contextImpl.computeRewritingInformation(mappedRanges);
       int index = 0;
+      int numberOfFramesToRemove = currentFrameInformation.getRemoveInnerFrames();
       RetracedMethodReferenceImpl prev = getTopFrame();
-      for (RetracedMethodReferenceImpl next : getOuterFrames()) {
-        consumer.accept(prev, index++);
+      List<RetracedMethodReferenceImpl> outerFrames = getOuterFrames();
+      if (numberOfFramesToRemove > outerFrames.size() + 1) {
+        assert prev.isKnown();
+        DiagnosticsHandler diagnosticsHandler = retracer.getDiagnosticsHandler();
+        diagnosticsHandler.warning(
+            RetraceInvalidRewriteFrameDiagnostics.create(
+                numberOfFramesToRemove, prev.asKnown().toString()));
+        numberOfFramesToRemove = 0;
+      }
+      for (RetracedMethodReferenceImpl next : outerFrames) {
+        if (numberOfFramesToRemove-- <= 0) {
+          consumer.accept(prev, index++);
+        }
         prev = next;
       }
       // We expect only the last frame, i.e., the outer-most caller to potentially be synthesized.
       // If not include it too.
-      if (!isOuterMostFrameCompilerSynthesized()) {
+      if (numberOfFramesToRemove <= 0 && !isOuterMostFrameCompilerSynthesized()) {
         consumer.accept(prev, index);
       }
     }
@@ -259,5 +275,11 @@
       }
       return outerFrames;
     }
+
+    @Override
+    public RetraceStackTraceContext getContext() {
+      // This will change when supporting outline frames.
+      return RetraceStackTraceContext.getInitialContext();
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java
index 8ccdbdb..7d97ed2 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java
@@ -10,10 +10,8 @@
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.retrace.RetraceMethodElement;
 import com.android.tools.r8.retrace.RetraceMethodResult;
-import com.android.tools.r8.retrace.RetraceStackTraceContext;
 import com.android.tools.r8.retrace.RetracedMethodReference;
 import com.android.tools.r8.retrace.RetracedSourceFile;
-import com.android.tools.r8.retrace.Retracer;
 import com.android.tools.r8.retrace.internal.RetraceClassResultImpl.RetraceClassElementImpl;
 import com.android.tools.r8.utils.Pair;
 import com.google.common.collect.ImmutableList;
@@ -27,13 +25,13 @@
   private final MethodDefinition methodDefinition;
   private final RetraceClassResultImpl classResult;
   private final List<Pair<RetraceClassElementImpl, List<MappedRange>>> mappedRanges;
-  private final Retracer retracer;
+  private final RetracerImpl retracer;
 
   RetraceMethodResultImpl(
       RetraceClassResultImpl classResult,
       List<Pair<RetraceClassElementImpl, List<MappedRange>>> mappedRanges,
       MethodDefinition methodDefinition,
-      Retracer retracer) {
+      RetracerImpl retracer) {
     this.classResult = classResult;
     this.mappedRanges = mappedRanges;
     this.methodDefinition = methodDefinition;
@@ -149,11 +147,6 @@
     }
 
     @Override
-    public RetraceStackTraceContext getContext() {
-      return RetraceStackTraceContext.getInitialContext();
-    }
-
-    @Override
     public boolean isUnknown() {
       return methodReference.isUnknown();
     }
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackTraceContextImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackTraceContextImpl.java
index b33e06d..7cc4a30 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackTraceContextImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackTraceContextImpl.java
@@ -4,6 +4,72 @@
 
 package com.android.tools.r8.retrace.internal;
 
+import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRange;
+import com.android.tools.r8.naming.mappinginformation.RewriteFrameMappingInformation;
+import com.android.tools.r8.naming.mappinginformation.RewriteFrameMappingInformation.Condition;
+import com.android.tools.r8.naming.mappinginformation.RewriteFrameMappingInformation.RewriteAction;
+import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.retrace.RetraceStackTraceContext;
+import com.android.tools.r8.utils.ListUtils;
+import java.util.List;
 
-public class RetraceStackTraceContextImpl implements RetraceStackTraceContext {}
+public class RetraceStackTraceContextImpl implements RetraceStackTraceContext {
+
+  private final ClassReference seenException;
+
+  private RetraceStackTraceContextImpl(ClassReference seenException) {
+    this.seenException = seenException;
+  }
+
+  public ClassReference getSeenException() {
+    return seenException;
+  }
+
+  RetraceStackTraceCurrentEvaluationInformation computeRewritingInformation(
+      List<MappedRange> mappedRanges) {
+    if (mappedRanges == null || mappedRanges.isEmpty()) {
+      return RetraceStackTraceCurrentEvaluationInformation.empty();
+    }
+    RetraceStackTraceCurrentEvaluationInformation.Builder builder =
+        RetraceStackTraceCurrentEvaluationInformation.builder();
+    MappedRange last = ListUtils.last(mappedRanges);
+    for (RewriteFrameMappingInformation rewriteInformation :
+        last.getRewriteFrameMappingInformation()) {
+      if (evaluateConditions(rewriteInformation.getConditions())) {
+        for (RewriteAction action : rewriteInformation.getActions()) {
+          action.evaluate(builder);
+        }
+      }
+    }
+    return builder.build();
+  }
+
+  private boolean evaluateConditions(List<Condition> conditions) {
+    for (Condition condition : conditions) {
+      if (!condition.evaluate(this)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static class Builder {
+
+    private ClassReference seenException;
+
+    private Builder() {}
+
+    public Builder setSeenException(ClassReference seenException) {
+      this.seenException = seenException;
+      return this;
+    }
+
+    public RetraceStackTraceContextImpl build() {
+      return new RetraceStackTraceContextImpl(seenException);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackTraceCurrentEvaluationInformation.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackTraceCurrentEvaluationInformation.java
new file mode 100644
index 0000000..b25454f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackTraceCurrentEvaluationInformation.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace.internal;
+
+public class RetraceStackTraceCurrentEvaluationInformation {
+
+  private static final RetraceStackTraceCurrentEvaluationInformation EMPTY =
+      new RetraceStackTraceCurrentEvaluationInformation(0);
+
+  private final int removeInnerFrames;
+
+  private RetraceStackTraceCurrentEvaluationInformation(int removeInnerFrames) {
+    this.removeInnerFrames = removeInnerFrames;
+  }
+
+  public int getRemoveInnerFrames() {
+    return removeInnerFrames;
+  }
+
+  public static RetraceStackTraceCurrentEvaluationInformation empty() {
+    return EMPTY;
+  }
+
+  public static RetraceStackTraceCurrentEvaluationInformation.Builder builder() {
+    return new Builder();
+  }
+
+  public static class Builder {
+
+    private int removeInnerFramesCount;
+
+    public Builder incrementRemoveInnerFramesCount(int increment) {
+      removeInnerFramesCount += increment;
+      return this;
+    }
+
+    RetraceStackTraceCurrentEvaluationInformation build() {
+      return new RetraceStackTraceCurrentEvaluationInformation(removeInnerFramesCount);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetracerImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetracerImpl.java
index 0758be3..bad33d8 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetracerImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetracerImpl.java
@@ -21,9 +21,11 @@
 public class RetracerImpl implements Retracer {
 
   private final ClassNameMapper classNameMapper;
+  private final DiagnosticsHandler diagnosticsHandler;
 
-  public RetracerImpl(ClassNameMapper classNameMapper) {
+  public RetracerImpl(ClassNameMapper classNameMapper, DiagnosticsHandler diagnosticsHandler) {
     this.classNameMapper = classNameMapper;
+    this.diagnosticsHandler = diagnosticsHandler;
     assert classNameMapper != null;
   }
 
@@ -33,7 +35,8 @@
       boolean allowExperimentalMapping) {
     if (proguardMapProducer instanceof DirectClassNameMapperProguardMapProducer) {
       return new RetracerImpl(
-          ((DirectClassNameMapperProguardMapProducer) proguardMapProducer).getClassNameMapper());
+          ((DirectClassNameMapperProguardMapProducer) proguardMapProducer).getClassNameMapper(),
+          diagnosticsHandler);
     }
     try {
       ClassNameMapper classNameMapper =
@@ -42,12 +45,16 @@
               diagnosticsHandler,
               true,
               allowExperimentalMapping);
-      return new RetracerImpl(classNameMapper);
+      return new RetracerImpl(classNameMapper, diagnosticsHandler);
     } catch (Throwable throwable) {
       throw new InvalidMappingFileException(throwable);
     }
   }
 
+  public DiagnosticsHandler getDiagnosticsHandler() {
+    return diagnosticsHandler;
+  }
+
   @Override
   public RetraceMethodResultImpl retraceMethod(MethodReference methodReference) {
     return retraceClass(methodReference.getHolderClass())
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java
index 9fe783b..2e2c38f 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java
@@ -78,7 +78,9 @@
                             .setRetracedClass(classElement.getRetracedClass())
                             .joinAmbiguous(classResult.isAmbiguous())
                             .setTopFrame(true)
-                            .setContext(classElement.getContext())
+                            // We assume, since no method was defined for this stack trace element,
+                            // that this was a thrown exception.
+                            .setContext(classElement.getContextWhereClassWasThrown())
                             .applyIf(
                                 element.hasSourceFile(),
                                 builder -> {
@@ -110,7 +112,8 @@
                   frameElement -> {
                     List<RetraceStackTraceElementProxyImpl<T, ST>> retracedProxies =
                         new ArrayList<>();
-                    frameElement.visitNonCompilerSynthesizedFrames(
+                    frameElement.visitRewrittenFrames(
+                        proxy.getContext(),
                         (frame, index) -> {
                           boolean isTopFrame = index == 0;
                           retracedProxies.add(
@@ -164,7 +167,6 @@
                           .setRetracedField(fieldElement.getField())
                           .joinAmbiguous(retraceFieldResult.isAmbiguous())
                           .setTopFrame(true)
-                          .setContext(fieldElement.getContext())
                           .applyIf(
                               element.hasSourceFile(),
                               builder -> {
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 5ef1b35..7b5eee3 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -138,8 +138,6 @@
   public final Map<DexMember<?, ?>, ProguardMemberRule> assumedValues;
   /** All methods that should be inlined if possible due to a configuration directive. */
   private final Set<DexMethod> alwaysInline;
-  /** All methods that *must* be inlined due to a configuration directive (testing only). */
-  private final Set<DexMethod> forceInline;
   /** All methods that *must* never be inlined due to a configuration directive (testing only). */
   private final Set<DexMethod> neverInline;
   /**
@@ -218,7 +216,6 @@
       Map<DexMember<?, ?>, ProguardMemberRule> noSideEffects,
       Map<DexMember<?, ?>, ProguardMemberRule> assumedValues,
       Set<DexMethod> alwaysInline,
-      Set<DexMethod> forceInline,
       Set<DexMethod> neverInline,
       Set<DexMethod> neverInlineDueToSingleCaller,
       Set<DexMethod> whyAreYouNotInlining,
@@ -256,7 +253,6 @@
     this.assumedValues = assumedValues;
     this.callSites = callSites;
     this.alwaysInline = alwaysInline;
-    this.forceInline = forceInline;
     this.neverInline = neverInline;
     this.neverInlineDueToSingleCaller = neverInlineDueToSingleCaller;
     this.whyAreYouNotInlining = whyAreYouNotInlining;
@@ -302,7 +298,6 @@
         previous.noSideEffects,
         previous.assumedValues,
         previous.alwaysInline,
-        previous.forceInline,
         previous.neverInline,
         previous.neverInlineDueToSingleCaller,
         previous.whyAreYouNotInlining,
@@ -349,7 +344,6 @@
         previous.noSideEffects,
         previous.assumedValues,
         previous.alwaysInline,
-        previous.forceInline,
         previous.neverInline,
         previous.neverInlineDueToSingleCaller,
         previous.whyAreYouNotInlining,
@@ -404,7 +398,6 @@
         noSideEffects,
         assumedValues,
         alwaysInline,
-        forceInline,
         neverInline,
         neverInlineDueToSingleCaller,
         whyAreYouNotInlining,
@@ -487,7 +480,6 @@
     this.assumedValues = previous.assumedValues;
     this.callSites = previous.callSites;
     this.alwaysInline = previous.alwaysInline;
-    this.forceInline = previous.forceInline;
     this.neverInline = previous.neverInline;
     this.neverInlineDueToSingleCaller = previous.neverInlineDueToSingleCaller;
     this.whyAreYouNotInlining = previous.whyAreYouNotInlining;
@@ -599,6 +591,10 @@
     return bootstrapMethods.contains(method);
   }
 
+  public boolean isBootstrapMethod(ProgramMethod method) {
+    return isBootstrapMethod(method.getReference());
+  }
+
   public boolean isMethodTargetedByInvokeDynamic(DexMethod method) {
     return methodsTargetedByInvokeDynamic.contains(method);
   }
@@ -619,14 +615,6 @@
     return alwaysInline.isEmpty();
   }
 
-  public boolean isForceInlineMethod(DexMethod method) {
-    return forceInline.contains(method);
-  }
-
-  public boolean hasNoForceInlineMethods() {
-    return forceInline.isEmpty();
-  }
-
   public boolean isNeverInlineMethod(DexMethod method) {
     return neverInline.contains(method);
   }
@@ -1108,7 +1096,6 @@
         lens.rewriteReferenceKeys(noSideEffects, rules -> null),
         lens.rewriteReferenceKeys(assumedValues, rules -> null),
         lens.rewriteMethods(alwaysInline),
-        lens.rewriteMethods(forceInline),
         lens.rewriteMethods(neverInline),
         lens.rewriteMethods(neverInlineDueToSingleCaller),
         lens.rewriteMethods(whyAreYouNotInlining),
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 52d59bb..28c2602 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -3727,7 +3727,6 @@
             rootSet.noSideEffects,
             rootSet.assumedValues,
             amendWithCompanionMethods(rootSet.alwaysInline),
-            amendWithCompanionMethods(rootSet.forceInline),
             amendWithCompanionMethods(rootSet.neverInline),
             amendWithCompanionMethods(rootSet.neverInlineDueToSingleCaller),
             amendWithCompanionMethods(rootSet.whyAreYouNotInlining),
diff --git a/src/main/java/com/android/tools/r8/shaking/InlineRule.java b/src/main/java/com/android/tools/r8/shaking/InlineRule.java
index 32412d7..7f77fab 100644
--- a/src/main/java/com/android/tools/r8/shaking/InlineRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/InlineRule.java
@@ -10,16 +10,8 @@
 
 public class InlineRule extends ProguardConfigurationRule {
 
-  public static final Origin checkDiscardOrigin = new Origin(Origin.root()) {
-    @Override
-    public String part() {
-      return "<SYNTHETIC_CHECK_DISCARD_RULE>";
-    }
-  };
-
   public enum Type {
     ALWAYS,
-    FORCE,
     NEVER,
     NEVER_SINGLE_CALLER
   }
@@ -104,30 +96,11 @@
     return type;
   }
 
-  public ProguardCheckDiscardRule asProguardCheckDiscardRule() {
-    assert type == Type.FORCE;
-    ProguardCheckDiscardRule.Builder builder = ProguardCheckDiscardRule.builder();
-    builder.setOrigin(checkDiscardOrigin);
-    builder.setSource(null);
-    builder.addClassAnnotations(getClassAnnotations());
-    builder.setClassAccessFlags(getClassAccessFlags());
-    builder.setNegatedClassAccessFlags(getNegatedClassAccessFlags());
-    builder.setClassTypeNegated(getClassTypeNegated());
-    builder.setClassType(getClassType());
-    builder.setClassNames(getClassNames());
-    builder.addInheritanceAnnotations(getInheritanceAnnotations());
-    builder.setInheritanceIsExtends(getInheritanceIsExtends());
-    builder.setMemberRules(getMemberRules());
-    return builder.build();
-  }
-
   @Override
   String typeString() {
     switch (type) {
       case ALWAYS:
         return "alwaysinline";
-      case FORCE:
-        return "forceinline";
       case NEVER:
         return "neverinline";
       case NEVER_SINGLE_CALLER:
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 3072667..99da4fb 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -432,7 +432,7 @@
         String devMessage = "";
         if (Version.isDevelopmentVersion()
             && unknownOption != null
-            && (unknownOption.equals("forceinline") || unknownOption.equals("neverinline"))) {
+            && unknownOption.equals("neverinline")) {
           devMessage = ", this option needs to be turned on explicitly if used for tests.";
         }
         throw unknownOption(unknownOption, optionStart, devMessage);
@@ -449,14 +449,6 @@
           configurationBuilder.addRule(rule);
           return true;
         }
-        if (acceptString("forceinline")) {
-          InlineRule rule = parseInlineRule(InlineRule.Type.FORCE, optionStart);
-          configurationBuilder.addRule(rule);
-          // Insert a matching -checkdiscard rule to ensure force inlining happens.
-          ProguardCheckDiscardRule ruled = rule.asProguardCheckDiscardRule();
-          configurationBuilder.addRule(ruled);
-          return true;
-        }
         if (acceptString("keepconstantarguments")) {
           ConstantArgumentRule rule = parseConstantArgumentRule(optionStart);
           configurationBuilder.addRule(rule);
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 bbe1773..c4ddd9f 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -106,7 +106,6 @@
     private final LinkedHashMap<DexReference, DexReference> reasonAsked = new LinkedHashMap<>();
     private final LinkedHashMap<DexReference, DexReference> checkDiscarded = new LinkedHashMap<>();
     private final Set<DexMethod> alwaysInline = Sets.newIdentityHashSet();
-    private final Set<DexMethod> forceInline = Sets.newIdentityHashSet();
     private final Set<DexMethod> neverInline = Sets.newIdentityHashSet();
     private final Set<DexMethod> neverInlineDueToSingleCaller = Sets.newIdentityHashSet();
     private final Set<DexMethod> bypassClinitforInlining = Sets.newIdentityHashSet();
@@ -368,14 +367,12 @@
             bypassClinitforInlining);
       }
       assert Sets.intersection(neverInline, alwaysInline).isEmpty()
-              && Sets.intersection(neverInline, forceInline).isEmpty()
-          : "A method cannot be marked as both -neverinline and -forceinline/-alwaysinline.";
+          : "A method cannot be marked as both -neverinline and -alwaysinline.";
       return new RootSet(
           dependentMinimumKeepInfo,
           ImmutableList.copyOf(reasonAsked.values()),
           ImmutableList.copyOf(checkDiscarded.values()),
           alwaysInline,
-          forceInline,
           neverInline,
           neverInlineDueToSingleCaller,
           bypassClinitforInlining,
@@ -1193,9 +1190,6 @@
             case ALWAYS:
               alwaysInline.add(reference);
               break;
-            case FORCE:
-              forceInline.add(reference);
-              break;
             case NEVER:
               neverInline.add(reference);
               break;
@@ -1561,7 +1555,6 @@
     public final ImmutableList<DexReference> reasonAsked;
     public final ImmutableList<DexReference> checkDiscarded;
     public final Set<DexMethod> alwaysInline;
-    public final Set<DexMethod> forceInline;
     public final Set<DexMethod> bypassClinitForInlining;
     public final Set<DexMethod> whyAreYouNotInlining;
     public final Set<DexMethod> keepConstantArguments;
@@ -1584,7 +1577,6 @@
         ImmutableList<DexReference> reasonAsked,
         ImmutableList<DexReference> checkDiscarded,
         Set<DexMethod> alwaysInline,
-        Set<DexMethod> forceInline,
         Set<DexMethod> neverInline,
         Set<DexMethod> neverInlineDueToSingleCaller,
         Set<DexMethod> bypassClinitForInlining,
@@ -1618,7 +1610,6 @@
       this.reasonAsked = reasonAsked;
       this.checkDiscarded = checkDiscarded;
       this.alwaysInline = alwaysInline;
-      this.forceInline = forceInline;
       this.bypassClinitForInlining = bypassClinitForInlining;
       this.whyAreYouNotInlining = whyAreYouNotInlining;
       this.keepConstantArguments = keepConstantArguments;
@@ -1995,7 +1986,6 @@
           Collections.emptySet(),
           Collections.emptySet(),
           Collections.emptySet(),
-          Collections.emptySet(),
           PredicateSet.empty(),
           Collections.emptySet(),
           Collections.emptySet(),
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java
index a06fdb7..d660753 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java
@@ -109,11 +109,10 @@
 
   private static SyntheticMarker internalStripMarkerFromClass(
       DexProgramClass clazz, AppView<?> appView) {
-    ClassAccessFlags flags = clazz.accessFlags;
     if (clazz.superType != appView.dexItemFactory().objectType) {
       return NO_MARKER;
     }
-    if (!flags.isSynthetic() || flags.isAbstract() || flags.isEnum()) {
+    if (isDefinitelyNotSyntheticProgramClass(clazz)) {
       return NO_MARKER;
     }
     SyntheticKind kind =
@@ -139,6 +138,12 @@
         kind, SynthesizingContext.fromSyntheticInputClass(clazz, context, appView));
   }
 
+  // Filters out definitely not synthetic classes to avoid expensive computations on all classes.
+  public static boolean isDefinitelyNotSyntheticProgramClass(DexProgramClass clazz) {
+    ClassAccessFlags flags = clazz.accessFlags;
+    return !flags.isSynthetic() || flags.isEnum();
+  }
+
   private static DexType getSyntheticContextType(
       DexType type, SyntheticKind kind, DexItemFactory factory) {
     String prefix = SyntheticNaming.getPrefixForExternalSyntheticType(kind, type);
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassBuilder.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassBuilder.java
index 8c07884..fa3fc33 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassBuilder.java
@@ -29,4 +29,11 @@
   public SyntheticProgramClassBuilder self() {
     return this;
   }
+
+  @Override
+  public DexProgramClass build() {
+    DexProgramClass clazz = super.build();
+    assert !SyntheticMarker.isDefinitelyNotSyntheticProgramClass(clazz);
+    return clazz;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 3718fcf..308862a 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -214,7 +214,6 @@
   }
 
   public void disableGlobalOptimizations() {
-    enableArgumentRemoval = false;
     enableInlining = false;
     enableClassInlining = false;
     enableClassStaticizer = false;
@@ -255,7 +254,6 @@
   public boolean enableFieldBitAccessAnalysis =
       System.getProperty("com.android.tools.r8.fieldBitAccessAnalysis") != null;
   public boolean enableVerticalClassMerging = true;
-  public boolean enableArgumentRemoval = true;
   public boolean enableUnusedInterfaceRemoval = true;
   public boolean enableDevirtualization = true;
   public boolean enableInlining =
@@ -1258,6 +1256,15 @@
       return enableDynamicTypePropagation;
     }
 
+    public CallSiteOptimizationOptions setEnabled(boolean enabled) {
+      if (enabled) {
+        assert isEnabled();
+      } else {
+        disableOptimization();
+      }
+      return this;
+    }
+
     public CallSiteOptimizationOptions setEnableLegacyConstantPropagation() {
       assert !enableLegacyConstantPropagation;
       enableLegacyConstantPropagation = true;
diff --git a/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureSet.java b/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureSet.java
index b6f7c52..8ef1277 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureSet.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureSet.java
@@ -94,6 +94,10 @@
     return backing.contains(signature);
   }
 
+  public boolean contains(DexMethod method) {
+    return contains(method.getSignature());
+  }
+
   public boolean contains(DexEncodedMethod method) {
     return contains(method.getSignature());
   }
diff --git a/src/test/examples/classmerging/CallGraphCycleTest.java b/src/test/examples/classmerging/CallGraphCycleTest.java
index 40347d6..ea958d2 100644
--- a/src/test/examples/classmerging/CallGraphCycleTest.java
+++ b/src/test/examples/classmerging/CallGraphCycleTest.java
@@ -7,14 +7,14 @@
 public class CallGraphCycleTest {
 
   public static void main(String[] args) {
-    new B(true);
+    new B(args.length == 0, args.length == 1);
   }
 
   public static class A {
 
-    public A(boolean instantiateB) {
+    public A(boolean instantiateB, boolean alwaysFalse) {
       if (instantiateB) {
-        new B(false);
+        new B(alwaysFalse, alwaysFalse);
       }
       System.out.println("A(" + instantiateB + ")");
     }
@@ -22,8 +22,8 @@
 
   public static class B extends A {
 
-    public B(boolean instantiateBinA) {
-      super(instantiateBinA);
+    public B(boolean instantiateBinA, boolean alwaysFalse) {
+      super(instantiateBinA, alwaysFalse);
       System.out.println("B(" + instantiateBinA + ")");
     }
   }
diff --git a/src/test/examples/shaking1/Shaking.java b/src/test/examples/shaking1/Shaking.java
index 8c39ab8..924ae93 100644
--- a/src/test/examples/shaking1/Shaking.java
+++ b/src/test/examples/shaking1/Shaking.java
@@ -5,6 +5,7 @@
 
 public class Shaking {
   public static void main(String[] args) {
-    System.out.println(new Used("world").method());
+    String world = args.length == 0 ? "world" : null;
+    System.out.println(new Used(world).method());
   }
 }
diff --git a/src/test/examples/shaking1/print-mapping-cf.ref b/src/test/examples/shaking1/print-mapping-cf.ref
index a7e0b8e..8b38084 100644
--- a/src/test/examples/shaking1/print-mapping-cf.ref
+++ b/src/test/examples/shaking1/print-mapping-cf.ref
@@ -1,6 +1,7 @@
 shaking1.Shaking -> shaking1.Shaking:
 shaking1.Used -> a.a:
+    1:2:void <init>(java.lang.String):12:13 -> <init>
+    1:1:java.lang.String aMethodThatIsNotUsedButKept():21:21 -> aMethodThatIsNotUsedButKept
+    1:2:void main(java.lang.String[]):8:9 -> main
     1:1:java.lang.String method():17:17 -> a
-    1:1:void main(java.lang.String[]):8:8 -> main
-    1:1:void <init>(java.lang.String):12:12 -> <init>
-    1:1:java.lang.String aMethodThatIsNotUsedButKept():21:21 -> aMethodThatIsNotUsedButKept
\ No newline at end of file
+    java.lang.String name -> a
\ No newline at end of file
diff --git a/src/test/examples/shaking1/print-mapping-dex.ref b/src/test/examples/shaking1/print-mapping-dex.ref
index 7ba8ee8..5a9504a 100644
--- a/src/test/examples/shaking1/print-mapping-dex.ref
+++ b/src/test/examples/shaking1/print-mapping-dex.ref
@@ -1,5 +1,8 @@
 shaking1.Shaking -> shaking1.Shaking:
 shaking1.Used -> a.a:
-    java.lang.String method() -> a
-    0:16:void main(java.lang.String[]):8:8 -> main
-    0:3:void <init>(java.lang.String):12:12 -> <init>
+    0:2:void <init>(java.lang.String):12:12 -> <init>
+    3:5:void <init>(java.lang.String):13:13 -> <init>
+    0:6:void main(java.lang.String[]):8:8 -> main
+    7:21:void main(java.lang.String[]):9:9 -> main
+    0:19:java.lang.String method():17:17 -> a
+    java.lang.String name -> a
diff --git a/src/test/examplesJava17/records/RecordLib.java b/src/test/examplesJava17/records/RecordLib.java
new file mode 100644
index 0000000..7f1b149
--- /dev/null
+++ b/src/test/examplesJava17/records/RecordLib.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2021, 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 records;
+
+public class RecordLib {
+
+  record LibRecord(String data) {}
+
+  public static Object getRecord() {
+    return new LibRecord("data");
+  }
+}
diff --git a/src/test/examplesJava17/records/RecordMain.java b/src/test/examplesJava17/records/RecordMain.java
new file mode 100644
index 0000000..dcca88e
--- /dev/null
+++ b/src/test/examplesJava17/records/RecordMain.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2021, 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 records;
+
+public class RecordMain {
+
+  record MainRecord(String data) {}
+  ;
+
+  public static void main(String[] args) {
+    System.out.println(new MainRecord("main") instanceof java.lang.Record);
+    System.out.println(RecordLib.getRecord() instanceof java.lang.Record);
+  }
+}
diff --git a/src/test/examplesJava17/records/RecordShrinkField.java b/src/test/examplesJava17/records/RecordShrinkField.java
new file mode 100644
index 0000000..b3cd366
--- /dev/null
+++ b/src/test/examplesJava17/records/RecordShrinkField.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2021, 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 records;
+
+public class RecordShrinkField {
+
+  record Person(String name, int age, int unused) {
+    Person(String name, int age) {
+      this(name, age, -1);
+    }
+  }
+
+  public static void main(String[] args) {
+    Person jane = new Person("Jane Doe", 42);
+    Person bob = new Person("Bob", 42);
+    System.out.println(jane);
+    System.out.println(bob);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ForceInline.java b/src/test/java/com/android/tools/r8/ForceInline.java
deleted file mode 100644
index dfeace7..0000000
--- a/src/test/java/com/android/tools/r8/ForceInline.java
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Target;
-
-@Target({ElementType.METHOD})
-public @interface ForceInline {}
diff --git a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
index 33152f1..94ceeb4 100644
--- a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
+++ b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
@@ -3,15 +3,15 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
-import static com.android.tools.r8.ToolHelper.getKotlinC_1_4_20;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MAX_SUPPORTED_VERSION;
 import static com.android.tools.r8.ToolHelper.isWindows;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.TestRuntime.CfRuntime;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.utils.ArrayUtils;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.structural.Ordered;
@@ -30,12 +30,41 @@
 
 public class KotlinCompilerTool {
 
+  public enum KotlinTargetVersion {
+    NONE(""),
+    JAVA_6("JAVA_6"),
+    JAVA_8("JAVA_8");
+
+    private final String folderName;
+
+    KotlinTargetVersion(String folderName) {
+      this.folderName = folderName;
+    }
+
+    public String getFolderName() {
+      return folderName;
+    }
+
+    public String getJvmTargetString() {
+      switch (this) {
+        case JAVA_6:
+          return "1.6";
+        case JAVA_8:
+          return "1.8";
+        default:
+          throw new Unimplemented("JvmTarget not specified for " + this);
+      }
+    }
+  }
+
   public enum KotlinCompilerVersion implements Ordered<KotlinCompilerVersion> {
     KOTLINC_1_3_72("kotlin-compiler-1.3.72"),
     KOTLINC_1_4_20("kotlin-compiler-1.4.20"),
-    KOTLINC_1_5_0("kotlin-compiler-1.5.0");
+    KOTLINC_1_5_0("kotlin-compiler-1.5.0"),
+    KOTLIN_DEV("kotlin-compiler-dev");
 
     public static final KotlinCompilerVersion MIN_SUPPORTED_VERSION = KOTLINC_1_4_20;
+    public static final KotlinCompilerVersion MAX_SUPPORTED_VERSION = KOTLINC_1_5_0;
 
     private final String folder;
 
@@ -46,6 +75,10 @@
     public static KotlinCompilerVersion latest() {
       return ArrayUtils.last(values());
     }
+
+    public KotlinCompiler getCompiler() {
+      return new KotlinCompiler(this);
+    }
   }
 
   public static final class KotlinCompiler {
@@ -71,7 +104,7 @@
     }
 
     public static KotlinCompiler latest() {
-      return getKotlinC_1_4_20();
+      return MAX_SUPPORTED_VERSION.getCompiler();
     }
 
     public Path getCompiler() {
@@ -94,6 +127,30 @@
       return compilerVersion;
     }
 
+    public Path getKotlinStdlibJar() {
+      Path stdLib = getFolder().resolve("kotlin-stdlib.jar");
+      assert Files.exists(stdLib) : "Expected kotlin stdlib jar";
+      return stdLib;
+    }
+
+    public Path getKotlinReflectJar() {
+      Path reflectJar = getFolder().resolve("kotlin-reflect.jar");
+      assert Files.exists(reflectJar) : "Expected kotlin reflect jar";
+      return reflectJar;
+    }
+
+    public Path getKotlinScriptRuntime() {
+      Path reflectJar = getFolder().resolve("kotlin-script-runtime.jar");
+      assert Files.exists(reflectJar) : "Expected kotlin script runtime jar";
+      return reflectJar;
+    }
+
+    public Path getKotlinAnnotationJar() {
+      Path annotationJar = getFolder().resolve("annotations-13.0.jar");
+      assert Files.exists(annotationJar) : "Expected annotation jar";
+      return annotationJar;
+    }
+
     @Override
     public String toString() {
       return name;
diff --git a/src/test/java/com/android/tools/r8/KotlinTestBase.java b/src/test/java/com/android/tools/r8/KotlinTestBase.java
index 1696327..539fe56 100644
--- a/src/test/java/com/android/tools/r8/KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/KotlinTestBase.java
@@ -7,8 +7,8 @@
 import static org.hamcrest.CoreMatchers.containsString;
 
 import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinCompilerTool.KotlinTargetVersion;
 import com.android.tools.r8.TestRuntime.CfRuntime;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.FileUtils;
 import java.io.IOException;
diff --git a/src/test/java/com/android/tools/r8/KotlinTestParameters.java b/src/test/java/com/android/tools/r8/KotlinTestParameters.java
index 4306e53..534a42c 100644
--- a/src/test/java/com/android/tools/r8/KotlinTestParameters.java
+++ b/src/test/java/com/android/tools/r8/KotlinTestParameters.java
@@ -5,10 +5,13 @@
 
 import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
 import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.KotlinCompilerTool.KotlinTargetVersion;
+import com.google.common.collect.ImmutableList;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.function.Predicate;
+import java.util.stream.Collectors;
 
 public class KotlinTestParameters {
 
@@ -56,6 +59,8 @@
 
     private Predicate<KotlinCompilerVersion> compilerFilter = c -> false;
     private Predicate<KotlinTargetVersion> targetVersionFilter = t -> false;
+    private boolean withDevCompiler =
+        System.getProperty("com.android.tools.r8.kotlincompilerdev") != null;
 
     private Builder() {}
 
@@ -83,8 +88,13 @@
       return this;
     }
 
+    public Builder withDevCompiler() {
+      this.withDevCompiler = true;
+      return this;
+    }
+
     public Builder withAllTargetVersions() {
-      withTargetVersionFilter(t -> true);
+      withTargetVersionFilter(t -> t != KotlinTargetVersion.NONE);
       return this;
     }
 
@@ -93,6 +103,10 @@
       return this;
     }
 
+    public Builder withNoTargetVersion() {
+      return withTargetVersion(KotlinTargetVersion.NONE);
+    }
+
     public Builder withCompilersStartingFromIncluding(KotlinCompilerVersion version) {
       withCompilerFilter(c -> c.isGreaterThanOrEqualTo(version));
       return this;
@@ -101,15 +115,24 @@
     public KotlinTestParametersCollection build() {
       List<KotlinTestParameters> testParameters = new ArrayList<>();
       int index = 0;
-      for (KotlinCompilerVersion kotlinVersion : KotlinCompilerVersion.values()) {
+      List<KotlinCompilerVersion> compilerVersions;
+      if (withDevCompiler) {
+        compilerVersions = ImmutableList.of(KotlinCompilerVersion.KOTLIN_DEV);
+      } else {
+        compilerVersions =
+            Arrays.stream(KotlinCompilerVersion.values())
+                .filter(c -> c != KotlinCompilerVersion.KOTLIN_DEV && compilerFilter.test(c))
+                .collect(Collectors.toList());
+      }
+      for (KotlinCompilerVersion kotlinVersion : compilerVersions) {
         for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
           // KotlinTargetVersion java 6 is deprecated from kotlinc 1.5 and forward, no need to run
           // tests on that target.
           if (targetVersion == KotlinTargetVersion.JAVA_6
-              && kotlinVersion.equals(KotlinCompilerVersion.KOTLINC_1_5_0)) {
+              && kotlinVersion.isGreaterThanOrEqualTo(KotlinCompilerVersion.KOTLINC_1_5_0)) {
             continue;
           }
-          if (compilerFilter.test(kotlinVersion) && targetVersionFilter.test(targetVersion)) {
+          if (targetVersionFilter.test(targetVersion)) {
             testParameters.add(
                 new KotlinTestParameters(
                     new KotlinCompiler(kotlinVersion), targetVersion, index++));
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 188f23f..c4f152e 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -201,6 +201,24 @@
           .put(
               "145-alloc-tracking-stress",
               TestCondition.match(TestCondition.runtimes(DexVm.Version.V12_0_0)))
+          // Art in 12.0.0 beta4 will hang on this test when running on dx input.
+          // D8 will never generate the code introducing this (goto32 0)
+          // See: b/200660605
+          .put(
+              "083-compiler-regressions",
+              TestCondition.match(
+                  TestCondition.tools(DexTool.DX),
+                  TestCondition.compilers(CompilerUnderTest.D8),
+                  TestCondition.runtimes(DexVm.Version.V12_0_0)))
+          // Art in 12.0.0 beta4 will hang on this test when running on dx input.
+          // D8 will never generate the code introducing this (goto32 0)
+          // See: b/200660605
+          .put(
+              "121-simple-suspend-check",
+              TestCondition.match(
+                  TestCondition.tools(DexTool.DX),
+                  TestCondition.compilers(CompilerUnderTest.D8),
+                  TestCondition.runtimes(DexVm.Version.V12_0_0)))
           .build();
 
   // Tests that are flaky with the Art version we currently use.
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index d245c58..2233484 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -405,16 +405,6 @@
         "-neverinline class * { @" + annotationPackageName + ".NeverInline *; }");
   }
 
-  public T enableForceInliningAnnotations() {
-    return addForceInliningAnnotations()
-        .enableForceInliningAnnotations(ForceInline.class.getPackage().getName());
-  }
-
-  public T enableForceInliningAnnotations(String annotationPackageName) {
-    return addInternalKeepRules(
-        "-forceinline class * { @" + annotationPackageName + ".ForceInline *; }");
-  }
-
   public T enableNeverSingleCallerInlineAnnotations() {
     return addNeverSingleCallerInlineAnnotations()
         .addInternalKeepRules(
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 1fda6a4..e3c86ad 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -17,10 +17,10 @@
 import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
 import com.android.tools.r8.DataResourceProvider.Visitor;
 import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinCompilerTool.KotlinTargetVersion;
 import com.android.tools.r8.TestRuntime.CfRuntime;
 import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
 import com.android.tools.r8.ToolHelper.DexVm;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.code.Instruction;
@@ -1153,9 +1153,7 @@
    * specified class and add rules to inline methods with the inlining annotation.
    */
   public static String keepMainProguardConfigurationWithInliningAnnotation(Class<?> clazz) {
-    return "-forceinline class * { @com.android.tools.r8.ForceInline *; }"
-        + System.lineSeparator()
-        + "-neverinline class * { @com.android.tools.r8.NeverInline *; }"
+    return "-neverinline class * { @com.android.tools.r8.NeverInline *; }"
         + System.lineSeparator()
         + keepMainProguardConfiguration(clazz);
   }
diff --git a/src/test/java/com/android/tools/r8/TestBuilder.java b/src/test/java/com/android/tools/r8/TestBuilder.java
index 656955e..aec53db 100644
--- a/src/test/java/com/android/tools/r8/TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestBuilder.java
@@ -190,7 +190,6 @@
     return ImmutableList.of(
         AlwaysInline.class,
         AssumeMayHaveSideEffects.class,
-        ForceInline.class,
         KeepConstantArguments.class,
         KeepUnusedArguments.class,
         NeverClassInline.class,
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index fe6cc31..4004a47 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -395,10 +395,6 @@
     return addTestingAnnotation(KeepConstantArguments.class);
   }
 
-  public final T addForceInliningAnnotations() {
-    return addTestingAnnotation(ForceInline.class);
-  }
-
   public final T addInliningAnnotations() {
     return addTestingAnnotation(NeverInline.class);
   }
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index ec57f95..14db34c 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -3,9 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
-import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72;
-import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
-import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_5_0;
 import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
 import static com.android.tools.r8.utils.FileUtils.JAVA_EXTENSION;
 import static com.android.tools.r8.utils.FileUtils.isDexFile;
@@ -14,12 +11,10 @@
 import static org.junit.Assert.fail;
 
 import com.android.tools.r8.DeviceRunner.DeviceRunnerConfigurationException;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
 import com.android.tools.r8.TestBase.Backend;
 import com.android.tools.r8.TestRuntime.CfRuntime;
 import com.android.tools.r8.ToolHelper.DexVm.Kind;
 import com.android.tools.r8.dex.ApplicationReader;
-import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.AssemblyWriter;
@@ -895,34 +890,6 @@
     throw new Unreachable("Unable to find a most recent android.jar");
   }
 
-  public static Path getKotlinStdlibJar(KotlinCompiler kotlinc) {
-    Path stdLib = kotlinc.getFolder().resolve("kotlin-stdlib.jar");
-    assert Files.exists(stdLib) : "Expected kotlin stdlib jar";
-    return stdLib;
-  }
-
-  public static Path getKotlinReflectJar(KotlinCompiler kotlinc) {
-    Path reflectJar = kotlinc.getFolder().resolve("kotlin-reflect.jar");
-    assert Files.exists(reflectJar) : "Expected kotlin reflect jar";
-    return reflectJar;
-  }
-
-  public static Path getKotlinScriptRuntime(KotlinCompiler kotlinc) {
-    Path reflectJar = kotlinc.getFolder().resolve("kotlin-script-runtime.jar");
-    assert Files.exists(reflectJar) : "Expected kotlin script runtime jar";
-    return reflectJar;
-  }
-
-  public static Path getKotlinAnnotationJar(KotlinCompiler kotlinc) {
-    Path annotationJar = kotlinc.getFolder().resolve("annotations-13.0.jar");
-    assert Files.exists(annotationJar) : "Expected annotation jar";
-    return annotationJar;
-  }
-
-  public static Path getMostRecentKotlinAnnotationJar() {
-    return getKotlinAnnotationJar(KotlinCompiler.latest());
-  }
-
   public static Path getJdwpTestsCfJarPath(AndroidApiLevel minSdk) {
     if (minSdk.getLevel() >= AndroidApiLevel.N.getLevel()) {
       return Paths.get("third_party", "jdwp-tests", "apache-harmony-jdwp-tests-host.jar");
@@ -2206,49 +2173,7 @@
         options,
         null);
   }
-
-  public enum KotlinTargetVersion {
-    JAVA_6("JAVA_6"),
-    JAVA_8("JAVA_8");
-
-    private final String folderName;
-
-    KotlinTargetVersion(String folderName) {
-      this.folderName = folderName;
-    }
-
-    public String getFolderName() {
-      return folderName;
-    }
-
-    public String getJvmTargetString() {
-      switch (this) {
-        case JAVA_6:
-          return "1.6";
-        case JAVA_8:
-          return "1.8";
-        default:
-          throw new Unimplemented("JvmTarget not specified for " + this);
-      }
-    }
-  }
-
-  public static KotlinCompiler getKotlinC_1_3_72() {
-    return new KotlinCompiler(KOTLINC_1_3_72);
-  }
-
-  public static KotlinCompiler getKotlinC_1_4_20() {
-    return new KotlinCompiler(KOTLINC_1_4_20);
-  }
-
-  public static KotlinCompiler getKotlinC_1_5_0() {
-    return new KotlinCompiler(KOTLINC_1_5_0);
-  }
-
-  public static KotlinCompiler[] getKotlinCompilers() {
-    return new KotlinCompiler[] {getKotlinC_1_3_72(), getKotlinC_1_4_20(), getKotlinC_1_5_0()};
-  }
-
+  
   public static void disassemble(AndroidApp app, PrintStream ps) throws IOException {
     DexApplication application =
         new ApplicationReader(app, new InternalOptions(), Timing.empty()).read().toDirect();
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java
index 19c8015..29e44c0 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java
@@ -113,7 +113,7 @@
 
     MethodSignature barMethodSignatureAfterArgumentRemoval =
         enableUnusedArgumentRemoval
-            ? new MethodSignature("bara", STRING, ImmutableList.of())
+            ? new MethodSignature("bar$1", STRING, ImmutableList.of())
             : new MethodSignature("bar", STRING, ImmutableList.of("int"));
     assertPublic(inspector, A.class, new MethodSignature("baz", STRING, ImmutableList.of()));
     assertPublic(inspector, A.class, new MethodSignature("bar", STRING, ImmutableList.of()));
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 8540d13..14b6376 100644
--- a/src/test/java/com/android/tools/r8/annotations/SourceDebugExtensionTest.java
+++ b/src/test/java/com/android/tools/r8/annotations/SourceDebugExtensionTest.java
@@ -5,20 +5,18 @@
 package com.android.tools.r8.annotations;
 
 import static com.android.tools.r8.ToolHelper.getFilesInTestFolderRelativeToClass;
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinCompilerTool.KotlinTargetVersion;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRuntime;
 import com.android.tools.r8.TestRuntime.CfRuntime;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.retrace.KotlinInlineFunctionRetraceTest;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.codeinspector.AnnotationSubject;
@@ -37,25 +35,28 @@
 public class SourceDebugExtensionTest extends TestBase {
 
   private final TestParameters parameters;
-  private final KotlinCompiler kotlinCompiler;
+  private final KotlinTestParameters kotlinTestParameters;
 
   @Parameters(name = "{0}, kotlinc: {1}")
   public static List<Object[]> data() {
     return buildParameters(
-        getTestParameters().withAllRuntimesAndApiLevels().build(), getKotlinCompilers());
+        getTestParameters().withAllRuntimesAndApiLevels().build(),
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
-  public SourceDebugExtensionTest(TestParameters parameters, KotlinCompiler kotlinCompiler) {
+  public SourceDebugExtensionTest(
+      TestParameters parameters, KotlinTestParameters kotlinTestParameters) {
     this.parameters = parameters;
-    this.kotlinCompiler = kotlinCompiler;
+    this.kotlinTestParameters = kotlinTestParameters;
   }
 
   @Test
   public void testR8() throws IOException, CompilationFailedException, ExecutionException {
     CfRuntime cfRuntime =
         parameters.isCfRuntime() ? parameters.getRuntime().asCf() : TestRuntime.getCheckedInJdk9();
+    KotlinCompiler kotlinc = kotlinTestParameters.getCompiler();
     Path kotlinSources =
-        kotlinc(cfRuntime, getStaticTemp(), kotlinCompiler, KotlinTargetVersion.JAVA_8)
+        kotlinc(cfRuntime, getStaticTemp(), kotlinc, KotlinTargetVersion.JAVA_8)
             .addSourceFiles(
                 getFilesInTestFolderRelativeToClass(
                     KotlinInlineFunctionRetraceTest.class, "kt", ".kt"))
@@ -63,8 +64,7 @@
     CodeInspector kotlinInspector = new CodeInspector(kotlinSources);
     inspectSourceDebugExtension(kotlinInspector);
     testForR8(parameters.getBackend())
-        .addClasspathFiles(
-            getKotlinStdlibJar(kotlinCompiler), getKotlinAnnotationJar(kotlinCompiler))
+        .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
         .addProgramFiles(kotlinSources)
         .addKeepAttributes(ProguardKeepAttributes.SOURCE_DEBUG_EXTENSION)
         .addKeepAllClassesRule()
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 3dd6878..d5e17f4 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
@@ -3,14 +3,14 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.cf.bootstrap;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinCompilerTool.KotlinTargetVersion;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.internal.CompilationTestBase;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -38,28 +38,34 @@
   private static final int MAX_SIZE = (int) (31361268 * 0.4);
 
   private final TestParameters parameters;
-  private final KotlinCompiler kotlinCompiler;
+  private final KotlinTestParameters kotlinTestParameters;
 
   @Parameters(name = "{0}, kotlinc: {1}")
   public static List<Object[]> data() {
-    return buildParameters(getTestParameters().withCfRuntimes().build(), getKotlinCompilers());
+    return buildParameters(
+        getTestParameters().withCfRuntimes().build(),
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
-  public KotlinCompilerTreeShakingTest(TestParameters parameters, KotlinCompiler kotlinCompiler) {
+  public KotlinCompilerTreeShakingTest(
+      TestParameters parameters, KotlinTestParameters kotlinTestParameters) {
     this.parameters = parameters;
-    this.kotlinCompiler = kotlinCompiler;
+    this.kotlinTestParameters = kotlinTestParameters;
   }
 
   @Test
   public void testForRuntime() throws Exception {
     // Compile Hello.kt and make sure it works as expected.
     Path classPathBefore =
-        kotlinc(parameters.getRuntime().asCf(), kotlinCompiler, KotlinTargetVersion.JAVA_8)
+        kotlinc(
+                parameters.getRuntime().asCf(),
+                kotlinTestParameters.getCompiler(),
+                kotlinTestParameters.getTargetVersion())
             .addSourceFiles(HELLO_KT)
             .setOutputPath(temp.newFolder().toPath())
             .compile();
     testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinCompiler))
+        .addRunClasspathFiles(kotlinTestParameters.getCompiler().getKotlinStdlibJar())
         .addClasspath(classPathBefore)
         .run(parameters.getRuntime(), PKG_NAME + ".HelloKt")
         .assertSuccessWithOutputLines("I'm Woody. Howdy, howdy, howdy.");
@@ -73,17 +79,18 @@
 
   @Test
   public void test() throws Exception {
+    KotlinCompiler kotlinc = kotlinTestParameters.getCompiler();
     List<Path> libs =
         ImmutableList.of(
-            ToolHelper.getKotlinStdlibJar(kotlinCompiler),
-            ToolHelper.getKotlinReflectJar(kotlinCompiler),
-            ToolHelper.getKotlinScriptRuntime(kotlinCompiler));
+            kotlinc.getKotlinStdlibJar(),
+            kotlinc.getKotlinReflectJar(),
+            kotlinc.getKotlinScriptRuntime());
     // Process kotlin-compiler.jar.
     Path r8ProcessedKotlinc =
         testForR8(parameters.getBackend())
             .addLibraryFiles(libs)
             .addLibraryFiles(ToolHelper.getJava8RuntimeJar())
-            .addProgramFiles(kotlinCompiler.getCompiler())
+            .addProgramFiles(kotlinc.getCompiler())
             .addKeepAttributes("*Annotation*")
             .addKeepClassAndMembersRules(ToolHelper.K2JVMCompiler)
             .addKeepClassAndMembersRules("**.K2JVMCompilerArguments")
@@ -120,13 +127,13 @@
         kotlinc(
                 parameters.getRuntime().asCf(),
                 new KotlinCompiler(
-                    "r8ProcessedKotlinc", r8ProcessedKotlinc, kotlinCompiler.getCompilerVersion()),
+                    "r8ProcessedKotlinc", r8ProcessedKotlinc, kotlinc.getCompilerVersion()),
                 KotlinTargetVersion.JAVA_8)
             .addSourceFiles(HELLO_KT)
             .setOutputPath(temp.newFolder().toPath())
             .compile();
     testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinCompiler))
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar())
         .addClasspath(classPathAfter)
         .run(parameters.getRuntime(), PKG_NAME + ".HelloKt")
         .assertSuccessWithOutputLines("I'm Woody. Howdy, howdy, howdy.");
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
index e0c6c9e..06bfb5b 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
@@ -38,8 +38,7 @@
         .enableNoHorizontalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines(
-            "changed", "13", "42", "foo", "7", "foo", "print a", "print b")
+        .assertSuccessWithOutputLines("changed", "0", "42", "foo", "7", "foo", "print a", "print b")
         .inspect(
             codeInspector -> {
               ClassSubject changedClassSubject = codeInspector.clazz(Changed.class);
@@ -118,7 +117,7 @@
   public static class Main {
     public static void main(String[] args) {
       Parent p = new Changed();
-      A a = new A(13);
+      A a = new A(args.length);
       a = new A(p);
       B b = new B(p);
       a.print();
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndExtraNullsMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndExtraNullsMergingTest.java
index 3917d56..6094157 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndExtraNullsMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndExtraNullsMergingTest.java
@@ -75,14 +75,14 @@
                   isPresent());
             })
         .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines("C", "42", "C", "D");
+        .assertSuccessWithOutputLines("C", "0", "C", "D");
   }
 
   static class Main {
 
     public static void main(String[] args) {
       System.out.println(new A(new C()));
-      System.out.println(new A(new C(), 42));
+      System.out.println(new A(new C(), args.length));
       System.out.println(new B(new D()));
     }
   }
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMemberAccessTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMemberAccessTest.java
index dd9ab2a..9b45774 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMemberAccessTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMemberAccessTest.java
@@ -30,7 +30,7 @@
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines("foo", "B", "bar", "5", "foobar")
+        .assertSuccessWithOutputLines("foo", "B", "bar", "0", "foobar")
         .inspect(
             codeInspector -> {
               assertThat(codeInspector.clazz(A.class), isAbsent());
@@ -56,7 +56,7 @@
       a.foo();
       B b = a.get("B");
       b.bar();
-      C c = new C(5);
+      C c = new C(args.length);
       c.foobar();
     }
   }
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
index 3b35a12..8f4273f 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
@@ -635,13 +635,14 @@
                 .addKeepRules(
                     getProguardConfig(
                         EXAMPLE_KEEP,
-                        "-forceinline class classmerging.ProguardMethodMapTest$A { public void"
+                        "-alwaysinline class classmerging.ProguardMethodMapTest$A { public void"
                             + " method(); }"))
                 .addOptionsModification(this::configure)
                 .addOptionsModification(
                     options -> {
                       options.enableVerticalClassMerging = false;
-                      options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
+                      options.testing.validInliningReasons =
+                          ImmutableSet.of(Reason.ALWAYS, Reason.FORCE);
                     })
                 .allowUnusedProguardConfigurationRules(),
             main,
@@ -675,12 +676,13 @@
                 .addKeepRules(
                     getProguardConfig(
                         EXAMPLE_KEEP,
-                        "-forceinline class classmerging.ProguardMethodMapTest$A { public void"
+                        "-alwaysinline class classmerging.ProguardMethodMapTest$A { public void"
                             + " method(); }"))
                 .addOptionsModification(this::configure)
                 .addOptionsModification(
                     options -> {
-                      options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
+                      options.testing.validInliningReasons =
+                          ImmutableSet.of(Reason.ALWAYS, Reason.FORCE);
                     })
                 .allowUnusedProguardConfigurationRules(),
             main,
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java b/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
index eebed6b..f93bfb4 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
@@ -3,15 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.debug;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestDiagnosticMessages;
 import com.android.tools.r8.TestParameters;
@@ -26,33 +24,35 @@
 public class KotlinStdLibCompilationTest extends TestBase {
 
   private final TestParameters parameters;
-  private final KotlinCompiler kotlinc;
+  private final KotlinTestParameters kotlinTestParameters;
 
   @Parameters(name = "{0}, kotlinc: {1}")
   public static List<Object[]> setup() {
     return buildParameters(
         TestParametersBuilder.builder().withAllRuntimesAndApiLevels().build(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilers().withNoTargetVersion().build());
   }
 
-  public KotlinStdLibCompilationTest(TestParameters parameters, KotlinCompiler kotlinc) {
+  public KotlinStdLibCompilationTest(
+      TestParameters parameters, KotlinTestParameters kotlinTestParameters) {
     this.parameters = parameters;
-    this.kotlinc = kotlinc;
+    this.kotlinTestParameters = kotlinTestParameters;
   }
 
   @Test
   public void testD8() throws CompilationFailedException {
     assumeTrue(parameters.isDexRuntime());
     testForD8()
-        .addProgramFiles(getKotlinStdlibJar(kotlinc))
+        .addProgramFiles(kotlinTestParameters.getCompiler().getKotlinStdlibJar())
         .setMinApi(parameters.getApiLevel())
         .compileWithExpectedDiagnostics(TestDiagnosticMessages::assertNoMessages);
   }
 
   @Test
   public void testR8() throws CompilationFailedException {
+    KotlinCompiler compiler = kotlinTestParameters.getCompiler();
     testForR8(parameters.getBackend())
-        .addProgramFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+        .addProgramFiles(compiler.getKotlinStdlibJar(), compiler.getKotlinAnnotationJar())
         .addKeepAllAttributes()
         .allowDiagnosticWarningMessages()
         .noMinification()
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 c10af51..b0c3032 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
@@ -15,6 +15,7 @@
 
 import com.android.tools.r8.D8TestRunResult;
 import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer;
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
 import com.android.tools.r8.KotlinTestBase.KotlinCompileMemoizer;
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.R8FullTestBuilder;
@@ -51,6 +52,7 @@
 
   private final TestParameters parameters;
   private final KotlinTestParameters kotlinParameters;
+  private final KotlinCompiler kotlinc;
   private final boolean shrinkDesugaredLibrary;
 
   @Parameters(name = "{0}, {1}, shrinkDesugaredLibrary: {2}")
@@ -67,6 +69,7 @@
       boolean shrinkDesugaredLibrary) {
     this.parameters = parameters;
     this.kotlinParameters = kotlinParameters;
+    this.kotlinc = kotlinParameters.getCompiler();
     this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
   }
 
@@ -83,8 +86,8 @@
     assumeTrue(parameters.getRuntime().isCf());
     testForRuntime(parameters)
         .addProgramFiles(compiledJars.getForConfiguration(kotlinParameters))
-        .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinParameters.getCompiler()))
-        .addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinParameters.getCompiler()))
+        .addProgramFiles(kotlinc.getKotlinStdlibJar())
+        .addProgramFiles(kotlinc.getKotlinReflectJar())
         .run(parameters.getRuntime(), PKG + ".MainKt")
         .assertSuccessWithOutputLines(EXPECTED_OUTPUT);
   }
@@ -98,8 +101,8 @@
         testForD8()
             .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
             .addProgramFiles(compiledJars.getForConfiguration(kotlinParameters))
-            .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinParameters.getCompiler()))
-            .addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinParameters.getCompiler()))
+            .addProgramFiles(kotlinc.getKotlinStdlibJar())
+            .addProgramFiles(kotlinc.getKotlinReflectJar())
             .setProgramConsumer(new ArchiveConsumer(output.toPath(), true))
             .setMinApi(parameters.getApiLevel())
             .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
@@ -128,9 +131,9 @@
         testForR8(parameters.getBackend())
             .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
             .addProgramFiles(compiledJars.getForConfiguration(kotlinParameters))
-            .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinParameters.getCompiler()))
-            .addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinParameters.getCompiler()))
-            .addProgramFiles(ToolHelper.getKotlinAnnotationJar(kotlinParameters.getCompiler()))
+            .addProgramFiles(kotlinc.getKotlinStdlibJar())
+            .addProgramFiles(kotlinc.getKotlinReflectJar())
+            .addProgramFiles(kotlinc.getKotlinAnnotationJar())
             .addKeepMainRule(PKG + ".MainKt")
             .addKeepAllClassesRule()
             .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaPrivateInstanceInterfaceMethodWithNonLambdaCallSiteTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaPrivateInstanceInterfaceMethodWithNonLambdaCallSiteTest.java
index 7d0b2fa..6961fe9 100644
--- a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaPrivateInstanceInterfaceMethodWithNonLambdaCallSiteTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaPrivateInstanceInterfaceMethodWithNonLambdaCallSiteTest.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.desugar.lambdas;
 
+import static org.junit.Assert.assertFalse;
+
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
@@ -41,7 +43,21 @@
         .addProgramClasses(Main.class, A.class, FunctionalInterface.class)
         .addProgramClassFileData(getProgramClassFileData())
         .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines("Hello world!", "Hello world!");
+        .assertSuccessWithOutputLines("Hello world!", "Hello world!")
+        .inspect(
+            inspector -> {
+              if (parameters.isDexRuntime()
+                  && !parameters.canUseDefaultAndStaticInterfaceMethods()) {
+                inspector
+                    .clazz(I.class)
+                    .toCompanionClass()
+                    .forAllMethods(
+                        m ->
+                            // We don't expect any synthetic accessors to be needed for the private
+                            // interface method.
+                            assertFalse("Unexpected synthetic method: " + m, m.isSynthetic()));
+              }
+            });
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaWithPrivateInterfaceInvokeTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaWithPrivateInterfaceInvokeTest.java
new file mode 100644
index 0000000..d66b1d2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaWithPrivateInterfaceInvokeTest.java
@@ -0,0 +1,114 @@
+// Copyright (c) 2021, 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.desugar.lambdas;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+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.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class LambdaWithPrivateInterfaceInvokeTest extends TestBase {
+
+  static final String EXPECTED = StringUtils.lines("Hello world");
+
+  private final TestParameters parameters;
+  private final boolean useInvokeSpecial;
+
+  @Parameterized.Parameters(name = "{0}, invokespecial:{1}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+  }
+
+  public LambdaWithPrivateInterfaceInvokeTest(TestParameters parameters, boolean useInvokeSpecial) {
+    this.parameters = parameters;
+    this.useInvokeSpecial = useInvokeSpecial;
+  }
+
+  @Test
+  public void testReference() throws Exception {
+    testForRuntime(parameters)
+        .addProgramClasses(TestClass.class, MyFun.class, A.class)
+        .addProgramClassFileData(getTransformForI())
+        .run(parameters.getRuntime(), TestClass.class)
+        // On JDK 8 and 9 the VM will fail if not targeting with invoke special.
+        .applyIf(
+            !useInvokeSpecial
+                && parameters.isCfRuntime()
+                && parameters.asCfRuntime().isOlderThan(CfVm.JDK11),
+            r -> r.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class),
+            r -> r.assertSuccessWithOutput(EXPECTED));
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(TestClass.class, MyFun.class, A.class)
+        .addProgramClassFileData(getTransformForI())
+        .addKeepMainRule(TestClass.class)
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  private byte[] getTransformForI() throws Exception {
+    return transformer(I.class)
+        .setPrivate(I.class.getDeclaredMethod("bar"))
+        .transformMethodInsnInMethod(
+            "lambda$foo$0",
+            (opcode, owner, name, descriptor, isInterface, visitor) -> {
+              if (name.equals("bar")) {
+                assertEquals(Opcodes.INVOKEINTERFACE, opcode);
+                visitor.visitMethodInsn(
+                    useInvokeSpecial ? Opcodes.INVOKESPECIAL : Opcodes.INVOKEINTERFACE,
+                    owner,
+                    name,
+                    descriptor,
+                    isInterface);
+              } else {
+                visitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+              }
+            })
+        .transform();
+  }
+
+  interface I {
+    /* private */ default String bar() {
+      return "Hello world";
+    }
+
+    default void foo() {
+      TestClass.run(
+          () -> {
+            System.out.println(bar());
+          });
+    }
+  }
+
+  interface MyFun {
+    void run();
+  }
+
+  static class A implements I {}
+
+  static class TestClass {
+
+    public static void run(MyFun fn) {
+      fn.run();
+    }
+
+    public static void main(String[] args) {
+      new A().foo();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordAnnotationTest.java b/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordAnnotationTest.java
index 4ea7cd7..2446b14 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordAnnotationTest.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.desugar.records;
 
-import static com.android.tools.r8.desugar.records.RecordTestUtils.RECORD_KEEP_RULE_R8_CF_TO_CF;
 import static com.android.tools.r8.utils.InternalOptions.TestingOptions;
 
 import com.android.tools.r8.R8FullTestBuilder;
@@ -70,11 +69,12 @@
             .addProgramClassFileData(PROGRAM_DATA)
             .setMinApi(parameters.getApiLevel())
             .addKeepRules("-keep class records.EmptyRecordAnnotation { *; }")
+            .addKeepRules("-keepattributes *Annotation*")
+            .addKeepRules("-keep class records.EmptyRecordAnnotation$Empty")
             .addKeepMainRule(MAIN_TYPE)
             .addOptionsModification(TestingOptions::allowExperimentClassFileVersion);
     if (parameters.isCfRuntime()) {
       builder
-          .addKeepRules(RECORD_KEEP_RULE_R8_CF_TO_CF)
           .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
           .compile()
           .inspect(RecordTestUtils::assertRecordsAreRecords)
@@ -84,8 +84,6 @@
       return;
     }
     builder
-        .addKeepRules("-keepattributes *Annotation*")
-        .addKeepRules("-keep class records.EmptyRecordAnnotation$Empty")
         .addKeepRules("-keep class java.lang.Record")
         .run(parameters.getRuntime(), MAIN_TYPE)
         .assertSuccessWithOutput(EXPECTED_RESULT_DEX);
diff --git a/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordTest.java b/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordTest.java
index effcba3..7a64c3c 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordTest.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.desugar.records;
 
-import static com.android.tools.r8.desugar.records.RecordTestUtils.RECORD_KEEP_RULE_R8_CF_TO_CF;
 import static com.android.tools.r8.utils.InternalOptions.TestingOptions;
 
 import com.android.tools.r8.R8FullTestBuilder;
@@ -23,7 +22,8 @@
   private static final String RECORD_NAME = "EmptyRecord";
   private static final byte[][] PROGRAM_DATA = RecordTestUtils.getProgramData(RECORD_NAME);
   private static final String MAIN_TYPE = RecordTestUtils.getMainType(RECORD_NAME);
-  private static final String EXPECTED_RESULT = StringUtils.lines("Empty[]");
+  private static final String EXPECTED_RESULT_D8 = StringUtils.lines("Empty[]");
+  private static final String EXPECTED_RESULT_R8 = StringUtils.lines("a[]");
 
   private final TestParameters parameters;
 
@@ -48,7 +48,7 @@
       testForJvm()
           .addProgramClassFileData(PROGRAM_DATA)
           .run(parameters.getRuntime(), MAIN_TYPE)
-          .assertSuccessWithOutput(EXPECTED_RESULT);
+          .assertSuccessWithOutput(EXPECTED_RESULT_D8);
     }
     testForD8(parameters.getBackend())
         .addProgramClassFileData(PROGRAM_DATA)
@@ -56,7 +56,7 @@
         .addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
         .compile()
         .run(parameters.getRuntime(), MAIN_TYPE)
-        .assertSuccessWithOutput(EXPECTED_RESULT);
+        .assertSuccessWithOutput(EXPECTED_RESULT_D8);
   }
 
   @Test
@@ -69,14 +69,13 @@
             .addOptionsModification(TestingOptions::allowExperimentClassFileVersion);
     if (parameters.isCfRuntime()) {
       builder
-          .addKeepRules(RECORD_KEEP_RULE_R8_CF_TO_CF)
           .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
           .compile()
           .inspect(RecordTestUtils::assertRecordsAreRecords)
           .run(parameters.getRuntime(), MAIN_TYPE)
-          .assertSuccessWithOutput(EXPECTED_RESULT);
+          .assertSuccessWithOutput(EXPECTED_RESULT_R8);
       return;
     }
-    builder.run(parameters.getRuntime(), MAIN_TYPE).assertSuccessWithOutput(EXPECTED_RESULT);
+    builder.run(parameters.getRuntime(), MAIN_TYPE).assertSuccessWithOutput(EXPECTED_RESULT_R8);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordInstanceOfTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordInstanceOfTest.java
index d4da59e..1c32b27 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordInstanceOfTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordInstanceOfTest.java
@@ -4,8 +4,6 @@
 
 package com.android.tools.r8.desugar.records;
 
-import static com.android.tools.r8.desugar.records.RecordTestUtils.RECORD_KEEP_RULE_R8_CF_TO_CF;
-
 import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -69,7 +67,6 @@
             .addOptionsModification(TestingOptions::allowExperimentClassFileVersion);
     if (parameters.isCfRuntime()) {
       builder
-          .addKeepRules(RECORD_KEEP_RULE_R8_CF_TO_CF)
           .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
           .compile()
           .inspect(RecordTestUtils::assertRecordsAreRecords)
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomSplitDesugaringTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomSplitDesugaringTest.java
index 2d771cc..9ee21d2 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomSplitDesugaringTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomSplitDesugaringTest.java
@@ -22,7 +22,7 @@
   private static final String MAIN_TYPE = RecordTestUtils.getMainType(RECORD_NAME);
   private static final String EXPECTED_RESULT =
       StringUtils.lines(
-          "Empty[]",
+          "%s[]",
           "true",
           "true",
           "true",
@@ -33,7 +33,10 @@
           "true",
           "false",
           "false",
-          "Person[name=Jane Doe, age=42]");
+          "%s[name=Jane Doe, age=42]");
+  private static final String EXPECTED_RESULT_D8 =
+      String.format(EXPECTED_RESULT, "Empty", "Person");
+  private static final String EXPECTED_RESULT_R8 = String.format(EXPECTED_RESULT, "a", "b");
 
   private final TestParameters parameters;
 
@@ -62,7 +65,7 @@
         .addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
         .compile()
         .run(parameters.getRuntime(), MAIN_TYPE)
-        .assertSuccessWithOutput(EXPECTED_RESULT);
+        .assertSuccessWithOutput(EXPECTED_RESULT_D8);
   }
 
   @Test
@@ -81,6 +84,6 @@
         .addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
         .compile()
         .run(parameters.getRuntime(), MAIN_TYPE)
-        .assertSuccessWithOutput(EXPECTED_RESULT);
+        .assertSuccessWithOutput(EXPECTED_RESULT_R8);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomTest.java
index 1fc2f3e..228275b 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomTest.java
@@ -4,8 +4,6 @@
 
 package com.android.tools.r8.desugar.records;
 
-import static com.android.tools.r8.desugar.records.RecordTestUtils.RECORD_KEEP_RULE_R8_CF_TO_CF;
-
 import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -25,7 +23,7 @@
   private static final String MAIN_TYPE = RecordTestUtils.getMainType(RECORD_NAME);
   private static final String EXPECTED_RESULT =
       StringUtils.lines(
-          "Empty[]",
+          "%s[]",
           "true",
           "true",
           "true",
@@ -36,7 +34,10 @@
           "true",
           "false",
           "false",
-          "Person[name=Jane Doe, age=42]");
+          "%s[name=Jane Doe, age=42]");
+  private static final String EXPECTED_RESULT_D8 =
+      String.format(EXPECTED_RESULT, "Empty", "Person");
+  private static final String EXPECTED_RESULT_R8 = String.format(EXPECTED_RESULT, "a", "b");
 
   private final TestParameters parameters;
 
@@ -61,7 +62,7 @@
       testForJvm()
           .addProgramClassFileData(PROGRAM_DATA)
           .run(parameters.getRuntime(), MAIN_TYPE)
-          .assertSuccessWithOutput(EXPECTED_RESULT);
+          .assertSuccessWithOutput(EXPECTED_RESULT_D8);
     }
     testForD8(parameters.getBackend())
         .addProgramClassFileData(PROGRAM_DATA)
@@ -69,7 +70,7 @@
         .addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
         .compile()
         .run(parameters.getRuntime(), MAIN_TYPE)
-        .assertSuccessWithOutput(EXPECTED_RESULT);
+        .assertSuccessWithOutput(EXPECTED_RESULT_D8);
   }
 
   @Test
@@ -82,14 +83,13 @@
             .addOptionsModification(TestingOptions::allowExperimentClassFileVersion);
     if (parameters.isCfRuntime()) {
       builder
-          .addKeepRules(RECORD_KEEP_RULE_R8_CF_TO_CF)
           .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
           .compile()
           .inspect(RecordTestUtils::assertRecordsAreRecords)
           .run(parameters.getRuntime(), MAIN_TYPE)
-          .assertSuccessWithOutput(EXPECTED_RESULT);
+          .assertSuccessWithOutput(EXPECTED_RESULT_R8);
       return;
     }
-    builder.run(parameters.getRuntime(), MAIN_TYPE).assertSuccessWithOutput(EXPECTED_RESULT);
+    builder.run(parameters.getRuntime(), MAIN_TYPE).assertSuccessWithOutput(EXPECTED_RESULT_R8);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordLibMergeTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordLibMergeTest.java
new file mode 100644
index 0000000..7c32b98
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordLibMergeTest.java
@@ -0,0 +1,79 @@
+// Copyright (c) 2021, 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.desugar.records;
+
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.utils.InternalOptions.TestingOptions;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RecordLibMergeTest extends TestBase {
+
+  private static final String RECORD_LIB = "RecordLib";
+  private static final String RECORD_MAIN = "RecordMain";
+  private static final byte[][] PROGRAM_DATA_LIB = RecordTestUtils.getProgramData(RECORD_LIB);
+  private static final byte[][] PROGRAM_DATA_MAIN = RecordTestUtils.getProgramData(RECORD_MAIN);
+  private static final String MAIN_TYPE = RecordTestUtils.getMainType(RECORD_MAIN);
+  private static final String EXPECTED_RESULT = StringUtils.lines("true", "true");
+
+  private final TestParameters parameters;
+
+  public RecordLibMergeTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Parameterized.Parameters(name = "{0}")
+  public static List<Object[]> data() {
+    // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk17).
+    return buildParameters(
+        getTestParameters()
+            .withCustomRuntime(CfRuntime.getCheckedInJdk17())
+            .withDexRuntimes()
+            .withAllApiLevelsAlsoForCf()
+            .build());
+  }
+
+  @Test
+  public void testR8Merge() throws Exception {
+    Path lib =
+        testForR8(Backend.CF)
+            .addProgramClassFileData(PROGRAM_DATA_LIB)
+            .setMinApi(parameters.getApiLevel())
+            .addKeepRules(
+                "-keep class records.RecordLib { public static java.lang.Object getRecord(); }")
+            .addKeepRules("-keep class records.RecordLib$LibRecord")
+            .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
+            .addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
+            .compile()
+            .writeToZip();
+    R8FullTestBuilder builder =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(lib)
+            .addProgramClassFileData(PROGRAM_DATA_MAIN)
+            .setMinApi(parameters.getApiLevel())
+            .addKeepMainRule(MAIN_TYPE)
+            .addKeepRules("-keep class records.RecordLib$LibRecord")
+            .addKeepRules("-keep class records.RecordMain$MainRecord")
+            .addOptionsModification(TestingOptions::allowExperimentClassFileVersion);
+    if (parameters.isCfRuntime()) {
+      builder
+          .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
+          .compile()
+          .inspect(RecordTestUtils::assertRecordsAreRecords)
+          .run(parameters.getRuntime(), MAIN_TYPE)
+          .assertSuccessWithOutput(EXPECTED_RESULT);
+      return;
+    }
+    builder.run(parameters.getRuntime(), MAIN_TYPE).assertSuccessWithOutput(EXPECTED_RESULT);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordMergeTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordMergeTest.java
index 7af5f41..8401196 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordMergeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordMergeTest.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.InternalOptions.TestingOptions;
 import com.android.tools.r8.utils.StringUtils;
 import java.nio.file.Path;
@@ -33,12 +34,14 @@
       StringUtils.lines("Jane Doe", "42", "Jane Doe", "42");
 
   private final TestParameters parameters;
+  private final boolean intermediate;
 
-  public RecordMergeTest(TestParameters parameters) {
+  public RecordMergeTest(TestParameters parameters, boolean intermediate) {
     this.parameters = parameters;
+    this.intermediate = intermediate;
   }
 
-  @Parameterized.Parameters(name = "{0}")
+  @Parameterized.Parameters(name = "{0}, intermediate: {1}")
   public static List<Object[]> data() {
     // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk17).
     return buildParameters(
@@ -46,7 +49,8 @@
             .withCustomRuntime(CfRuntime.getCheckedInJdk17())
             .withDexRuntimes()
             .withAllApiLevelsAlsoForCf()
-            .build());
+            .build(),
+        BooleanUtils.values());
   }
 
   @Test
@@ -56,6 +60,7 @@
             .addProgramClassFileData(PROGRAM_DATA_1)
             .setMinApi(parameters.getApiLevel())
             .addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
+            .setIntermediate(intermediate)
             .compile()
             .writeToZip();
     Path output2 =
@@ -63,6 +68,7 @@
             .addProgramClassFileData(PROGRAM_DATA_2)
             .setMinApi(parameters.getApiLevel())
             .addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
+            .setIntermediate(intermediate)
             .compile()
             .writeToZip();
     D8TestCompileResult result =
@@ -82,6 +88,7 @@
             .addProgramClassFileData(PROGRAM_DATA_1)
             .setMinApi(parameters.getApiLevel())
             .addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
+            .setIntermediate(intermediate)
             .compile()
             .writeToZip();
     D8TestCompileResult result =
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordMethods.java b/src/test/java/com/android/tools/r8/desugar/records/RecordMethods.java
index a1fc48e..a3df3b5 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordMethods.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordMethods.java
@@ -11,13 +11,13 @@
 public class RecordMethods {
 
   public static String toString(
-      Object[] recordFieldsAsObjects, String simpleName, String fieldNames) {
+      Object[] recordFieldsValues, Class<?> recordClass, String fieldNames) {
     // Example: "Person[name=Jane Doe, age=42]"
     String[] fieldNamesSplit = fieldNames.isEmpty() ? new String[0] : fieldNames.split(";");
     StringBuilder builder = new StringBuilder();
-    builder.append(simpleName).append("[");
+    builder.append(recordClass.getSimpleName()).append("[");
     for (int i = 0; i < fieldNamesSplit.length; i++) {
-      builder.append(fieldNamesSplit[i]).append("=").append(recordFieldsAsObjects[i]);
+      builder.append(fieldNamesSplit[i]).append("=").append(recordFieldsValues[i]);
       if (i != fieldNamesSplit.length - 1) {
         builder.append(", ");
       }
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordReflectionTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordReflectionTest.java
index 26d259a..82891db 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordReflectionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordReflectionTest.java
@@ -4,8 +4,6 @@
 
 package com.android.tools.r8.desugar.records;
 
-import static com.android.tools.r8.desugar.records.RecordTestUtils.RECORD_KEEP_RULE_R8_CF_TO_CF;
-
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRuntime.CfRuntime;
@@ -61,7 +59,8 @@
         .addProgramClassFileData(PROGRAM_DATA)
         .setMinApi(parameters.getApiLevel())
         .addKeepMainRule(MAIN_TYPE)
-        .addKeepRules(RECORD_KEEP_RULE_R8_CF_TO_CF)
+        .addKeepRules("-keepattributes *")
+        .addKeepRules("-keep class * extends java.lang.Record { private final <fields>; }")
         .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
         .addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
         .compile()
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordShrinkFieldTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordShrinkFieldTest.java
new file mode 100644
index 0000000..d46e947
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordShrinkFieldTest.java
@@ -0,0 +1,97 @@
+// Copyright (c) 2021, 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.desugar.records;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.InternalOptions.TestingOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RecordShrinkFieldTest extends TestBase {
+
+  private static final String RECORD_NAME = "RecordShrinkField";
+  private static final byte[][] PROGRAM_DATA = RecordTestUtils.getProgramData(RECORD_NAME);
+  private static final String MAIN_TYPE = RecordTestUtils.getMainType(RECORD_NAME);
+  private static final String EXPECTED_RESULT =
+      StringUtils.lines("%s[name=Jane Doe, age=42, unused=-1]", "%s[name=Bob, age=42, unused=-1]");
+  private static final String EXPECTED_RESULT_D8 =
+      String.format(EXPECTED_RESULT, "Person", "Person");
+  private static final String EXPECTED_RESULT_R8 = String.format(EXPECTED_RESULT, "a", "a");
+
+  private final TestParameters parameters;
+
+  public RecordShrinkFieldTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Parameterized.Parameters(name = "{0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withDexRuntimes().withAllApiLevelsAlsoForCf().build());
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    testForD8(parameters.getBackend())
+        .addProgramClassFileData(PROGRAM_DATA)
+        .setMinApi(parameters.getApiLevel())
+        .addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
+        .compile()
+        .run(parameters.getRuntime(), MAIN_TYPE)
+        .assertSuccessWithOutput(EXPECTED_RESULT_D8);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClassFileData(PROGRAM_DATA)
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(MAIN_TYPE)
+        .addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
+        .compile()
+        .inspect(this::assertSingleField)
+        .run(parameters.getRuntime(), MAIN_TYPE)
+        .assertSuccessWithOutput(EXPECTED_RESULT_R8);
+  }
+
+  @Test
+  public void testR8CfThenDex() throws Exception {
+    Path desugared =
+        testForR8(Backend.CF)
+            .addProgramClassFileData(PROGRAM_DATA)
+            .setMinApi(parameters.getApiLevel())
+            .addKeepMainRule(MAIN_TYPE)
+            .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
+            .addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
+            .compile()
+            .writeToZip();
+    testForR8(parameters.getBackend())
+        .addProgramFiles(desugared)
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(MAIN_TYPE)
+        .addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
+        .compile()
+        .inspect(this::assertSingleField)
+        .run(parameters.getRuntime(), MAIN_TYPE)
+        .assertSuccessWithOutput(EXPECTED_RESULT_R8);
+  }
+
+  private void assertSingleField(CodeInspector inspector) {
+    ClassSubject recordClass = inspector.clazz("records.a");
+    assertEquals(1, recordClass.allInstanceFields().size());
+    assertEquals(
+        "java.lang.String", recordClass.allInstanceFields().get(0).getField().type().toString());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordTestUtils.java b/src/test/java/com/android/tools/r8/desugar/records/RecordTestUtils.java
index 3f8adcc..7008bfe 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordTestUtils.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordTestUtils.java
@@ -40,12 +40,6 @@
     return Paths.get(ToolHelper.TESTS_BUILD_DIR, EXAMPLE_FOLDER, RECORD_FOLDER + ".jar");
   }
 
-  // TODO(b/197081367): Rediscuss if we want to maintain the toString() method.
-  // In R8 Cf to Cf we need to maintain the record component attributes, the class name and fields
-  // so the mapping between the record components and fields is valid and the toString() prints
-  // the record as specified (including the class name).
-  public static final String RECORD_KEEP_RULE_R8_CF_TO_CF =
-      "-keepattributes *\n" + "-keep class * extends java.lang.Record { private final <fields>; }";
 
   public static Path[] getJdk15LibraryFiles(TemporaryFolder temp) throws IOException {
     Assume.assumeFalse(ToolHelper.isWindows());
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordWithMembersTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordWithMembersTest.java
index f1fc5ae..f22f8cd 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordWithMembersTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordWithMembersTest.java
@@ -4,8 +4,6 @@
 
 package com.android.tools.r8.desugar.records;
 
-import static com.android.tools.r8.desugar.records.RecordTestUtils.RECORD_KEEP_RULE_R8_CF_TO_CF;
-
 import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -71,7 +69,6 @@
             .addOptionsModification(TestingOptions::allowExperimentClassFileVersion);
     if (parameters.isCfRuntime()) {
       builder
-          .addKeepRules(RECORD_KEEP_RULE_R8_CF_TO_CF)
           .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
           .compile()
           .inspect(RecordTestUtils::assertRecordsAreRecords)
diff --git a/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java b/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java
index 68ad95e..5e7aa9a 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.desugar.records;
 
-import static com.android.tools.r8.desugar.records.RecordTestUtils.RECORD_KEEP_RULE_R8_CF_TO_CF;
 
 import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.TestBase;
@@ -12,7 +11,9 @@
 import com.android.tools.r8.TestRuntime.CfRuntime;
 import com.android.tools.r8.utils.InternalOptions.TestingOptions;
 import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Path;
 import java.util.List;
+import org.junit.Assume;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -64,6 +65,25 @@
   }
 
   @Test
+  public void testD8Intermediate() throws Exception {
+    Assume.assumeTrue(parameters.isDexRuntime());
+    Path path =
+        testForD8(Backend.DEX)
+            .addProgramClassFileData(PROGRAM_DATA)
+            .setMinApi(parameters.getApiLevel())
+            .addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
+            .setIntermediate(true)
+            .compile()
+            .writeToZip();
+    testForD8()
+        .addProgramFiles(path)
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .run(parameters.getRuntime(), MAIN_TYPE)
+        .assertSuccessWithOutput(EXPECTED_RESULT);
+  }
+
+  @Test
   public void testR8() throws Exception {
     R8FullTestBuilder builder =
         testForR8(parameters.getBackend())
@@ -73,7 +93,6 @@
             .addOptionsModification(TestingOptions::allowExperimentClassFileVersion);
     if (parameters.isCfRuntime()) {
       builder
-          .addKeepRules(RECORD_KEEP_RULE_R8_CF_TO_CF)
           .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
           .compile()
           .inspect(RecordTestUtils::assertRecordsAreRecords)
@@ -101,7 +120,6 @@
             .addOptionsModification(TestingOptions::allowExperimentClassFileVersion);
     if (parameters.isCfRuntime()) {
       builder
-          .addKeepRules(RECORD_KEEP_RULE_R8_CF_TO_CF)
           .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
           .compile()
           .inspect(RecordTestUtils::assertRecordsAreRecords)
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 2d89396..de13512 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
@@ -66,7 +66,7 @@
     testForR8(parameters.getBackend())
         .addProgramFiles(
             jars.getForConfiguration(kotlinParameters),
-            ToolHelper.getKotlinStdlibJar(kotlinParameters.getCompiler()))
+            kotlinParameters.getCompiler().getKotlinStdlibJar())
         .addKeepMainRule(PKG + ".MainKt")
         .addKeepRules(enumKeepRules.getKeepRules())
         .addKeepRuntimeVisibleAnnotations()
diff --git a/src/test/java/com/android/tools/r8/graph/DexTypeTest.java b/src/test/java/com/android/tools/r8/graph/DexTypeTest.java
index 83164c5..ab7fea5 100644
--- a/src/test/java/com/android/tools/r8/graph/DexTypeTest.java
+++ b/src/test/java/com/android/tools/r8/graph/DexTypeTest.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72;
 import static org.hamcrest.CoreMatchers.hasItems;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -30,7 +31,7 @@
         new ApplicationReader(
                 AndroidApp.builder()
                     .addLibraryFiles(ToolHelper.getDefaultAndroidJar())
-                    .addLibraryFiles(ToolHelper.getKotlinStdlibJar(ToolHelper.getKotlinC_1_3_72()))
+                    .addLibraryFiles(KOTLINC_1_3_72.getCompiler().getKotlinStdlibJar())
                     .build(),
                 options,
                 Timing.empty())
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureKeepAttributesTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureKeepAttributesTest.java
index fe4a189..48877b9 100644
--- a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureKeepAttributesTest.java
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureKeepAttributesTest.java
@@ -8,6 +8,7 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 
+import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.graph.genericsignature.GenericSignatureKeepAttributesTest.Outer.Middle;
@@ -72,6 +73,7 @@
         .addKeepAttributeInnerClassesAndEnclosingMethod()
         .addKeepMainRule(Main.class)
         .addKeepClassAndMembersRules(Outer.Middle.Inner.class, Supplier.class, Predicate.class)
+        .enableInliningAnnotations()
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines(parameters.isCfRuntime() ? EXPECTED_JVM : EXPECTED_DEX)
         .inspect(this::inspectSignatures);
@@ -146,6 +148,7 @@
       return new Outer<O>.Middle<>();
     }
 
+    @NeverInline
     public static Outer<?>.Middle<?>.Inner<Object> create() {
       return new Outer<>().createMiddle().createInner();
     }
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureReflectiveInnerTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureReflectiveInnerTest.java
index e34a36c..3c32c7b 100644
--- a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureReflectiveInnerTest.java
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureReflectiveInnerTest.java
@@ -7,6 +7,7 @@
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 
+import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
@@ -48,6 +49,7 @@
         .addKeepAttributeInnerClassesAndEnclosingMethod()
         .addKeepAttributeSignature()
         .addKeepClassRules(Foo.Bar.class)
+        .enableInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines(EXPECTED)
@@ -71,6 +73,7 @@
       }
     }
 
+    @NeverInline
     public Bar<String> foo() {
       return new Bar<>();
     }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/SubsumedCatchHandlerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/SubsumedCatchHandlerTest.java
index 19d7226..3d3db4a 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/SubsumedCatchHandlerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/SubsumedCatchHandlerTest.java
@@ -3,12 +3,12 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.optimize;
 
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.core.IsNot.not;
 import static org.junit.Assert.assertEquals;
 
-import com.android.tools.r8.ForceInline;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
@@ -42,7 +42,6 @@
       System.out.print(" -> " + exitCode);
     }
 
-    @ForceInline
     private static int foo() {
       try {
         bar();
@@ -86,7 +85,6 @@
         testForR8(backend)
             .addInnerClasses(SubsumedCatchHandlerTest.class)
             .addKeepMainRule(TestClass.class)
-            .enableForceInliningAnnotations()
             .enableInliningAnnotations()
             .run(TestClass.class)
             .assertSuccessWithOutput(expected)
@@ -118,5 +116,7 @@
       DexType guard = handler.guards.get(0);
       assertEquals("java.lang.Exception", guard.toSourceString());
     }
+
+    assertThat(classSubject.uniqueMethodWithName("foo"), isAbsent());
   }
 }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/nonpublicsubtype/ClassInlineNonPublicSubtypeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/nonpublicsubtype/ClassInlineNonPublicSubtypeTest.java
new file mode 100644
index 0000000..4292720
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/nonpublicsubtype/ClassInlineNonPublicSubtypeTest.java
@@ -0,0 +1,75 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.classinliner.nonpublicsubtype;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ir.optimize.classinliner.nonpublicsubtype.subpkg.Utils;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ClassInlineNonPublicSubtypeTest extends TestBase {
+
+  static final String EXPECTED = StringUtils.lines("Hello world");
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public ClassInlineNonPublicSubtypeTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testReference() throws Exception {
+    testForRuntime(parameters)
+        .addInnerClasses(getClass())
+        .addProgramClassesAndInnerClasses(Utils.class)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .enableInliningAnnotations()
+        .addInnerClasses(getClass())
+        .addProgramClassesAndInnerClasses(Utils.class)
+        .addKeepMainRule(TestClass.class)
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  public interface I {
+    @NeverInline
+    void method();
+  }
+
+  static class Accessor {
+    @NeverInline
+    static void access(I i) {
+      i.method();
+    }
+  }
+
+  static class TestClass {
+
+    static void run() {
+      Accessor.access(Utils.INSTANCE);
+    }
+
+    public static void main(String[] args) {
+      run();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/nonpublicsubtype/subpkg/Utils.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/nonpublicsubtype/subpkg/Utils.java
new file mode 100644
index 0000000..18c9946
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/nonpublicsubtype/subpkg/Utils.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.classinliner.nonpublicsubtype.subpkg;
+
+import com.android.tools.r8.ir.optimize.classinliner.nonpublicsubtype.ClassInlineNonPublicSubtypeTest.I;
+
+public class Utils {
+
+  // Non-public class can't be accessed by the context calling 'method'.
+  // TODO(b/120061431): Class inlining should be able to inline this as the instance itself will be
+  //  eliminated, but until then, it must consistently refuse to inline in case of invalid access.
+  public static final I INSTANCE =
+      new I() {
+        @Override
+        public void method() {
+          System.out.println("Hello world");
+        }
+      };
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineLibraryInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineLibraryInterfaceMethodTest.java
index 48d3203..3c76052 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineLibraryInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineLibraryInterfaceMethodTest.java
@@ -58,11 +58,9 @@
               counts.println = countInvokesWithName(methodSubject, "println");
             });
 
-    // TODO(b/129044633) We expect 0 x run and 2 x println after we can inline those methods.
-    assertEquals(2, counts.run);
-    assertEquals(0, counts.println);
-    // TODO(b/126323172) After this issue has been fixed, add test here to check the same with
-    // desugared lambdas.
+    // TODO(b/126323172) Add test here to check the same with desugared lambdas.
+    assertEquals(0, counts.run);
+    assertEquals(2, counts.println);
   }
 
   private static long countInvokesWithName(MethodSubject methodSubject, String name) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlinerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlinerTest.java
index 74b2a56..db81629 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlinerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlinerTest.java
@@ -4,11 +4,13 @@
 
 package com.android.tools.r8.ir.optimize.inliner;
 
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.fail;
 
-import com.android.tools.r8.R8Command;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.ProcessResult;
@@ -20,16 +22,12 @@
 import com.android.tools.r8.ir.optimize.inliner.interfaces.InterfaceTargetsTestClass.IfaceD;
 import com.android.tools.r8.ir.optimize.inliner.interfaces.InterfaceTargetsTestClass.IfaceNoImpl;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
-import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalOptions;
 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 com.google.common.collect.ImmutableList;
 import java.nio.file.Path;
-import java.util.Collections;
-import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import org.junit.Test;
@@ -52,31 +50,33 @@
 
   @Test
   public void testExceptionHandling() throws Exception {
-    String className = ExceptionHandlingTestClass.class.getName();
-    AndroidApp inputApp = readClasses(ExceptionHandlingTestClass.class);
-    List<String> proguardConfig =
-        ImmutableList.of(
-            "-keep public class " + className + "{",
-            "  public static void main(...);",
-            "}",
-            "-forceinline public class " + className + "{",
-            "  private static void inlinee*(...);",
-            "}",
-            "-neverinline public class " + className + "{",
-            "  private static void *Test(...);",
-            "}");
-    R8Command.Builder commandBuilder =
-        ToolHelper.prepareR8CommandBuilder(inputApp, emptyConsumer(backend))
-            .addProguardConfiguration(proguardConfig, Origin.unknown())
-            .addLibraryFiles(runtimeJar(backend));
-    ToolHelper.allowTestProguardOptions(commandBuilder);
-    AndroidApp outputApp = ToolHelper.runR8(commandBuilder.build(), this::configure);
-    assert backend == Backend.DEX || backend == Backend.CF;
-    assertEquals(
-        runOnJava(ExceptionHandlingTestClass.class),
-        backend == Backend.DEX
-            ? runOnArt(outputApp, className)
-            : runOnJava(outputApp, className, Collections.emptyList()));
+    testForR8(backend)
+        .addProgramClasses(ExceptionHandlingTestClass.class)
+        .addKeepMainRule(ExceptionHandlingTestClass.class)
+        .addOptionsModification(this::configure)
+        .enableInliningAnnotations()
+        .compile()
+        .inspect(
+            inspector -> {
+              ClassSubject mainClassSubject = inspector.clazz(ExceptionHandlingTestClass.class);
+              assertThat(mainClassSubject, isPresent());
+              assertThat(
+                  mainClassSubject.uniqueMethodWithName("inlineeWithNormalExitThatDoesNotThrow"),
+                  isAbsent());
+              assertThat(
+                  mainClassSubject.uniqueMethodWithName("inlineeWithNormalExitThatThrows"),
+                  isAbsent());
+              assertThat(
+                  mainClassSubject.uniqueMethodWithName("inlineeWithoutNormalExit"), isAbsent());
+            })
+        .run(ExceptionHandlingTestClass.class)
+        .assertSuccessWithOutputLines(
+            "Test succeeded: methodWithoutCatchHandlersTest(1)",
+            "Test succeeded: methodWithoutCatchHandlersTest(2)",
+            "Test succeeded: methodWithoutCatchHandlersTest(3)",
+            "Test succeeded: methodWithCatchHandlersTest(1)",
+            "Test succeeded: methodWithCatchHandlersTest(2)",
+            "Test succeeded: methodWithCatchHandlersTest(3)");
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningAfterClassInitializationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningAfterClassInitializationTest.java
index f9081c2..59e5118 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningAfterClassInitializationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningAfterClassInitializationTest.java
@@ -10,6 +10,7 @@
 import static org.hamcrest.core.IsNot.not;
 
 import com.android.tools.r8.KeepConstantArguments;
+import com.android.tools.r8.KeepUnusedArguments;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -224,6 +225,7 @@
         .addOptionsModification(
             options -> options.enableInliningOfInvokesWithClassInitializationSideEffects = false)
         .enableConstantArgumentAnnotations()
+        .enableUnusedArgumentAnnotations()
         .enableInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), mainClass)
@@ -322,6 +324,7 @@
     }
 
     @KeepConstantArguments
+    @KeepUnusedArguments
     @NeverInline
     private static void test(A obj) {
       try {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningIntoVisibilityBridgeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningIntoVisibilityBridgeTest.java
index 75679e5..dc74e0f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningIntoVisibilityBridgeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningIntoVisibilityBridgeTest.java
@@ -54,10 +54,8 @@
             .addInnerClasses(InliningIntoVisibilityBridgeTest.class)
             .addInnerClasses(InliningIntoVisibilityBridgeTestClasses.class)
             .addKeepMainRule(TestClass.class)
-            .addForceInliningAnnotations()
             .addInliningAnnotations()
             .applyIf(neverInline, R8TestBuilder::enableInliningAnnotations)
-            .applyIf(!neverInline, R8TestBuilder::enableForceInliningAnnotations)
             .enableNoVerticalClassMergingAnnotations()
             .enableProguardTestOptions()
             .setMinApi(parameters.getApiLevel())
@@ -93,13 +91,11 @@
   static class TestClass {
 
     public static void main(String[] args) {
-      InliningIntoVisibilityBridgeTestClassC obj = new InliningIntoVisibilityBridgeTestClassC();
-
       // Invoke method three times to prevent the synthetic bridge on InliningIntoVisibilityBridge-
       // TestClassB from being inlined.
-      obj.method();
-      obj.method();
-      obj.method();
+      InliningIntoVisibilityBridgeTestClassC.method();
+      InliningIntoVisibilityBridgeTestClassC.method();
+      InliningIntoVisibilityBridgeTestClassC.method();
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/exceptionhandling/ExceptionHandlingTestClass.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/exceptionhandling/ExceptionHandlingTestClass.java
index cda0856..a23b775 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/exceptionhandling/ExceptionHandlingTestClass.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/exceptionhandling/ExceptionHandlingTestClass.java
@@ -4,11 +4,12 @@
 
 package com.android.tools.r8.ir.optimize.inliner.exceptionhandling;
 
+import com.android.tools.r8.NeverInline;
+
 public class ExceptionHandlingTestClass {
 
   private static boolean FALSE;
 
-  // -keep
   public static void main(String[] args) {
     FALSE = args == null;
     try {
@@ -33,7 +34,7 @@
     methodWithCatchHandlersTest();
   }
 
-  // -neverinline
+  @NeverInline
   private static void methodWithoutCatchHandlersTest(int i) {
     switch (i) {
       case 1:
@@ -50,7 +51,7 @@
     }
   }
 
-  // -neverinline
+  @NeverInline
   private static void methodWithCatchHandlersTest() {
     try {
       inlineeWithNormalExitThatDoesNotThrow();
@@ -70,21 +71,18 @@
     }
   }
 
-  // -forceinline
   private static void inlineeWithNormalExitThatDoesNotThrow() {
     if (FALSE) {
       throw new RuntimeException();
     }
   }
 
-  // -forceinline
   private static void inlineeWithNormalExitThatThrows() {
     if (!FALSE) {
       throw new RuntimeException();
     }
   }
 
-  // -forceinline
   private static void inlineeWithoutNormalExit() {
     throw new RuntimeException();
   }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/testclasses/InliningIntoVisibilityBridgeTestClasses.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/testclasses/InliningIntoVisibilityBridgeTestClasses.java
index 54018ee..e856133 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/testclasses/InliningIntoVisibilityBridgeTestClasses.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/testclasses/InliningIntoVisibilityBridgeTestClasses.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.ir.optimize.inliner.testclasses;
 
-import com.android.tools.r8.ForceInline;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.NoVerticalClassMerging;
 
@@ -17,7 +16,6 @@
   @NoVerticalClassMerging
   static class InliningIntoVisibilityBridgeTestClassA {
 
-    @ForceInline
     @NeverInline
     public static void method() {
       System.out.println("Hello world");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
index 000f5d4..3f0c8d6 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
@@ -4,16 +4,15 @@
 package com.android.tools.r8.ir.optimize.reflection;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.onlyIf;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.ForceInline;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.NoHorizontalClassMerging;
-import com.android.tools.r8.R8TestBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ListUtils;
@@ -23,7 +22,6 @@
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import com.google.common.collect.ImmutableList;
 import java.util.List;
-import java.util.concurrent.Callable;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -39,17 +37,15 @@
   static class EffectivelyFinal {}
 
   @NoHorizontalClassMerging
-  static class Reflection implements Callable<Class<?>> {
+  static class Reflection {
 
-    @ForceInline
-    @Override
     public Class<?> call() {
       return getClass();
     }
   }
 
   @NoHorizontalClassMerging
-  static class GetClassTestMain implements Callable<Class<?>> {
+  static class GetClassTestMain {
 
     @NeverInline
     static Class<?> getMainClass(GetClassTestMain instance) {
@@ -58,7 +54,6 @@
     }
 
     @NeverInline
-    @Override
     public Class<?> call() {
       // Non-null `this` pointer.
       return getClass();
@@ -180,6 +175,11 @@
     assertEquals(expectedGetClassCount, countGetClass(mainMethod));
     assertEquals(expectedConstClassCount, countConstClass(mainMethod));
 
+    ClassSubject reflectionClass = codeInspector.clazz(Reflection.class);
+    assertThat(reflectionClass, isPresent());
+    assertThat(
+        reflectionClass.uniqueMethodWithName("call"), onlyIf(expectCallPresent, isPresent()));
+
     ClassSubject getterClass = codeInspector.clazz(GetClassTestMain.class);
     MethodSubject getMainClass = getterClass.uniqueMethodWithName("getMainClass");
     assertThat(getMainClass, isPresent());
@@ -219,8 +219,6 @@
     testForR8(parameters.getBackend())
         .setMode(mode)
         .addInnerClasses(GetClassTest.class)
-        .addForceInliningAnnotations()
-        .applyIf(mode == CompilationMode.RELEASE, R8TestBuilder::enableForceInliningAnnotations)
         .enableInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
         .addKeepMainRule(MAIN)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameTest.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameTest.java
index f5d0eb9..9b3b394 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameTest.java
@@ -4,12 +4,14 @@
 package com.android.tools.r8.ir.optimize.reflection;
 
 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;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.D8TestRunResult;
-import com.android.tools.r8.ForceInline;
 import com.android.tools.r8.R8TestRunResult;
 import com.android.tools.r8.SingleTestRunResult;
 import com.android.tools.r8.TestParameters;
@@ -17,11 +19,11 @@
 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.FoundMethodSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import com.google.common.collect.ImmutableList;
 import java.nio.file.Path;
 import java.util.Collection;
-import org.junit.Ignore;
 import org.junit.Test;
 
 class GetName0Class {
@@ -32,7 +34,6 @@
     void foo();
   }
 
-  @ForceInline
   static Itf createRunnable() {
     return new Itf() {
       @Override
@@ -42,7 +43,6 @@
     };
   }
 
-  @ForceInline
   static GetName0Class factory() {
     return new GetName0Class() {
       @Override
@@ -212,7 +212,6 @@
     builder.addAll(ToolHelper.getClassFilesForTestDirectory(
         ToolHelper.getPackageDirectoryForTestPackage(MAIN.getPackage()),
         path -> path.getFileName().toString().startsWith("GetName0")));
-    builder.add(ToolHelper.getClassFileForTestClass(ForceInline.class));
     classPaths = builder.build();
   }
 
@@ -263,7 +262,6 @@
   }
 
   @Test
-  @Ignore("b/154813140: Invalidly assumes that getClass on kept classes can be optimized")
   public void testR8_pinning() throws Exception {
     // Pinning the test class.
     R8TestRunResult result =
@@ -276,13 +274,21 @@
             .minification(enableMinification)
             .setMinApi(parameters.getApiLevel())
             .addOptionsModification(this::configure)
+            .compile()
+            .inspect(
+                inspector -> {
+                  ClassSubject getName0ClassSubject = inspector.clazz(GetName0Class.class);
+                  assertThat(getName0ClassSubject, isPresent());
+                  assertTrue(
+                      getName0ClassSubject.allMethods(FoundMethodSubject::isStatic).isEmpty());
+                })
             .run(parameters.getRuntime(), MAIN)
-            .assertSuccessWithOutput(JAVA_OUTPUT);
-    test(result, 2);
+            // TODO(b/154813140): Invalidly assumes that getClass on kept classes can be optimized.
+            .assertSuccessWithOutputThatMatches(not(equalTo(JAVA_OUTPUT)));
+    test(result, 8);
   }
 
   @Test
-  @Ignore("b/154813140: Invalidly assumes that getClass on kept classes can be optimized")
   public void testR8_shallow_pinning() throws Exception {
     // Shallow pinning the test class.
     R8TestRunResult result =
@@ -295,13 +301,21 @@
             .minification(enableMinification)
             .setMinApi(parameters.getApiLevel())
             .addOptionsModification(this::configure)
+            .compile()
+            .inspect(
+                inspector -> {
+                  ClassSubject getName0ClassSubject = inspector.clazz(GetName0Class.class);
+                  assertThat(getName0ClassSubject, isPresent());
+                  assertTrue(
+                      getName0ClassSubject.allMethods(FoundMethodSubject::isStatic).isEmpty());
+                })
             .run(parameters.getRuntime(), MAIN);
+    // TODO(b/154813140): Invalidly assumes that getClass on kept classes can be optimized.
     if (enableMinification) {
-      result.assertSuccessWithOutput(RENAMED_OUTPUT);
+      result.assertSuccessWithOutputThatMatches(not(equalTo(RENAMED_OUTPUT)));
     } else {
-      result.assertSuccessWithOutput(JAVA_OUTPUT);
+      result.assertSuccessWithOutputThatMatches(not(equalTo(JAVA_OUTPUT)));
     }
-    test(result, 2);
+    test(result, 8);
   }
-
 }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetSimpleNameTest.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetSimpleNameTest.java
index 102d5c7..a6d3931 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetSimpleNameTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetSimpleNameTest.java
@@ -3,17 +3,18 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.optimize.reflection;
 
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.D8TestRunResult;
-import com.android.tools.r8.ForceInline;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.SingleTestRunResult;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ir.optimize.reflection.Outer.TestHelper;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -100,7 +101,6 @@
       this.inner = inner;
     }
 
-    @ForceInline
     String getClassName() {
       return inner.getClass().getSimpleName();
     }
@@ -159,12 +159,18 @@
         .assertSuccessWithOutput(JVM_OUTPUT);
   }
 
-  private void test(SingleTestRunResult<?> result) throws Exception {
+  private void test(SingleTestRunResult<?> result, boolean isOptimizing) throws Exception {
     CodeInspector codeInspector = result.inspector();
     ClassSubject mainClass = codeInspector.clazz(MAIN);
     MethodSubject mainMethod = mainClass.mainMethod();
     assertThat(mainMethod, isPresent());
     assertEquals(0, countGetName(mainMethod));
+
+    if (isOptimizing) {
+      ClassSubject testHelperClassSubject = codeInspector.clazz(TestHelper.class);
+      assertThat(testHelperClassSubject, isPresent());
+      assertThat(testHelperClassSubject.uniqueMethodWithName("getClassName"), isAbsent());
+    }
   }
 
   @Test
@@ -179,7 +185,7 @@
             .addOptionsModification(this::configure)
             .run(parameters.getRuntime(), MAIN)
             .assertSuccessWithOutput(JVM_OUTPUT);
-    test(result);
+    test(result, false);
 
     result =
         testForD8()
@@ -189,7 +195,7 @@
             .addOptionsModification(this::configure)
             .run(parameters.getRuntime(), MAIN)
             .assertSuccessWithOutput(JVM_OUTPUT);
-    test(result);
+    test(result, false);
   }
 
   @Test
@@ -197,7 +203,6 @@
     // Pinning the test class.
     testForR8(parameters.getBackend())
         .addProgramFiles(classPaths)
-        .addForceInliningAnnotations()
         .enableInliningAnnotations()
         .addKeepAllClassesRule()
         .addKeepAttributeInnerClassesAndEnclosingMethod()
@@ -206,7 +211,7 @@
         .addOptionsModification(this::configure)
         .run(parameters.getRuntime(), MAIN)
         .assertSuccessWithOutput(JVM_OUTPUT)
-        .apply(this::test);
+        .apply(result -> test(result, false));
   }
 
   @Test
@@ -214,7 +219,6 @@
     // Pinning the test class.
     testForR8(parameters.getBackend())
         .addProgramFiles(classPaths)
-        .enableForceInliningAnnotations()
         .enableInliningAnnotations()
         .addKeepMainRule(MAIN)
         .addKeepRules("-keep class **.ClassGetSimpleName*")
@@ -225,7 +229,7 @@
         .addOptionsModification(this::configure)
         .run(parameters.getRuntime(), MAIN)
         .assertSuccessWithOutput(OUTPUT_NO_ATTRIBUTES)
-        .apply(this::test);
+        .apply(result -> test(result, true));
   }
 
   @Test
@@ -233,7 +237,6 @@
     // Shallow pinning the test class.
     testForR8(parameters.getBackend())
         .addProgramFiles(classPaths)
-        .enableForceInliningAnnotations()
         .enableInliningAnnotations()
         .addKeepMainRule(MAIN)
         .addKeepRules("-keep,allowobfuscation class **.ClassGetSimpleName*")
@@ -249,6 +252,6 @@
         .applyIf(enableMinification, result -> result.assertSuccessWithOutput(RENAMED_OUTPUT))
         .applyIf(
             !enableMinification, result -> result.assertSuccessWithOutput(OUTPUT_NO_ATTRIBUTES))
-        .apply(this::test);
+        .apply(result -> test(result, true));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
index a1b6f08..c3f71e3 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
@@ -164,16 +164,19 @@
     assertTrue(instanceMethods(simpleWithPhi).isEmpty());
     assertThat(simpleWithPhi.clinit(), not(isPresent()));
 
+    // TODO(b/200498092): SimpleWithParams should be staticized, but due to reprocessing the
+    //  instantiation of SimpleWithParams, it is marked as ineligible for staticizing.
     assertEquals(
         Lists.newArrayList(
-            "STATIC: String SimpleWithParams.bar(String)",
-            "STATIC: String SimpleWithParams.foo()",
-            "STATIC: String TrivialTestClass.next()"),
+            "STATIC: String TrivialTestClass.next()",
+            "SimpleWithParams SimpleWithParams.INSTANCE",
+            "VIRTUAL: String SimpleWithParams.bar(String)",
+            "VIRTUAL: String SimpleWithParams.foo()"),
         references(clazz, "testSimpleWithParams", "void"));
 
     ClassSubject simpleWithParams = inspector.clazz(SimpleWithParams.class);
-    assertTrue(instanceMethods(simpleWithParams).isEmpty());
-    assertThat(simpleWithParams.clinit(), not(isPresent()));
+    assertFalse(instanceMethods(simpleWithParams).isEmpty());
+    assertThat(simpleWithParams.clinit(), isPresent());
 
     assertEquals(
         Lists.newArrayList(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/NestedStringBuilderTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/NestedStringBuilderTest.java
index 38ddc76..65d51c3 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/NestedStringBuilderTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/NestedStringBuilderTest.java
@@ -6,7 +6,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assume.assumeTrue;
 
-import com.android.tools.r8.ForceInline;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
@@ -39,20 +38,21 @@
 
     testForR8(parameters.getBackend())
         .addProgramClasses(MAIN)
-        .enableForceInliningAnnotations()
         .addKeepMainRule(MAIN)
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), MAIN.getTypeName(), "$")
         .assertSuccessWithOutput(EXPECTED)
-        .inspect(codeInspector -> {
-          ClassSubject mainClass = codeInspector.clazz(MAIN);
-          MethodSubject main = mainClass.mainMethod();
-          assertEquals(
-              // TODO(b/113859361): should be 1 after merging StringBuilder's
-              2,
-              main.streamInstructions().filter(
-                  i -> i.isNewInstance(StringBuilder.class.getTypeName())).count());
-          });
+        .inspect(
+            codeInspector -> {
+              ClassSubject mainClass = codeInspector.clazz(MAIN);
+              MethodSubject main = mainClass.uniqueMethod();
+              assertEquals(
+                  // TODO(b/113859361): should be 1 after merging StringBuilder's
+                  2,
+                  main.streamInstructions()
+                      .filter(i -> i.isNewInstance(StringBuilder.class.getTypeName()))
+                      .count());
+            });
   }
 
   static class NestedStringBuilders {
@@ -61,7 +61,6 @@
       System.out.println(concat("one", args[0]) + "two");
     }
 
-    @ForceInline
     public static String concat(String one, String two) {
       return one + two;
     }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringLengthTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringLengthTest.java
index 7924a77..b2927fb 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringLengthTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringLengthTest.java
@@ -9,7 +9,6 @@
 import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.D8TestRunResult;
-import com.android.tools.r8.ForceInline;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.R8TestRunResult;
 import com.android.tools.r8.SingleTestRunResult;
@@ -96,7 +95,6 @@
     R8TestRunResult result =
         testForR8(parameters.getBackend())
             .addProgramClasses(MAIN)
-            .enableForceInliningAnnotations()
             .enableInliningAnnotations()
             .addKeepMainRule(MAIN)
             .setMinApi(parameters.getApiLevel())
@@ -107,7 +105,6 @@
 
   public static class TestClass {
 
-    @ForceInline
     static String simpleInlineable() {
       return "Shared";
     }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
index 01ac9dc..3861a06 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
@@ -3,12 +3,12 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.optimize.string;
 
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assume.assumeTrue;
 
-import com.android.tools.r8.ForceInline;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.NeverPropagateValue;
 import com.android.tools.r8.SingleTestRunResult;
@@ -45,7 +45,7 @@
 
   @Parameterized.Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimes().build();
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
   private final TestParameters parameters;
@@ -111,13 +111,19 @@
     SingleTestRunResult<?> result =
         testForR8(parameters.getBackend())
             .addProgramClassesAndInnerClasses(MAIN)
-            .enableForceInliningAnnotations()
             .enableInliningAnnotations()
             .enableMemberValuePropagationAnnotations()
             .addKeepMainRule(MAIN)
-            .setMinApi(parameters.getRuntime())
+            .setMinApi(parameters.getApiLevel())
             .noMinification()
             .addOptionsModification(this::configure)
+            .compile()
+            .inspect(
+                inspector -> {
+                  ClassSubject fooClassSubject = inspector.clazz(Foo.class);
+                  assertThat(fooClassSubject, isPresent());
+                  assertThat(fooClassSubject.uniqueMethodWithName("getter"), isAbsent());
+                })
             .run(parameters.getRuntime(), MAIN)
             .assertSuccessWithOutput(JAVA_OUTPUT);
     test(result, true, true);
@@ -155,7 +161,6 @@
     }
 
     static class Foo implements Itf {
-      @ForceInline
       @Override
       public String getter() {
         return String.valueOf(getClass().getName());
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/InvokeMethodWithReceiverOptimizationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/InvokeMethodWithReceiverOptimizationTest.java
index e7e09dd..06aae25 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/InvokeMethodWithReceiverOptimizationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/InvokeMethodWithReceiverOptimizationTest.java
@@ -12,7 +12,7 @@
 
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestBase;
-import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -33,17 +33,19 @@
 @RunWith(Parameterized.class)
 public class InvokeMethodWithReceiverOptimizationTest extends TestBase {
 
-  private final Backend backend;
-  private final boolean enableArgumentRemoval;
+  private final TestParameters parameters;
+  private final boolean enableArgumentPropagation;
 
-  @Parameters(name = "Backend: {0}, enable argument removal: {1}")
+  @Parameters(name = "{0}, argument propagation: {1}")
   public static List<Object[]> data() {
-    return buildParameters(ToolHelper.getBackends(), BooleanUtils.values());
+    return buildParameters(
+        getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
   }
 
-  public InvokeMethodWithReceiverOptimizationTest(Backend backend, boolean enableArgumentRemoval) {
-    this.backend = backend;
-    this.enableArgumentRemoval = enableArgumentRemoval;
+  public InvokeMethodWithReceiverOptimizationTest(
+      TestParameters parameters, boolean enableArgumentPropagation) {
+    this.parameters = parameters;
+    this.enableArgumentPropagation = enableArgumentPropagation;
   }
 
   @Test
@@ -57,15 +59,20 @@
     testForJvm().addTestClasspath().run(TestClass.class).assertSuccessWithOutput(expected);
 
     CodeInspector inspector =
-        testForR8(backend)
+        testForR8(parameters.getBackend())
             .addInnerClasses(InvokeMethodWithReceiverOptimizationTest.class)
             .addKeepMainRule(TestClass.class)
             .enableInliningAnnotations()
-            .addOptionsModification(o -> o.enableArgumentRemoval = enableArgumentRemoval)
+            .addOptionsModification(
+                options -> {
+                  options.enableUninstantiatedTypeOptimization = enableArgumentPropagation;
+                  options.callSiteOptimizationOptions().setEnabled(enableArgumentPropagation);
+                })
             // TODO(b/120764902): The calls to getOriginalName() below does not work in presence of
-            // argument removal.
-            .addKeepRules("-dontobfuscate")
-            .run(TestClass.class)
+            //  argument removal.
+            .noMinification()
+            .setMinApi(parameters.getApiLevel())
+            .run(parameters.getRuntime(), TestClass.class)
             .assertSuccessWithOutput(expected)
             .inspector();
 
@@ -73,7 +80,7 @@
     assertThat(testClassSubject, isPresent());
 
     ClassSubject otherClassSubject = inspector.clazz(A.class);
-    assertNotEquals(enableArgumentRemoval, otherClassSubject.isPresent());
+    assertNotEquals(enableArgumentPropagation, otherClassSubject.isPresent());
 
     // Check that A.method() has been removed.
     assertThat(otherClassSubject.uniqueMethodWithName("method"), not(isPresent()));
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/PrivateInstanceMethodCollisionTest.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/PrivateInstanceMethodCollisionTest.java
index 8dc29fb..58dc4e5 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/PrivateInstanceMethodCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/PrivateInstanceMethodCollisionTest.java
@@ -35,7 +35,7 @@
   @Parameters(name = "{0}, minification: {1}, allowaccessmodification: {2}")
   public static List<Object[]> data() {
     return buildParameters(
-        getTestParameters().withAllRuntimes().build(),
+        getTestParameters().withAllRuntimesAndApiLevels().build(),
         BooleanUtils.values(),
         BooleanUtils.values());
   }
@@ -66,7 +66,7 @@
         .enableNoHorizontalClassMergingAnnotations()
         .minification(minification)
         .allowAccessModification(allowAccessModification)
-        .setMinApi(parameters.getRuntime())
+        .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::verifyUninstantiatedArgumentsRemovedAndNoCollisions)
         .run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/UninstantiatedAnnotatedArgumentsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/UninstantiatedAnnotatedArgumentsTest.java
index 663bcf6..0e4a3a2 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/UninstantiatedAnnotatedArgumentsTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/UninstantiatedAnnotatedArgumentsTest.java
@@ -100,10 +100,7 @@
     for (MethodSubject methodSubject : methodSubjects) {
       assertThat(methodSubject, isPresent());
 
-      // TODO(b/131735725): Should also remove arguments from the virtual methods.
-      boolean shouldHaveArgumentRemoval =
-          keepUninstantiatedArguments || methodSubject.getOriginalName().contains("Virtual");
-      if (shouldHaveArgumentRemoval) {
+      if (keepUninstantiatedArguments) {
         assertEquals(3, methodSubject.getMethod().getParameters().size());
 
         // In non-compat mode, R8 removes annotations from non-pinned items.
@@ -122,7 +119,7 @@
         assertEquals(1, annotationSet.size());
 
         DexAnnotation annotation = annotationSet.getFirst();
-        if (shouldHaveArgumentRemoval && i == getPositionOfUnusedArgument(methodSubject)) {
+        if (keepUninstantiatedArguments && i == getPositionOfUnusedArgument(methodSubject)) {
           assertEquals(
               uninstantiatedClassSubject.getFinalName(),
               annotation.getAnnotationType().getTypeName());
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/CollisionWithLibraryMethodsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/CollisionWithLibraryMethodsTest.java
index 14efc77..612a0b5 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/CollisionWithLibraryMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/CollisionWithLibraryMethodsTest.java
@@ -67,7 +67,7 @@
       assertEquals("a", methodSubject.getFinalName());
       assertEquals(0, methodSubject.getMethod().getReference().proto.parameters.size());
     } else {
-      assertEquals("toString1", methodSubject.getFinalName());
+      assertEquals("toString$1", methodSubject.getFinalName());
       assertEquals(0, methodSubject.getMethod().getReference().proto.parameters.size());
     }
   }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAndUninstantiatedTypesTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAndUninstantiatedTypesTest.java
index 138f0a9..7ca05fb 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAndUninstantiatedTypesTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAndUninstantiatedTypesTest.java
@@ -6,6 +6,7 @@
 
 import static org.junit.Assert.assertEquals;
 
+import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -39,6 +40,7 @@
         .addKeepMainRule(Main.class)
         .noMinification()
         .enableInliningAnnotations()
+        .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::assertMethodsAreThere)
@@ -168,5 +170,6 @@
 
   private static class UnInstantiated {}
 
+  @NeverClassInline
   private static class Unused {}
 }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAnnotatedArgumentsWithMissingAnnotationsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAnnotatedArgumentsWithMissingAnnotationsTest.java
index c008972..c074ee1 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAnnotatedArgumentsWithMissingAnnotationsTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAnnotatedArgumentsWithMissingAnnotationsTest.java
@@ -82,7 +82,9 @@
         .addKeepRules(
             "-keep @interface Annotation?",
             "-neverclassinline class *",
-            "-nohorizontalclassmerging class Test$Inner?")
+            "-nohorizontalclassmerging class Test$Inner?",
+            "-keepclassmembers class Test$Inner? { synthetic <fields>; }",
+            "-keepconstantarguments class Test$Inner? { void <init>(...); }")
         .addKeepRuntimeVisibleParameterAnnotations()
         .enableProguardTestOptions()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentRemovalWithOverridingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentRemovalWithOverridingTest.java
index 281db3e..58b052c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentRemovalWithOverridingTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentRemovalWithOverridingTest.java
@@ -32,7 +32,8 @@
 
   @Parameters(name = "{1}, minification: {0}")
   public static List<Object[]> params() {
-    return buildParameters(BooleanUtils.values(), getTestParameters().withAllRuntimes().build());
+    return buildParameters(
+        BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
   }
 
   public UnusedArgumentRemovalWithOverridingTest(boolean minification, TestParameters parameters) {
@@ -50,7 +51,7 @@
         .enableInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .minification(minification)
-        .setMinApi(parameters.getRuntime())
+        .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::verify)
         .run(parameters.getRuntime(), TestClass.class)
@@ -70,8 +71,9 @@
   static class TestClass {
 
     public static void main(String[] args) {
-      System.out.println(new A().greeting("Hello world!"));
-      System.out.println(new B().greeting("Hello world!"));
+      String greeting = System.currentTimeMillis() > 0 ? "Hello world!" : null;
+      System.out.println(new A().greeting(greeting));
+      System.out.println(new B().greeting(greeting));
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsCollisionMappingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsCollisionMappingTest.java
index 7d9a941..8ccee13 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsCollisionMappingTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsCollisionMappingTest.java
@@ -9,12 +9,12 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.KeepConstantArguments;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.R8TestRunResult;
 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;
@@ -29,25 +29,20 @@
 public class UnusedArgumentsCollisionMappingTest extends TestBase {
 
   private final TestParameters parameters;
-  private final CompilationMode compilationMode;
 
-  @Parameters(name = "{0}, compilation mode: {1}")
-  public static List<Object[]> data() {
-    return buildParameters(
-        getTestParameters().withAllRuntimesAndApiLevels().build(), CompilationMode.values());
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
-  public UnusedArgumentsCollisionMappingTest(
-      TestParameters parameters, CompilationMode compilationMode) {
+  public UnusedArgumentsCollisionMappingTest(TestParameters parameters) {
     this.parameters = parameters;
-    this.compilationMode = compilationMode;
   }
 
   @Test
   public void testR8() throws Exception {
     R8TestRunResult runResult =
         testForR8(parameters.getBackend())
-            .setMode(compilationMode)
             .addProgramClasses(Main.class)
             .setMinApi(parameters.getApiLevel())
             .addKeepMainRule(Main.class)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsCollisionTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsCollisionTest.java
index 1137a01..c59eb85 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsCollisionTest.java
@@ -34,7 +34,8 @@
 
   @Parameters(name = "{1}, minification: {0}")
   public static List<Object[]> data() {
-    return buildParameters(BooleanUtils.values(), getTestParameters().withAllRuntimes().build());
+    return buildParameters(
+        BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
   }
 
   public UnusedArgumentsCollisionTest(boolean minification, TestParameters parameters) {
@@ -60,7 +61,7 @@
         .enableNeverClassInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .minification(minification)
-        .setMinApi(parameters.getRuntime())
+        .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::verifyUnusedArgumentsRemovedAndNoCollisions)
         .run(parameters.getRuntime(), TestClass.class)
@@ -85,11 +86,10 @@
     MethodSubject methodB1Subject =
         bClassSubject.allMethods().stream().filter(FoundMethodSubject::isStatic).findFirst().get();
     assertThat(methodB1Subject, isPresent());
-    assertEquals(0, methodB1Subject.getMethod().getReference().proto.parameters.size());
+    assertEquals(0, methodB1Subject.getMethod().getParameters().size());
 
-    // TODO(b/129933280): Determine if we should use member pool collection for unused argument
-    //  removal for private and static methods.
-    assertEquals(methodB1Subject.getFinalName(), methodA1Subject.getFinalName());
+    // Verify that the static method B.method1() does not collide with a method in A.
+    assertNotEquals(methodB1Subject.getFinalName(), methodA1Subject.getFinalName());
     assertNotEquals(methodB1Subject.getFinalName(), methodA2Subject.getFinalName());
 
     // Verify that the unused argument has been removed from B.method2().
@@ -97,7 +97,7 @@
     MethodSubject methodB2Subject =
         bClassSubject.allMethods().stream().filter(FoundMethodSubject::isVirtual).findFirst().get();
     assertThat(methodB2Subject, isPresent());
-    assertEquals(0, methodB2Subject.getMethod().getReference().proto.parameters.size());
+    assertEquals(0, methodB2Subject.getMethod().getParameters().size());
 
     // Verify that the virtual method B.method2() does not collide with a method in A.
     assertNotEquals(methodB2Subject.getFinalName(), methodA1Subject.getFinalName());
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsInstanceConstructorTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsInstanceConstructorTest.java
index c890e97..a4622bc 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsInstanceConstructorTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsInstanceConstructorTest.java
@@ -11,11 +11,14 @@
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
 import com.android.tools.r8.TestBase;
-import com.android.tools.r8.ToolHelper;
+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.HorizontallyMergedClassesInspector;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -25,32 +28,39 @@
 @RunWith(Parameterized.class)
 public class UnusedArgumentsInstanceConstructorTest extends TestBase {
 
-  private final Backend backend;
+  private final TestParameters parameters;
 
-  @Parameters(name = "Backend: {0}")
-  public static Backend[] data() {
-    return ToolHelper.getBackends();
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
-  public UnusedArgumentsInstanceConstructorTest(Backend backend) {
-    this.backend = backend;
+  public UnusedArgumentsInstanceConstructorTest(TestParameters parameters) {
+    this.parameters = parameters;
   }
 
   @Test
   public void test() throws Exception {
     String expectedOutput = StringUtils.lines("Hello world");
 
-    if (backend == Backend.CF) {
-      testForJvm().addTestClasspath().run(TestClass.class).assertSuccessWithOutput(expectedOutput);
+    if (parameters.isCfRuntime()) {
+      testForJvm()
+          .addTestClasspath()
+          .run(parameters.getRuntime(), TestClass.class)
+          .assertSuccessWithOutput(expectedOutput);
     }
 
     CodeInspector inspector =
-        testForR8(backend)
+        testForR8(parameters.getBackend())
             .addInnerClasses(UnusedArgumentsInstanceConstructorTest.class)
             .addKeepMainRule(TestClass.class)
+            .addHorizontallyMergedClassesInspector(
+                HorizontallyMergedClassesInspector::assertNoClassesMerged)
             .enableInliningAnnotations()
             .enableNeverClassInliningAnnotations()
-            .run(TestClass.class)
+            .enableNoHorizontalClassMergingAnnotations()
+            .setMinApi(parameters.getApiLevel())
+            .run(parameters.getRuntime(), TestClass.class)
             .assertSuccessWithOutput(expectedOutput)
             .inspector();
 
@@ -73,6 +83,7 @@
   }
 
   @NeverClassInline
+  @NoHorizontalClassMerging
   static class A {
 
     public A(B uninstantiated, C unused) {
@@ -90,7 +101,9 @@
     }
   }
 
+  @NoHorizontalClassMerging
   static class B {}
 
+  @NoHorizontalClassMerging
   static class C {}
 }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsObjectTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsObjectTest.java
index 794d588..3a4eeda 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsObjectTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsObjectTest.java
@@ -29,6 +29,7 @@
     return UnusedArgumentsTestBase.data();
   }
 
+  @NeverClassInline
   static class TestObject {
 
     public final String s;
diff --git a/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java b/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
index cdc4346..5c45b49 100644
--- a/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
+++ b/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
@@ -4,8 +4,6 @@
 package com.android.tools.r8.jsr45;
 
 import static com.android.tools.r8.ToolHelper.getDefaultAndroidJar;
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.D8;
@@ -66,12 +64,11 @@
 
   private AndroidApp compileWithR8(Path inputPath, Path outputPath, Path keepRulesPath)
       throws CompilationFailedException {
+    KotlinCompiler kotlinc = KotlinCompiler.latest();
     return ToolHelper.runR8(
         R8Command.builder()
             .addProgramFiles(inputPath)
-            .addProgramFiles(
-                getKotlinStdlibJar(KotlinCompiler.latest()),
-                getKotlinAnnotationJar(KotlinCompiler.latest()))
+            .addProgramFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addLibraryFiles(getDefaultAndroidJar())
             .setOutput(outputPath, OutputMode.DexIndexed)
             .addProguardConfigurationFiles(keepRulesPath)
diff --git a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
index 2516b74..79880c8 100644
--- a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.not;
@@ -228,7 +227,7 @@
     classpath.clear();
     classpath.add(kotlinJarFile);
     classpath.add(getJavaJarFile(folder));
-    classpath.add(getKotlinAnnotationJar(kotlinc));
+    classpath.add(kotlinc.getKotlinAnnotationJar());
     classpath.addAll(extraClasspath);
 
     // Compare with Java.
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 f6cc385..6ec949a 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.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72;
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -59,7 +58,7 @@
     testForR8(parameters.getBackend())
         .addProgramFiles(
             compiledJars.getForConfiguration(kotlinc, targetVersion),
-            getKotlinAnnotationJar(kotlinc))
+            kotlinc.getKotlinAnnotationJar())
         .addKeepRules(
             StringUtils.lines(
                 "-keepclasseswithmembers class " + MAIN + "{", "  public static *** *(...);", "}"))
@@ -110,7 +109,7 @@
     testForR8(parameters.getBackend())
         .addProgramFiles(
             compiledJars.getForConfiguration(kotlinc, targetVersion),
-            getKotlinAnnotationJar(kotlinc))
+            kotlinc.getKotlinAnnotationJar())
         .addKeepRules(
             StringUtils.lines(
                 "-keepclasseswithmembers class " + MAIN + "{",
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 baf1308..50e9c6e 100644
--- a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
@@ -48,9 +48,9 @@
     testForR8(parameters.getBackend())
         .addLibraryFiles(
             ToolHelper.getMostRecentAndroidJar(),
-            ToolHelper.getKotlinStdlibJar(kotlinc),
-            ToolHelper.getKotlinAnnotationJar(kotlinc))
-        .addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinc))
+            kotlinc.getKotlinStdlibJar(),
+            kotlinc.getKotlinAnnotationJar())
+        .addProgramFiles(kotlinc.getKotlinReflectJar())
         .addKeepAttributes(ProguardKeepAttributes.SIGNATURE)
         .addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
         .addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
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 68614ad..db73684 100644
--- a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
@@ -3,8 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static org.hamcrest.CoreMatchers.equalTo;
 
 import com.android.tools.r8.KotlinTestBase;
@@ -35,7 +33,7 @@
 
   private void test(Collection<String> rules) throws Exception {
     testForR8(parameters.getBackend())
-        .addProgramFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+        .addProgramFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
         .addKeepRules(rules)
         .addKeepAttributes(ProguardKeepAttributes.SIGNATURE)
         .addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
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 46adea0..0f534e0 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
@@ -10,11 +10,11 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
 
+import com.android.tools.r8.KotlinCompilerTool.KotlinTargetVersion;
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.R8TestBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.jasmin.JasminBuilder;
 import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
@@ -327,7 +327,7 @@
             "accessors",
             mainClass,
             builder -> {
-              builder.addClasspathFiles(ToolHelper.getKotlinAnnotationJar(kotlinc));
+              builder.addClasspathFiles(kotlinc.getKotlinAnnotationJar());
               builder.noClassStaticizing();
             })
         .inspect(
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 aebbbb2..2cb21d4 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
@@ -6,10 +6,8 @@
 
 import static org.junit.Assume.assumeTrue;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.kotlin.TestKotlinClass.Visibility;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
@@ -78,9 +76,7 @@
                     .addOptionsModification(disableClassInliner))
         .inspect(
             inspector -> {
-              if (allowAccessModification
-                  && kotlinParameters.is(
-                      KotlinCompilerVersion.KOTLINC_1_5_0, KotlinTargetVersion.JAVA_8)) {
+              if (allowAccessModification) {
                 checkClassIsRemoved(inspector, TEST_DATA_CLASS.getClassName());
               } else {
                 ClassSubject dataClass =
@@ -129,9 +125,7 @@
                     .addOptionsModification(disableClassInliner))
         .inspect(
             inspector -> {
-              if (allowAccessModification
-                  && kotlinParameters.is(
-                      KotlinCompilerVersion.KOTLINC_1_5_0, KotlinTargetVersion.JAVA_8)) {
+              if (allowAccessModification) {
                 checkClassIsRemoved(inspector, TEST_DATA_CLASS.getClassName());
               } else {
                 ClassSubject dataClass =
@@ -181,21 +175,26 @@
                     .addOptionsModification(disableClassInliner))
         .inspect(
             inspector -> {
-              ClassSubject dataClass = checkClassIsKept(inspector, TEST_DATA_CLASS.getClassName());
+              if (allowAccessModification) {
+                checkClassIsRemoved(inspector, TEST_DATA_CLASS.getClassName());
+              } else {
+                ClassSubject dataClass =
+                    checkClassIsKept(inspector, TEST_DATA_CLASS.getClassName());
 
-              boolean component2IsPresent = !allowAccessModification;
-              checkMethodIsKeptOrRemoved(dataClass, COMPONENT2_METHOD, component2IsPresent);
+                boolean component2IsPresent = !allowAccessModification;
+                checkMethodIsKeptOrRemoved(dataClass, COMPONENT2_METHOD, component2IsPresent);
 
-              // Function component1 is not used.
-              checkMethodIsRemoved(dataClass, COMPONENT1_METHOD);
+                // Function component1 is not used.
+                checkMethodIsRemoved(dataClass, COMPONENT1_METHOD);
 
-              // No use of getter.
-              checkMethodIsRemoved(dataClass, NAME_GETTER_METHOD);
-              checkMethodIsRemoved(dataClass, AGE_GETTER_METHOD);
+                // No use of getter.
+                checkMethodIsRemoved(dataClass, NAME_GETTER_METHOD);
+                checkMethodIsRemoved(dataClass, AGE_GETTER_METHOD);
 
-              // No use of copy functions.
-              checkMethodIsRemoved(dataClass, COPY_METHOD);
-              checkMethodIsRemoved(dataClass, COPY_DEFAULT_METHOD);
+                // No use of copy functions.
+                checkMethodIsRemoved(dataClass, COPY_METHOD);
+                checkMethodIsRemoved(dataClass, COPY_DEFAULT_METHOD);
+              }
 
               ClassSubject classSubject = checkClassIsKept(inspector, mainClassName);
               MethodSubject testMethod = checkMethodIsKept(classSubject, testMethodSignature);
@@ -222,9 +221,7 @@
                     .addOptionsModification(disableClassInliner))
         .inspect(
             inspector -> {
-              if (testParameters.isDexRuntime()
-                  && allowAccessModification
-                  && kotlinParameters.is(KotlinCompilerVersion.KOTLINC_1_3_72)) {
+              if (allowAccessModification) {
                 checkClassIsRemoved(inspector, TEST_DATA_CLASS.getClassName());
               } else {
                 ClassSubject dataClass =
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/JStyleKotlinLambdaMergingWithEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/JStyleKotlinLambdaMergingWithEnumUnboxingTest.java
index 787ae1f..c6885f3 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/JStyleKotlinLambdaMergingWithEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/JStyleKotlinLambdaMergingWithEnumUnboxingTest.java
@@ -4,14 +4,11 @@
 
 package com.android.tools.r8.kotlin.lambda;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
-
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.kotlin.lambda.JStyleKotlinLambdaMergingWithEnumUnboxingTest.Main.EnumUnboxingCandidate;
 import java.util.List;
 import org.junit.Test;
@@ -23,19 +20,19 @@
 public class JStyleKotlinLambdaMergingWithEnumUnboxingTest extends TestBase {
 
   private final TestParameters parameters;
-  private final KotlinCompiler kotlinc;
+  private final KotlinTestParameters kotlinTestParameters;
 
   @Parameters(name = "{0}, kotlinc: {1}")
   public static List<Object[]> data() {
     return buildParameters(
-        TestBase.getTestParameters().withDexRuntimes().withAllApiLevels().build(),
-        getKotlinCompilers());
+        getTestParameters().withDexRuntimes().withAllApiLevels().build(),
+        getKotlinTestParameters().withAllCompilers().withNoTargetVersion().build());
   }
 
   public JStyleKotlinLambdaMergingWithEnumUnboxingTest(
-      TestParameters parameters, KotlinCompiler kotlinc) {
+      TestParameters parameters, KotlinTestParameters kotlinTestParameters) {
     this.parameters = parameters;
-    this.kotlinc = kotlinc;
+    this.kotlinTestParameters = kotlinTestParameters;
   }
 
   @Test
@@ -43,7 +40,7 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(getClass())
         .addDefaultRuntimeLibrary(parameters)
-        .addLibraryFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
+        .addLibraryFiles(kotlinTestParameters.getCompiler().getKotlinStdlibJar())
         .addKeepMainRule(Main.class)
         .addHorizontallyMergedClassesInspector(
             inspector -> inspector.assertMergedInto(Lambda2.class, Lambda1.class))
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KStyleKotlinLambdaMergingWithEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KStyleKotlinLambdaMergingWithEnumUnboxingTest.java
index 5b22795..b4abfb6 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KStyleKotlinLambdaMergingWithEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KStyleKotlinLambdaMergingWithEnumUnboxingTest.java
@@ -4,12 +4,9 @@
 
 package com.android.tools.r8.kotlin.lambda;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static org.hamcrest.CoreMatchers.equalTo;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestBase;
@@ -25,26 +22,28 @@
 public class KStyleKotlinLambdaMergingWithEnumUnboxingTest extends TestBase {
 
   private final TestParameters parameters;
-  private final KotlinCompiler kotlinc;
+  private final KotlinTestParameters kotlinTestParameters;
 
   @Parameters(name = "{0}, kotlinc: {1}")
   public static List<Object[]> data() {
     return buildParameters(
-        TestBase.getTestParameters().withDexRuntimes().withAllApiLevels().build(),
-        getKotlinCompilers());
+        getTestParameters().withDexRuntimes().withAllApiLevels().build(),
+        getKotlinTestParameters().withAllCompilers().withNoTargetVersion().build());
   }
 
   public KStyleKotlinLambdaMergingWithEnumUnboxingTest(
-      TestParameters parameters, KotlinCompiler kotlinc) {
+      TestParameters parameters, KotlinTestParameters kotlinTestParameters) {
     this.parameters = parameters;
-    this.kotlinc = kotlinc;
+    this.kotlinTestParameters = kotlinTestParameters;
   }
 
   @Test
   public void test() throws Exception {
     testForR8(parameters.getBackend())
         .addInnerClasses(getClass())
-        .addProgramFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+        .addProgramFiles(
+            kotlinTestParameters.getCompiler().getKotlinStdlibJar(),
+            kotlinTestParameters.getCompiler().getKotlinAnnotationJar())
         .addKeepMainRule(Main.class)
         .addHorizontallyMergedClassesInspector(
             inspector -> inspector.assertMergedInto(Lambda2.class, Lambda1.class))
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 7966656..35087f5 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,8 +4,6 @@
 package com.android.tools.r8.kotlin.lambda;
 
 import static com.android.tools.r8.ToolHelper.getJava8RuntimeJar;
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.junit.Assume.assumeTrue;
 
@@ -52,14 +50,14 @@
             .compile();
     testForR8(parameters.getBackend())
         .addLibraryFiles(getJava8RuntimeJar())
-        .addLibraryFiles(getKotlinStdlibJar(kotlinc))
-        .addProgramFiles(ktClasses, getKotlinAnnotationJar(kotlinc))
+        .addLibraryFiles(kotlinc.getKotlinStdlibJar())
+        .addProgramFiles(ktClasses, kotlinc.getKotlinAnnotationJar())
         .addKeepMainRule("**.B143165163Kt")
         .allowDiagnosticWarningMessages()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc))
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar())
         .run(parameters.getRuntime(), pkg + ".B143165163Kt")
         .assertSuccessWithOutputLines("outer foo bar", "outer foo default");
   }
@@ -77,7 +75,7 @@
             .addSourceFiles(getKotlinFileInTest(folder, "b143165163"))
             .compile();
     testForR8(parameters.getBackend())
-        .addProgramFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+        .addProgramFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
         .addProgramFiles(ktClasses)
         .addKeepMainRule("**.B143165163Kt")
         .allowDiagnosticWarningMessages()
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingCapturesKotlinStyleTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingCapturesKotlinStyleTest.java
index 1e8065c..a1adb7a 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingCapturesKotlinStyleTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingCapturesKotlinStyleTest.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.kotlin.lambda;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
 import static com.android.tools.r8.utils.PredicateUtils.not;
 import static junit.framework.TestCase.assertEquals;
 import static org.hamcrest.CoreMatchers.containsString;
@@ -152,7 +151,7 @@
         getCompileMemoizer(getKotlinFilesInResource(getTestName()), getTestName())
             .configure(kotlinCompilerTool -> kotlinCompilerTool.includeRuntime().noReflect())
             .getForConfiguration(kotlinc, targetVersion);
-    return ImmutableList.of(kotlinJarFile, getJavaJarFile(), getKotlinAnnotationJar(kotlinc));
+    return ImmutableList.of(kotlinJarFile, getJavaJarFile(), kotlinc.getKotlinAnnotationJar());
   }
 
   private String getTestName() {
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 de923a9..03d1aa2 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
@@ -3,14 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.lambda;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
 import static org.hamcrest.CoreMatchers.equalTo;
 
 import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.KotlinCompilerTool.KotlinTargetVersion;
 import com.android.tools.r8.KotlinTestBase;
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -50,7 +49,7 @@
         .setMode(CompilationMode.DEBUG)
         .addProgramFiles(
             compiledJars.getForConfiguration(kotlinc, KotlinTargetVersion.JAVA_6),
-            getKotlinAnnotationJar(kotlinc))
+            kotlinc.getKotlinAnnotationJar())
         .addProgramFiles(getJavaJarFile(FOLDER))
         .setMinApi(parameters.getApiLevel())
         .addKeepMainRule(MAIN_CLASS)
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 a2127ef..2ac2b7b 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
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.kotlin.lambda;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
 import static com.android.tools.r8.shaking.ProguardKeepAttributes.ENCLOSING_METHOD;
 import static com.android.tools.r8.shaking.ProguardKeepAttributes.INNER_CLASSES;
 import static com.android.tools.r8.shaking.ProguardKeepAttributes.SIGNATURE;
@@ -146,7 +145,7 @@
         getCompileMemoizer(getKotlinFilesInResource(getTestName()), getTestName())
             .configure(kotlinCompilerTool -> kotlinCompilerTool.includeRuntime().noReflect())
             .getForConfiguration(kotlinc, targetVersion);
-    return ImmutableList.of(kotlinJarFile, getJavaJarFile(), getKotlinAnnotationJar(kotlinc));
+    return ImmutableList.of(kotlinJarFile, getJavaJarFile(), kotlinc.getKotlinAnnotationJar());
   }
 
   private String getTestName() {
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 500f7c1..33e5cff 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
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.kotlin.lambda;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
 import static com.android.tools.r8.utils.PredicateUtils.not;
 import static junit.framework.TestCase.assertEquals;
 import static org.hamcrest.CoreMatchers.containsString;
@@ -146,7 +145,7 @@
         getCompileMemoizer(getKotlinFilesInResource(getTestName()), getTestName())
             .configure(kotlinCompilerTool -> kotlinCompilerTool.includeRuntime().noReflect())
             .getForConfiguration(kotlinc, targetVersion);
-    return ImmutableList.of(kotlinJarFile, getJavaJarFile(), getKotlinAnnotationJar(kotlinc));
+    return ImmutableList.of(kotlinJarFile, getJavaJarFile(), kotlinc.getKotlinAnnotationJar());
   }
 
   private String getTestName() {
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialJavaStyleTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialJavaStyleTest.java
index 7b02bdb..41552ac 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialJavaStyleTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialJavaStyleTest.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.kotlin.lambda;
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_5_0;
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
 import static com.android.tools.r8.utils.PredicateUtils.not;
 import static junit.framework.TestCase.assertEquals;
 import static org.hamcrest.CoreMatchers.containsString;
@@ -204,7 +203,7 @@
         getCompileMemoizer(getKotlinFilesInResource(getTestName()), getTestName())
             .configure(kotlinCompilerTool -> kotlinCompilerTool.includeRuntime().noReflect())
             .getForConfiguration(kotlinc, targetVersion);
-    return ImmutableList.of(kotlinJarFile, getJavaJarFile(), getKotlinAnnotationJar(kotlinc));
+    return ImmutableList.of(kotlinJarFile, getJavaJarFile(), kotlinc.getKotlinAnnotationJar());
   }
 
   private String getTestName() {
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialKotlinStyleTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialKotlinStyleTest.java
index cb79d89..b7cc148 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialKotlinStyleTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialKotlinStyleTest.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.kotlin.lambda;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
 import static com.android.tools.r8.utils.PredicateUtils.not;
 import static junit.framework.TestCase.assertEquals;
 import static org.hamcrest.CoreMatchers.containsString;
@@ -162,7 +161,7 @@
         getCompileMemoizer(getKotlinFilesInResource(getTestName()), getTestName())
             .configure(kotlinCompilerTool -> kotlinCompilerTool.includeRuntime().noReflect())
             .getForConfiguration(kotlinc, targetVersion);
-    return ImmutableList.of(kotlinJarFile, getJavaJarFile(), getKotlinAnnotationJar(kotlinc));
+    return ImmutableList.of(kotlinJarFile, getJavaJarFile(), kotlinc.getKotlinAnnotationJar());
   }
 
   private String getTestName() {
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 23b51d7..06915ec 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
@@ -4,8 +4,6 @@
 
 package com.android.tools.r8.kotlin.lambda.b148525512;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static org.hamcrest.CoreMatchers.equalTo;
 
 import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer;
@@ -76,7 +74,7 @@
     Path featureCode = temp.newFile("feature.zip").toPath();
     R8TestCompileResult compileResult =
         testForR8(parameters.getBackend())
-            .addProgramFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addProgramFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(kotlinBaseClasses.getForConfiguration(kotlinc, targetVersion))
             .addProgramClasses(FeatureAPI.class)
             .addKeepMainRule(baseKtClassName)
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java
index 0a80034..c1da822 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java
@@ -4,9 +4,7 @@
 
 package com.android.tools.r8.kotlin.lambda.b159688129;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinC_1_3_72;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.not;
@@ -105,7 +103,7 @@
     writeClassFileDataToJar(classFiles, classFileData);
     return ImmutableList.of(
         classFiles,
-        getKotlinStdlibJar(getKotlinC_1_3_72()),
-        getKotlinAnnotationJar(getKotlinC_1_3_72()));
+        KOTLINC_1_3_72.getCompiler().getKotlinStdlibJar(),
+        KOTLINC_1_3_72.getCompiler().getKotlinAnnotationJar());
   }
 }
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 3ce8beb..7bd2b8c 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
@@ -4,8 +4,6 @@
 
 package com.android.tools.r8.kotlin.lambda.b159688129;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.equalTo;
@@ -61,7 +59,7 @@
             .addSourceFiles(getKotlinFileInTest(folder, "Simple"))
             .compile();
     testForR8(parameters.getBackend())
-        .addProgramFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+        .addProgramFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
         .addProgramFiles(ktClasses)
         .setMinApi(parameters.getApiLevel())
         .addKeepMainRule(PKG_NAME + ".SimpleKt")
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataFirstToLatestTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataFirstToLatestTest.java
index c030f80..de3e23d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataFirstToLatestTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataFirstToLatestTest.java
@@ -4,8 +4,6 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -15,7 +13,6 @@
 import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.StringUtils;
 import java.nio.file.Path;
@@ -64,7 +61,7 @@
     runTest(
         KotlinCompilerVersion.KOTLINC_1_5_0,
         libJars.getForConfiguration(kotlinc, targetVersion),
-        getKotlinStdlibJar(kotlinc));
+        kotlinc.getKotlinStdlibJar());
   }
 
   @Test
@@ -72,7 +69,7 @@
     Path libJar =
         testForR8(parameters.getBackend())
             .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
-            .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addKeepAllClassesRule()
             .addKeepAllAttributes()
             .addOptionsModification(
@@ -84,8 +81,7 @@
             .writeToZip();
     Path stdLibJar =
         testForR8(parameters.getBackend())
-            .addProgramFiles(
-                ToolHelper.getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinAnnotationJar(kotlinc))
+            .addProgramFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addKeepAllClassesRule()
             .addKeepAllAttributes()
             .allowDiagnosticWarningMessages()
@@ -109,15 +105,14 @@
     Path libJar =
         testForR8(parameters.getBackend())
             .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
-            .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addKeepAllClassesRule()
             .addKeepAllAttributes()
             .compile()
             .writeToZip();
     Path stdLibJar =
         testForR8(parameters.getBackend())
-            .addProgramFiles(
-                ToolHelper.getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinAnnotationJar(kotlinc))
+            .addProgramFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addKeepAllClassesRule()
             .addKeepAllAttributes()
             .allowDiagnosticWarningMessages()
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrimitiveTypeRewriteTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrimitiveTypeRewriteTest.java
index a9732a4..1dae299 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrimitiveTypeRewriteTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrimitiveTypeRewriteTest.java
@@ -5,8 +5,6 @@
 package com.android.tools.r8.kotlin.metadata;
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.equalTo;
@@ -63,7 +61,7 @@
             .setOutputPath(temp.newFolder().toPath())
             .compile();
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG_APP + ".MainKt")
         .assertSuccessWithOutputLines(EXPECTED);
@@ -83,8 +81,8 @@
     Path libJar =
         testForR8(parameters.getBackend())
             .addProgramFiles(
-                getKotlinStdlibJar(kotlinc),
-                getKotlinAnnotationJar(kotlinc),
+                kotlinc.getKotlinStdlibJar(),
+                kotlinc.getKotlinAnnotationJar(),
                 libJars.getForConfiguration(kotlinc, targetVersion))
             .addKeepAllClassesRuleWithAllowObfuscation()
             .addKeepRules("-keep class " + PKG_LIB + ".LibKt { *; }")
@@ -110,7 +108,7 @@
             .compile();
     final JvmTestRunResult runResult =
         testForJvm()
-            .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+            .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
             .addClasspath(output)
             .run(parameters.getRuntime(), PKG_APP + ".MainKt");
     if (keepUnit) {
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 505169e..2f27ec3 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
@@ -11,7 +11,6 @@
 
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.kotlin.metadata.metadata_pruned_fields.Main;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -45,7 +44,7 @@
   @Test
   public void testR8() throws Exception {
     testForR8(parameters.getBackend())
-        .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
+        .addProgramFiles(kotlinc.getKotlinStdlibJar())
         .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
         .addProgramClassFileData(Main.dump())
         .addKeepRules("-keep class " + PKG + ".metadata_pruned_fields.MethodsKt { *; }")
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAllowAccessModificationTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAllowAccessModificationTest.java
index a02d225..21c1cce 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAllowAccessModificationTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAllowAccessModificationTest.java
@@ -4,8 +4,6 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static junit.framework.TestCase.assertEquals;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -83,7 +81,7 @@
             .setOutputPath(temp.newFolder().toPath())
             .compile();
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG_APP + ".MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -97,7 +95,7 @@
     Path libJar =
         testForR8(parameters.getBackend())
             .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
-            .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addKeepRules("-keepclassmembers,allowaccessmodification class **.Lib { *; }")
             .addKeepRules("-keep,allowaccessmodification,allowobfuscation class **.Lib { *; }")
             .addKeepRules("-keepclassmembers,allowaccessmodification class **.Lib$Comp { *; }")
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java
index 5c9a639..440a8d5 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.kotlin.metadata;
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
 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;
@@ -15,7 +14,6 @@
 
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.StringUtils;
@@ -91,8 +89,7 @@
             .setOutputPath(temp.newFolder().toPath())
             .compile();
     testForJvm()
-        .addRunClasspathFiles(
-            ToolHelper.getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG_APP + ".MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -104,8 +101,8 @@
         testForR8(parameters.getBackend())
             .addProgramFiles(
                 libJars.getForConfiguration(kotlinc, targetVersion),
-                getKotlinAnnotationJar(kotlinc))
-            .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
+                kotlinc.getKotlinAnnotationJar())
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar())
             /// Keep the annotations
             .addKeepClassAndMembersRules(PKG_LIB + ".AnnoWithClassAndEnum")
             .addKeepClassAndMembersRules(PKG_LIB + ".AnnoWithClassArr")
@@ -140,8 +137,7 @@
                 getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main"))
             .compile();
     testForJvm()
-        .addRunClasspathFiles(
-            ToolHelper.getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), libJar)
         .addProgramFiles(output)
         .run(parameters.getRuntime(), PKG_APP + ".MainKt")
         .assertSuccessWithOutput(EXPECTED.replace(FOO_ORIGINAL_NAME, FOO_FINAL_NAME));
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnonymousTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnonymousTest.java
index 03c8d5a..5437d0d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnonymousTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnonymousTest.java
@@ -5,8 +5,6 @@
 package com.android.tools.r8.kotlin.metadata;
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
 import static org.hamcrest.MatcherAssert.assertThat;
 
@@ -58,7 +56,7 @@
             .setOutputPath(temp.newFolder().toPath())
             .compile();
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".anonymous_app.MainKt")
         .assertSuccessWithOutputLines(EXPECTED);
@@ -68,7 +66,7 @@
   public void testMetadataForLib() throws Exception {
     Path libJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
             .addKeepAllClassesRuleWithAllowObfuscation()
             .addKeepRules("-keep class " + PKG + ".anonymous_lib.Test$A { *; }")
@@ -88,7 +86,7 @@
             .setOutputPath(temp.newFolder().toPath())
             .compile();
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(main)
         .run(parameters.getRuntime(), PKG + ".anonymous_app.MainKt")
         .assertSuccessWithOutputLines(EXPECTED);
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 1cd0805..5e5fc28 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
@@ -4,8 +4,6 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static junit.framework.TestCase.assertEquals;
 import static junit.framework.TestCase.assertNotNull;
@@ -14,7 +12,6 @@
 
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.kotlin.KotlinMetadataWriter;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
@@ -64,7 +61,7 @@
             .setOutputPath(temp.newFolder().toPath())
             .compile();
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".box_primitives_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -81,8 +78,7 @@
             .compile();
     testForJvm()
         .addVmArguments("-ea")
-        .addRunClasspathFiles(
-            getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".box_primitives_app.Main_reflectKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -92,7 +88,7 @@
   public void testMetadataForLib() throws Exception {
     Path libJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
             .addKeepAllClassesRule()
             .addKeepAttributes(
@@ -110,8 +106,7 @@
             .setOutputPath(temp.newFolder().toPath())
             .compile();
     testForJvm()
-        .addRunClasspathFiles(
-            getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), libJar)
         .addClasspath(main)
         .run(parameters.getRuntime(), PKG + ".box_primitives_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -148,7 +143,7 @@
   public void testMetadataForReflect() throws Exception {
     Path libJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
             .addKeepAllClassesRule()
             .addKeepAttributes(
@@ -166,8 +161,7 @@
             .compile();
     testForJvm()
         .addVmArguments("-ea")
-        .addRunClasspathFiles(
-            getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), libJar)
         .addClasspath(main)
         .run(parameters.getRuntime(), PKG + ".box_primitives_app.Main_reflectKt")
         .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineAnonFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineAnonFunctionTest.java
index 5e64b54..cf5d1eb 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineAnonFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineAnonFunctionTest.java
@@ -4,9 +4,6 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
-
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -53,7 +50,7 @@
             .setOutputPath(temp.newFolder().toPath())
             .compile();
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG_APP + ".MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -64,7 +61,7 @@
     Path libJar =
         testForR8(parameters.getBackend())
             .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
-            .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addKeepAllClassesRule()
             .addKeepAllAttributes()
             .compile()
@@ -76,7 +73,7 @@
                 getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main"))
             .compile();
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addProgramFiles(output)
         .run(parameters.getRuntime(), PKG_APP + ".MainKt")
         .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineConcreteFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineConcreteFunctionTest.java
index 8ca888c..52bdbb6 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineConcreteFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineConcreteFunctionTest.java
@@ -4,9 +4,6 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
-
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -53,7 +50,7 @@
             .setOutputPath(temp.newFolder().toPath())
             .compile();
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG_APP + ".MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -64,7 +61,7 @@
     Path libJar =
         testForR8(parameters.getBackend())
             .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
-            .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addKeepAllClassesRule()
             .addKeepAllAttributes()
             .compile()
@@ -77,7 +74,7 @@
             .setOutputPath(temp.newFolder().toPath())
             .compile();
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG_APP + ".MainKt")
         .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
index ffe8b3a..994a011 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
@@ -4,10 +4,6 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinReflectJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
-
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
@@ -54,7 +50,7 @@
   @Test
   public void smokeTest() throws Exception {
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinReflectJar(kotlinc))
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar())
         .addClasspath(jars.getForConfiguration(kotlinc, targetVersion))
         .run(parameters.getRuntime(), PKG_APP + ".MainKt")
         .assertSuccessWithOutput(EXPECTED_MAIN);
@@ -65,9 +61,9 @@
     Path outputJar =
         testForR8(parameters.getBackend())
             .addClasspathFiles(
-                getKotlinStdlibJar(kotlinc),
-                getKotlinReflectJar(kotlinc),
-                getKotlinAnnotationJar(kotlinc))
+                kotlinc.getKotlinStdlibJar(),
+                kotlinc.getKotlinReflectJar(),
+                kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(jars.getForConfiguration(kotlinc, targetVersion))
             .addKeepAllClassesRule()
             .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
@@ -80,7 +76,7 @@
                         (addedStrings, addedNonInitStrings) -> {}))
             .writeToZip();
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinReflectJar(kotlinc))
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar())
         .addClasspath(outputJar)
         .run(parameters.getRuntime(), PKG_APP + ".MainKt")
         .assertSuccessWithOutput(EXPECTED_MAIN);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeep.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeep.java
index ef9c705..8230cef 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeep.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeep.java
@@ -4,8 +4,6 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
@@ -45,7 +43,7 @@
   @Test
   public void testR8() throws CompilationFailedException, IOException, ExecutionException {
     testForR8(parameters.getBackend())
-        .addProgramFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+        .addProgramFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
         .setMinApi(parameters.getApiLevel())
         .addKeepKotlinMetadata()
         .addKeepRules(StringUtils.joinLines("-if class *.Metadata", "-keep class <1>.io.** { *; }"))
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmpty.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmpty.java
index 08de164..1acdcf2 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmpty.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmpty.java
@@ -4,8 +4,6 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
@@ -47,7 +45,7 @@
   @Test
   public void testKotlinStdLib() throws Exception {
     testForR8(parameters.getBackend())
-        .addProgramFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+        .addProgramFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
         .setMinApi(parameters.getApiLevel())
         .addKeepAllClassesRuleWithAllowObfuscation()
         .addKeepKotlinMetadata()
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteFlexibleUpperBoundTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteFlexibleUpperBoundTest.java
index e37b5fd..15a3391 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteFlexibleUpperBoundTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteFlexibleUpperBoundTest.java
@@ -5,8 +5,6 @@
 package com.android.tools.r8.kotlin.metadata;
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 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;
@@ -16,7 +14,6 @@
 
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -70,7 +67,7 @@
             .setOutputPath(temp.newFolder().toPath())
             .compile();
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".flexible_upper_bound_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -80,7 +77,7 @@
   public void testMetadataForLib() throws Exception {
     Path libJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
             // Allow renaming A to ensure that we rename in the flexible upper bound type.
             .addKeepRules("-keep,allowobfuscation class " + PKG_LIB + ".A { *; }")
@@ -101,8 +98,7 @@
             .setOutputPath(temp.newFolder().toPath())
             .compile();
     testForJvm()
-        .addRunClasspathFiles(
-            getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), libJar)
         .addClasspath(main)
         .run(parameters.getRuntime(), PKG + ".flexible_upper_bound_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
index 0785411..8b9e4c1 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
@@ -4,8 +4,6 @@
 package com.android.tools.r8.kotlin.metadata;
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -75,7 +73,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), baseLibJar, extLibJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), baseLibJar, extLibJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".classpath_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -87,7 +85,7 @@
     Path libJar =
         testForR8(parameters.getBackend())
             .addClasspathFiles(
-                baseLibJar, getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+                baseLibJar, kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(extLibJarMap.getForConfiguration(kotlinc, targetVersion))
             // Keep the Extra class and its interface (which has the method).
             .addKeepRules("-keep class **.Extra")
@@ -109,7 +107,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), baseLibJar, libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), baseLibJar, libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".classpath_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
index 6e754e1..dc4535c 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
@@ -4,8 +4,6 @@
 package com.android.tools.r8.kotlin.metadata;
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 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;
@@ -75,7 +73,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".companion_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -85,7 +83,7 @@
   public void testMetadataInCompanion_kept() throws Exception {
     Path libJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(companionLibJarMap.getForConfiguration(kotlinc, targetVersion))
             // Keep everything
             .addKeepRules("-keep class **.companion_lib.** { *; }")
@@ -107,7 +105,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".companion_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -117,7 +115,7 @@
   public void testMetadataInCompanion_renamed() throws Exception {
     Path libJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(companionLibJarMap.getForConfiguration(kotlinc, targetVersion))
             // Keep the B class and its interface (which has the doStuff method).
             .addKeepRules("-keep class **.B")
@@ -149,7 +147,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".companion_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
index a95ed25..b1001e1 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.kotlin.metadata;
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -16,7 +15,6 @@
 
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -72,7 +70,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".extension_function_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -85,7 +83,7 @@
         testForR8(parameters.getBackend())
             .addProgramFiles(
                 extLibJarMap.getForConfiguration(kotlinc, targetVersion),
-                getKotlinAnnotationJar(kotlinc))
+                kotlinc.getKotlinAnnotationJar())
             // Keep the B class and its interface (which has the doStuff method).
             .addKeepRules("-keep class **.B")
             .addKeepRules("-keep class **.I { <methods>; }")
@@ -108,7 +106,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".extension_function_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -136,8 +134,7 @@
   public void testMetadataInExtensionFunction_renamed() throws Exception {
     Path libJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(
-                ToolHelper.getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(extLibJarMap.getForConfiguration(kotlinc, targetVersion))
             // Keep the B class and its interface (which has the doStuff method).
             .addKeepRules("-keep class **.B")
@@ -163,7 +160,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".extension_function_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java
index c418067..7f0fc70 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java
@@ -4,8 +4,6 @@
 package com.android.tools.r8.kotlin.metadata;
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionProperty;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
@@ -71,7 +69,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".extension_property_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -84,7 +82,7 @@
         testForR8(parameters.getBackend())
             .addProgramFiles(
                 extLibJarMap.getForConfiguration(kotlinc, targetVersion),
-                getKotlinAnnotationJar(kotlinc))
+                kotlinc.getKotlinAnnotationJar())
             // Keep the B class and its interface (which has the doStuff method).
             .addKeepRules("-keep class **.B")
             .addKeepRules("-keep class **.I { <methods>; }")
@@ -104,7 +102,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".extension_property_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -144,7 +142,7 @@
   public void testMetadataInExtensionProperty_renamed() throws Exception {
     Path libJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(extLibJarMap.getForConfiguration(kotlinc, targetVersion))
             // Keep the B class and its interface (which has the doStuff method).
             .addKeepRules("-keep class **.B")
@@ -167,7 +165,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".extension_property_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
index 14d1497..001f000 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
@@ -4,8 +4,6 @@
 package com.android.tools.r8.kotlin.metadata;
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -70,7 +68,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".function_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -83,7 +81,7 @@
         testForR8(parameters.getBackend())
             .addProgramFiles(
                 funLibJarMap.getForConfiguration(kotlinc, targetVersion),
-                getKotlinAnnotationJar(kotlinc))
+                kotlinc.getKotlinAnnotationJar())
             // Keep the B class and its interface (which has the doStuff method).
             .addKeepRules("-keep class **.B")
             .addKeepRules("-keep class **.I { <methods>; }")
@@ -102,7 +100,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".function_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -141,7 +139,7 @@
   public void testMetadataInFunction_renamed() throws Exception {
     Path libJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(funLibJarMap.getForConfiguration(kotlinc, targetVersion))
             // Keep the B class and its interface (which has the doStuff method).
             .addKeepRules("-keep class **.B")
@@ -163,7 +161,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".function_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
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 dc2ddce..a4cc7fa 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
@@ -4,8 +4,6 @@
 package com.android.tools.r8.kotlin.metadata;
 
 import static com.android.tools.r8.ToolHelper.getJava8RuntimeJar;
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -67,7 +65,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".default_value_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -78,7 +76,9 @@
     Path libJar =
         testForR8(parameters.getBackend())
             .addLibraryFiles(
-                getJava8RuntimeJar(), getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+                getJava8RuntimeJar(),
+                kotlinc.getKotlinStdlibJar(),
+                kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(defaultValueLibJarMap.getForConfiguration(kotlinc, targetVersion))
             // Keep LibKt and applyMap function, along with applyMap$default
             .addKeepRules("-keep class **.LibKt { *** applyMap*(...); }")
@@ -98,7 +98,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".default_value_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
index c2c61a6..2707bc2 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
@@ -4,8 +4,6 @@
 package com.android.tools.r8.kotlin.metadata;
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -71,7 +69,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".vararg_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -81,7 +79,7 @@
   public void testMetadataInFunctionWithVararg() throws Exception {
     Path libJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(varargLibJarMap.getForConfiguration(kotlinc, targetVersion))
             // keep SomeClass#foo, since there is a method reference in the app.
             .addKeepRules("-keep class **.SomeClass { *** foo(...); }")
@@ -103,7 +101,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".vararg_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
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 c990029..d8d7e4e 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
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
 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.anyOf;
@@ -13,7 +12,6 @@
 
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -70,8 +68,7 @@
   public void smokeTest() throws Exception {
     testForJvm()
         .addRunClasspathFiles(
-            ToolHelper.getKotlinStdlibJar(kotlinc),
-            baseLibJarMap.getForConfiguration(kotlinc, targetVersion))
+            kotlinc.getKotlinStdlibJar(), baseLibJarMap.getForConfiguration(kotlinc, targetVersion))
         .addClasspath(
             extLibJarMap.getForConfiguration(kotlinc, targetVersion),
             appJarMap.getForConfiguration(kotlinc, targetVersion))
@@ -88,7 +85,7 @@
             .addProgramFiles(
                 extLibJarMap.getForConfiguration(kotlinc, targetVersion),
                 appJarMap.getForConfiguration(kotlinc, targetVersion),
-                getKotlinAnnotationJar(kotlinc))
+                kotlinc.getKotlinAnnotationJar())
             // Keep Ext extension method which requires metadata to be called with Kotlin syntax
             // from other kotlin code.
             .addKeepRules("-keep class **.ExtKt { <methods>; }")
@@ -109,8 +106,7 @@
 
     testForJvm()
         .addRunClasspathFiles(
-            ToolHelper.getKotlinStdlibJar(kotlinc),
-            baseLibJarMap.getForConfiguration(kotlinc, targetVersion))
+            kotlinc.getKotlinStdlibJar(), baseLibJarMap.getForConfiguration(kotlinc, targetVersion))
         .addClasspath(out)
         .run(parameters.getRuntime(), main)
         .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java
index 593b124..dfc31a7 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java
@@ -3,8 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -73,7 +71,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".multifileclass_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -83,7 +81,7 @@
   public void testMetadataInMultifileClass_merged() throws Exception {
     Path libJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(multifileLibJarMap.getForConfiguration(kotlinc, targetVersion))
             // Keep UtilKt#comma*Join*(). Let R8 optimize (inline) others, such as joinOf*(String).
             .addKeepRules("-keep class **.UtilKt")
@@ -124,7 +122,7 @@
   public void testMetadataInMultifileClass_renamed() throws Exception {
     Path libJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(multifileLibJarMap.getForConfiguration(kotlinc, targetVersion))
             // Keep UtilKt#comma*Join*().
             .addKeepRules("-keep class **.UtilKt")
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java
index 24e7a56..97ec9f0 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java
@@ -14,7 +14,6 @@
 
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -64,7 +63,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".nested_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -74,7 +73,7 @@
   public void testMetadataInNestedClass() throws Exception {
     Path libJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar())
             .addProgramFiles(nestedLibJarMap.getForConfiguration(kotlinc, targetVersion))
             // Keep the Outer class and delegations.
             .addKeepRules("-keep class **.Outer { <init>(...); *** delegate*(...); }")
@@ -97,7 +96,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".nested_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java
index 59d3258..6d69d26 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java
@@ -4,8 +4,6 @@
 package com.android.tools.r8.kotlin.metadata;
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 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;
@@ -63,7 +61,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".parametertype_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -73,7 +71,7 @@
   public void testMetadataInParameterType_renamed() throws Exception {
     Path libJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(parameterTypeLibJarMap.getForConfiguration(kotlinc, targetVersion))
             // Keep non-private members of Impl
             .addKeepRules("-keep public class **.Impl { !private *; }")
@@ -92,7 +90,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".parametertype_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
index ebafde8..11e391d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
@@ -5,8 +5,6 @@
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_5_0;
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionProperty;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -71,7 +69,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".fragile_property_only_getter.Getter_userKt")
         .assertSuccessWithOutput(EXPECTED_GETTER);
@@ -81,7 +79,7 @@
   public void testMetadataInProperty_getterOnly() throws Exception {
     Path libJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(propertyTypeLibJarMap.getForConfiguration(kotlinc, targetVersion))
             // Keep property getters
             .addKeepRules("-keep class **.Person { <init>(...); }")
@@ -100,7 +98,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".fragile_property_only_getter.Getter_userKt")
         .assertSuccessWithOutput(EXPECTED_GETTER);
@@ -162,7 +160,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".fragile_property_only_setter.Setter_userKt")
         .assertSuccessWithOutputLines();
@@ -172,7 +170,7 @@
   public void testMetadataInProperty_setterOnly() throws Exception {
     Path libJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(propertyTypeLibJarMap.getForConfiguration(kotlinc, targetVersion))
             // Keep property setters (and users)
             .addKeepRules("-keep class **.Person { <init>(...); }")
@@ -195,7 +193,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".fragile_property_only_setter.Setter_userKt")
         .assertSuccessWithOutputLines();
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java
index e0233d9..06ed6a6 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java
@@ -4,8 +4,6 @@
 package com.android.tools.r8.kotlin.metadata;
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 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;
@@ -63,7 +61,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".propertytype_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -73,7 +71,7 @@
   public void testMetadataInProperty_renamed() throws Exception {
     Path libJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(propertyTypeLibJarMap.getForConfiguration(kotlinc, targetVersion))
             // Keep non-private members of Impl
             .addKeepRules("-keep public class **.Impl { !private *; }")
@@ -90,7 +88,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".propertytype_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
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 7badf74..8bcbc9e 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
@@ -61,7 +61,7 @@
         .addLibraryFiles(
             annoJarMap.getForConfiguration(kotlinc, targetVersion),
             ToolHelper.getJava8RuntimeJar(),
-            ToolHelper.getKotlinStdlibJar(kotlinc))
+            kotlinc.getKotlinStdlibJar())
         .addProgramFiles(inputJarMap.getForConfiguration(kotlinc, targetVersion))
         .addKeepRules(OBFUSCATE_RENAMED, KEEP_KEPT)
         .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
@@ -73,8 +73,7 @@
   public void testR8_kotlinStdlibAsClassPath() throws Exception {
     testForR8(parameters.getBackend())
         .addClasspathFiles(
-            annoJarMap.getForConfiguration(kotlinc, targetVersion),
-            ToolHelper.getKotlinStdlibJar(kotlinc))
+            annoJarMap.getForConfiguration(kotlinc, targetVersion), kotlinc.getKotlinStdlibJar())
         .addProgramFiles(inputJarMap.getForConfiguration(kotlinc, targetVersion))
         .addKeepRules(OBFUSCATE_RENAMED, KEEP_KEPT)
         .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
@@ -86,8 +85,7 @@
   public void testR8_kotlinStdlibAsProgramFile() throws Exception {
     testForR8(parameters.getBackend())
         .addProgramFiles(
-            annoJarMap.getForConfiguration(kotlinc, targetVersion),
-            ToolHelper.getKotlinStdlibJar(kotlinc))
+            annoJarMap.getForConfiguration(kotlinc, targetVersion), kotlinc.getKotlinStdlibJar())
         .addProgramFiles(inputJarMap.getForConfiguration(kotlinc, targetVersion))
         .addKeepRules(OBFUSCATE_RENAMED, KEEP_KEPT)
         .addKeepRules("-keep class **.Anno")
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java
index ac35453..59d626b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java
@@ -4,8 +4,6 @@
 package com.android.tools.r8.kotlin.metadata;
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 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;
@@ -63,7 +61,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".returntype_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -73,7 +71,7 @@
   public void testMetadataInReturnType_renamed() throws Exception {
     Path libJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(returnTypeLibJarMap.getForConfiguration(kotlinc, targetVersion))
             // Keep non-private members of Impl
             .addKeepRules("-keep public class **.Impl { !private *; }")
@@ -92,7 +90,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".returntype_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassNestedTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassNestedTest.java
index defd730..eacdadb 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassNestedTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassNestedTest.java
@@ -3,12 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
-
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.StringUtils;
@@ -59,8 +55,7 @@
             .setOutputPath(temp.newFolder().toPath())
             .compile();
     testForJvm()
-        .addRunClasspathFiles(
-            getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG_APP + ".MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -70,7 +65,7 @@
   public void testMetadataInSealedClass_nested() throws Exception {
     Path libJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(sealedLibJarMap.getForConfiguration(kotlinc, targetVersion))
             .addKeepAllClassesRule()
             .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
@@ -86,8 +81,7 @@
             .setOutputPath(temp.newFolder().toPath())
             .compile();
     testForJvm()
-        .addRunClasspathFiles(
-            getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG_APP + ".MainKt")
         .assertSuccessWithOutput(EXPECTED);
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 d3380e6..833ba26 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
@@ -3,8 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
@@ -67,7 +65,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".sealed_app.ValidKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -77,7 +75,7 @@
   public void testMetadataInSealedClass_valid() throws Exception {
     Path libJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(sealedLibJarMap.getForConfiguration(kotlinc, targetVersion))
             // Keep the Expr class
             .addKeepRules("-keep class **.Expr")
@@ -98,7 +96,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".sealed_app.ValidKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -144,7 +142,7 @@
   public void testMetadataInSealedClass_invalid() throws Exception {
     Path libJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(sealedLibJarMap.getForConfiguration(kotlinc, targetVersion))
             // Keep the Expr class
             .addKeepRules("-keep class **.Expr")
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java
index ffa0888..7aa3952 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java
@@ -13,7 +13,6 @@
 
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -65,7 +64,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".supertype_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -75,7 +74,7 @@
   public void testMetadataInSupertype_merged() throws Exception {
     Path libJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar())
             .addProgramFiles(superTypeLibJarMap.getForConfiguration(kotlinc, targetVersion))
             // Keep non-private members except for ones in `internal` definitions.
             .addKeepRules("-keep public class !**.internal.**, * { !private *; }")
@@ -92,7 +91,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".supertype_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -120,7 +119,7 @@
   public void testMetadataInSupertype_renamed() throws Exception {
     Path libJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar())
             .addProgramFiles(superTypeLibJarMap.getForConfiguration(kotlinc, targetVersion))
             // Keep non-private members except for ones in `internal` definitions.
             .addKeepRules("-keep public class !**.internal.**, * { !private *; }")
@@ -139,7 +138,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".supertype_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java
index 32be357..8482191 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java
@@ -4,9 +4,6 @@
 package com.android.tools.r8.kotlin.metadata;
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinReflectJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isDexClass;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -95,7 +92,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinReflectJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".typealias_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -108,9 +105,9 @@
     Path libJar =
         testForR8(parameters.getBackend())
             .addClasspathFiles(
-                getKotlinStdlibJar(kotlinc),
-                getKotlinReflectJar(kotlinc),
-                getKotlinAnnotationJar(kotlinc))
+                kotlinc.getKotlinStdlibJar(),
+                kotlinc.getKotlinReflectJar(),
+                kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(typeAliasLibJarMap.getForConfiguration(kotlinc, targetVersion))
             // Keep non-private members of Impl
             .addKeepRules("-keep class **.Impl { !private *; }")
@@ -144,7 +141,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinReflectJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), libJar)
         .addClasspath(appJar)
         .run(parameters.getRuntime(), PKG + ".typealias_app.MainKt")
         .assertSuccessWithOutput(EXPECTED.replace(superTypeName, renamedSuperTypeName));
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
index d5342de..18fce95 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
@@ -4,8 +4,6 @@
 package com.android.tools.r8.kotlin.metadata;
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isDexClass;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
@@ -108,7 +106,7 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".typeargument_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -118,7 +116,7 @@
   public void testMetadataInTypeAliasWithR8() throws Exception {
     Path libJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(jarMap.getForConfiguration(kotlinc, targetVersion))
             // Keep ClassThatWillBeObfuscated, but allow minification.
             .addKeepRules("-keep,allowobfuscation class **ClassThatWillBeObfuscated")
@@ -144,7 +142,7 @@
             .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/typeargument_app", "main"))
             .compile();
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(mainJar)
         .run(parameters.getRuntime(), PKG + ".typeargument_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
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 ab449b7..94b410e 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
@@ -4,15 +4,12 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertNull;
 
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.kotlin.KotlinMetadataWriter;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
@@ -61,7 +58,7 @@
             .setOutputPath(temp.newFolder().toPath())
             .compile();
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".inline_property_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -71,7 +68,7 @@
   public void testMetadataForLib() throws Exception {
     Path libJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
             // Allow renaming A to ensure that we rename in the flexible upper bound type.
             .addKeepAllClassesRule()
@@ -90,8 +87,7 @@
             .setOutputPath(temp.newFolder().toPath())
             .compile();
     testForJvm()
-        .addRunClasspathFiles(
-            getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), libJar)
         .addClasspath(main)
         .run(parameters.getRuntime(), PKG + ".inline_property_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
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 48007f1..5cc9de6 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
@@ -13,7 +13,6 @@
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.ProgramConsumer;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -63,8 +62,7 @@
     assumeTrue(parameters.isCfRuntime());
     Path libJar = jarMap.getForConfiguration(kotlinc, targetVersion);
     testForRuntime(parameters)
-        .addProgramFiles(
-            ToolHelper.getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc), libJar)
+        .addProgramFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), libJar)
         .run(parameters.getRuntime(), PKG_NESTED_REFLECT + ".MainKt")
         .assertSuccessWithOutput(EXPECTED);
   }
@@ -73,9 +71,9 @@
   public void testMetadataOuterRenamed() throws Exception {
     Path mainJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
-            .addClasspathFiles(ToolHelper.getKotlinReflectJar(kotlinc))
-            .addClasspathFiles(ToolHelper.getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar())
+            .addClasspathFiles(kotlinc.getKotlinReflectJar())
+            .addClasspathFiles(kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(jarMap.getForConfiguration(kotlinc, targetVersion))
             .addKeepRules("-keep public class " + PKG_NESTED_REFLECT + ".Outer$Nested { *; }")
             .addKeepRules("-keep public class " + PKG_NESTED_REFLECT + ".Outer$Inner { *; }")
@@ -93,9 +91,9 @@
   public void testMetadataOuterNotRenamed() throws Exception {
     Path mainJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
-            .addClasspathFiles(ToolHelper.getKotlinReflectJar(kotlinc))
-            .addClasspathFiles(ToolHelper.getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar())
+            .addClasspathFiles(kotlinc.getKotlinReflectJar())
+            .addClasspathFiles(kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(jarMap.getForConfiguration(kotlinc, targetVersion))
             .addKeepAttributeInnerClassesAndEnclosingMethod()
             .addKeepRules("-keep public class " + PKG_NESTED_REFLECT + ".Outer { *; }")
@@ -118,8 +116,7 @@
             ? new ClassFileConsumer.ArchiveConsumer(output, true)
             : new ArchiveConsumer(output, true);
     testForD8(parameters.getBackend())
-        .addProgramFiles(
-            ToolHelper.getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc), jar)
+        .addProgramFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), jar)
         .setMinApi(parameters.getApiLevel())
         .setProgramConsumer(programConsumer)
         .addOptionsModification(
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteJvmStaticTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteJvmStaticTest.java
index 2c378c6..cd667d3 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteJvmStaticTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteJvmStaticTest.java
@@ -3,15 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 
+import com.android.tools.r8.KotlinCompilerTool.KotlinTargetVersion;
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.kotlin.metadata.jvmstatic_app.MainJava;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -70,7 +68,7 @@
             .compile();
     testForJvm()
         .addRunClasspathFiles(
-            getKotlinStdlibJar(kotlinc), kotlincLibJar.getForConfiguration(kotlinc, targetVersion))
+            kotlinc.getKotlinStdlibJar(), kotlincLibJar.getForConfiguration(kotlinc, targetVersion))
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG_APP + ".MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -80,7 +78,7 @@
   public void smokeTestJava() throws Exception {
     testForJvm()
         .addRunClasspathFiles(
-            getKotlinStdlibJar(kotlinc), kotlincLibJar.getForConfiguration(kotlinc, targetVersion))
+            kotlinc.getKotlinStdlibJar(), kotlincLibJar.getForConfiguration(kotlinc, targetVersion))
         .addProgramClassFileData(MainJava.dump())
         .run(parameters.getRuntime(), MainJava.class)
         .assertSuccessWithOutput(EXPECTED);
@@ -91,7 +89,7 @@
     Path libJar =
         testForR8(parameters.getBackend())
             .addProgramFiles(kotlincLibJar.getForConfiguration(kotlinc, targetVersion))
-            .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addKeepAllClassesRule()
             .addKeepAttributes(
                 ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS,
@@ -113,7 +111,7 @@
             .setOutputPath(temp.newFolder().toPath())
             .compile();
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG_APP + ".MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -121,7 +119,7 @@
 
   private void testJava(Path libJar) throws Exception {
     testForJvm()
-        .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addProgramClassFileData(MainJava.dump())
         .run(parameters.getRuntime(), MainJava.class)
         .assertSuccessWithOutput(EXPECTED);
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 8d01e2e..07faab6 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
@@ -4,8 +4,6 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -51,7 +49,7 @@
   public void testProgramPath() throws Exception {
     testForR8(parameters.getBackend())
         .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
-        .addProgramFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+        .addProgramFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
         .addKeepRules("-keep class " + LIB_CLASS_NAME)
         .applyIf(keepMetadata, TestShrinkerBuilder::addKeepKotlinMetadata)
         .addKeepRuntimeVisibleAnnotations()
@@ -65,7 +63,7 @@
   public void testClassPathPath() throws Exception {
     testForR8(parameters.getBackend())
         .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
-        .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+        .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
         .addKeepRules("-keep class " + LIB_CLASS_NAME)
         .addKeepRuntimeVisibleAnnotations()
         .compile()
@@ -76,7 +74,7 @@
   public void testLibraryPath() throws Exception {
     testForR8(parameters.getBackend())
         .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
-        .addLibraryFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+        .addLibraryFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
         .addLibraryFiles(ToolHelper.getJava8RuntimeJar())
         .addKeepRules("-keep class " + LIB_CLASS_NAME)
         .addKeepRuntimeVisibleAnnotations()
@@ -88,7 +86,7 @@
   public void testMissing() throws Exception {
     testForR8(parameters.getBackend())
         .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
-        .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+        .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
         .addKeepRules("-keep class " + LIB_CLASS_NAME)
         .addKeepRuntimeVisibleAnnotations()
         .compile()
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 b49f740..68731b5 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,8 +4,6 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
@@ -40,7 +38,7 @@
   @Test
   public void testR8() throws Exception {
     testForR8(parameters.getBackend())
-        .addProgramFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+        .addProgramFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
         .setMinApi(parameters.getApiLevel())
         .addKeepKotlinMetadata()
         .addKeepRules("-keep class kotlin.io.** { *; }")
@@ -54,7 +52,7 @@
   @Test
   public void testR8KeepIf() throws Exception {
     testForR8(parameters.getBackend())
-        .addProgramFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+        .addProgramFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
         .setMinApi(parameters.getApiLevel())
         .addKeepRules("-keep class kotlin.io.** { *; }")
         .addKeepRules("-if class *", "-keep class kotlin.Metadata { *; }")
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java
index b175132..64e7bd8 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java
@@ -4,16 +4,14 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 
+import com.android.tools.r8.KotlinCompilerTool.KotlinTargetVersion;
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -47,7 +45,7 @@
   public void testKotlinStdLib() throws Exception {
     assumeFalse(parameters.isNoneRuntime());
     testForR8(parameters.getBackend())
-        .addProgramFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+        .addProgramFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
         .setMinApi(parameters.getApiLevel())
         .addKeepAllClassesRule()
         .addKeepKotlinMetadata()
@@ -61,7 +59,7 @@
         .inspect(
             inspector ->
                 assertEqualMetadata(
-                    new CodeInspector(getKotlinStdlibJar(kotlinc)),
+                    new CodeInspector(kotlinc.getKotlinStdlibJar()),
                     inspector,
                     (addedStrings, addedNonInitStrings) -> {
                       assertEquals(0, addedStrings.intValue());
@@ -73,13 +71,13 @@
   public void testKotlinStdLibD8() throws Exception {
     assumeTrue(parameters.isNoneRuntime());
     testForD8(Backend.DEX)
-        .addProgramFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+        .addProgramFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
         .setMinApi(AndroidApiLevel.B)
         .compile()
         .inspect(
             inspector ->
                 assertEqualMetadata(
-                    new CodeInspector(getKotlinStdlibJar(kotlinc)),
+                    new CodeInspector(kotlinc.getKotlinStdlibJar()),
                     inspector,
                     (addedStrings, addedNonInitStrings) -> {
                       assertEquals(0, addedStrings.intValue());
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
index 9b8a8e7..0cf8903 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
@@ -12,7 +12,6 @@
 
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -63,7 +62,7 @@
             .setOutputPath(temp.newFolder().toPath())
             .compile();
     testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG_APP + ".MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -74,8 +73,7 @@
     Path libJar =
         testForR8(parameters.getBackend())
             .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
-            .addClasspathFiles(
-                ToolHelper.getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addKeepRules(
                 "-keep class " + PKG_LIB + ".Sub { <init>(); *** kept(); *** keptProperty; }")
             .addKeepRules("-neverinline class * { @" + PKG_LIB + ".NeverInline *; }")
@@ -93,7 +91,7 @@
                 getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main"))
             .compile();
     testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
         .addProgramFiles(output)
         .run(parameters.getRuntime(), PKG_APP + ".MainKt")
         .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteUnitPrimitiveTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteUnitPrimitiveTest.java
index fafb083..07e22c5 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteUnitPrimitiveTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteUnitPrimitiveTest.java
@@ -5,10 +5,7 @@
 package com.android.tools.r8.kotlin.metadata;
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
-import static com.android.tools.r8.ToolHelper.KotlinTargetVersion.JAVA_8;
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinReflectJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinTargetVersion.JAVA_8;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -16,7 +13,6 @@
 
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.kotlin.KotlinMetadataWriter;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -87,8 +83,8 @@
             .compile();
     testForJvm()
         .addRunClasspathFiles(
-            getKotlinStdlibJar(kotlinc),
-            getKotlinReflectJar(kotlinc),
+            kotlinc.getKotlinStdlibJar(),
+            kotlinc.getKotlinReflectJar(),
             kotlincLibJar.getForConfiguration(kotlinc, targetVersion))
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG_APP + ".MainKt")
@@ -99,10 +95,10 @@
   public void testMetadataForLib() throws Exception {
     Path libJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(
                 kotlincLibJar.getForConfiguration(kotlinc, targetVersion),
-                getKotlinStdlibJar(kotlinc))
+                kotlinc.getKotlinStdlibJar())
             .addKeepClassAndMembersRules(PKG_LIB + ".*")
             .addKeepAttributes(
                 ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS,
@@ -124,8 +120,7 @@
             .setOutputPath(temp.newFolder().toPath())
             .compile();
     testForJvm()
-        .addRunClasspathFiles(
-            getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), libJar)
         .addClasspath(main)
         .run(parameters.getRuntime(), PKG_APP + ".MainKt")
         .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteValueClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteValueClassTest.java
index cc26b5c..29fa8ec 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteValueClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteValueClassTest.java
@@ -5,9 +5,7 @@
 package com.android.tools.r8.kotlin.metadata;
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_5_0;
-import static com.android.tools.r8.ToolHelper.KotlinTargetVersion.JAVA_8;
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinTargetVersion.JAVA_8;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.core.StringContains.containsString;
@@ -15,7 +13,6 @@
 
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.kotlin.KotlinMetadataWriter;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -72,7 +69,7 @@
             .compile();
     testForJvm()
         .addRunClasspathFiles(
-            getKotlinStdlibJar(kotlinc), kotlincLibJar.getForConfiguration(kotlinc, targetVersion))
+            kotlinc.getKotlinStdlibJar(), kotlincLibJar.getForConfiguration(kotlinc, targetVersion))
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG_APP + ".MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -82,7 +79,7 @@
   public void testMetadataForLib() throws Exception {
     Path libJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(kotlincLibJar.getForConfiguration(kotlinc, targetVersion))
             .addKeepAllClassesRule()
             .addKeepAttributes(
@@ -105,8 +102,7 @@
             .setOutputPath(temp.newFolder().toPath())
             .compile();
     testForJvm()
-        .addRunClasspathFiles(
-            getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc), libJar)
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), libJar)
         .addClasspath(main)
         .run(parameters.getRuntime(), PKG_APP + ".MainKt")
         .assertSuccessWithOutput(EXPECTED);
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 9490133..5d715f1 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
@@ -4,8 +4,6 @@
 package com.android.tools.r8.kotlin.metadata;
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72;
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinReflectJar;
 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;
@@ -54,7 +52,7 @@
         testForR8(parameters.getBackend())
             .addProgramFiles(compiledJars.getForConfiguration(kotlinc, targetVersion))
             .addProgramFiles(getJavaJarFile(FOLDER))
-            .addProgramFiles(getKotlinReflectJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addProgramFiles(kotlinc.getKotlinReflectJar(), kotlinc.getKotlinAnnotationJar())
             .addKeepMainRule(mainClassName)
             .addKeepKotlinMetadata()
             .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java
index b0ea3d9..84139d9 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java
@@ -4,17 +4,15 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 import static org.objectweb.asm.Opcodes.ASM7;
 
 import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
+import com.android.tools.r8.KotlinCompilerTool.KotlinTargetVersion;
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.graph.DexAnnotationElement;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
@@ -61,7 +59,7 @@
     final R8FullTestBuilder testBuilder = testForR8(parameters.getBackend());
     rewriteMetadataVersion(testBuilder::addProgramClassFileData, new int[] {1, 1, 16});
     testBuilder
-        .addProgramFiles(getKotlinAnnotationJar(kotlinc))
+        .addProgramFiles(kotlinc.getKotlinAnnotationJar())
         .setMinApi(parameters.getApiLevel())
         .addOptionsModification(options -> options.testing.keepMetadataInR8IfNotRewritten = false)
         .addKeepAllClassesRuleWithAllowObfuscation()
@@ -75,7 +73,7 @@
     final R8FullTestBuilder testBuilder = testForR8(parameters.getBackend());
     rewriteMetadataVersion(testBuilder::addProgramClassFileData, new int[] {1, 4, 0});
     testBuilder
-        .addProgramFiles(getKotlinAnnotationJar(kotlinc))
+        .addProgramFiles(kotlinc.getKotlinAnnotationJar())
         .setMinApi(parameters.getApiLevel())
         .addKeepAllClassesRuleWithAllowObfuscation()
         .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
@@ -88,7 +86,7 @@
     final R8FullTestBuilder testBuilder = testForR8(parameters.getBackend());
     rewriteMetadataVersion(testBuilder::addProgramClassFileData, new int[] {1, 4, 2});
     testBuilder
-        .addProgramFiles(getKotlinAnnotationJar(kotlinc))
+        .addProgramFiles(kotlinc.getKotlinAnnotationJar())
         .setMinApi(parameters.getApiLevel())
         .addKeepAllClassesRuleWithAllowObfuscation()
         .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
@@ -99,7 +97,7 @@
   private void rewriteMetadataVersion(Consumer<byte[]> rewrittenBytesConsumer, int[] newVersion)
       throws IOException {
     ZipUtils.iter(
-        ToolHelper.getKotlinStdlibJar(kotlinc).toString(),
+        kotlinc.getKotlinStdlibJar().toString(),
         ((entry, input) -> {
           if (!entry.getName().endsWith(".class")) {
             return;
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 ca9fbd2..b14aeb4 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
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.kotlin.optimize.switches;
 
-import static com.android.tools.r8.ToolHelper.getMostRecentKotlinAnnotationJar;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -53,7 +52,7 @@
   public void test() throws Exception {
     testForR8(parameters.getBackend())
         .addProgramFiles(
-            kotlinJars.getForConfiguration(kotlinParameters), getMostRecentKotlinAnnotationJar())
+            kotlinJars.getForConfiguration(kotlinParameters), kotlinc.getKotlinAnnotationJar())
         .addKeepMainRule("enumswitch.EnumSwitchKt")
         .addOptionsModification(
             options -> {
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 d448e96..1ad87d5 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
@@ -57,8 +57,8 @@
     assumeTrue(parameters.isCfRuntime());
     testForJvm()
         .addProgramFiles(compiledJars.getForConfiguration(kotlinc, targetVersion))
-        .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
-        .addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinc))
+        .addProgramFiles(kotlinc.getKotlinStdlibJar())
+        .addProgramFiles(kotlinc.getKotlinReflectJar())
         .run(parameters.getRuntime(), PKG + ".SimpleReflectKt")
         .assertSuccessWithOutputLines(EXPECTED_OUTPUT);
   }
@@ -69,8 +69,8 @@
     final File output = temp.newFile("output.zip");
     testForD8(parameters.getBackend())
         .addProgramFiles(compiledJars.getForConfiguration(kotlinc, targetVersion))
-        .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
-        .addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinc))
+        .addProgramFiles(kotlinc.getKotlinStdlibJar())
+        .addProgramFiles(kotlinc.getKotlinReflectJar())
         .setProgramConsumer(new ArchiveConsumer(output.toPath(), true))
         .setMinApi(parameters.getApiLevel())
         .addOptionsModification(
@@ -87,9 +87,9 @@
     final File foo = temp.newFile("foo");
     testForR8(parameters.getBackend())
         .addProgramFiles(compiledJars.getForConfiguration(kotlinc, targetVersion))
-        .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
-        .addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinc))
-        .addProgramFiles(ToolHelper.getKotlinAnnotationJar(kotlinc))
+        .addProgramFiles(kotlinc.getKotlinStdlibJar())
+        .addProgramFiles(kotlinc.getKotlinReflectJar())
+        .addProgramFiles(kotlinc.getKotlinAnnotationJar())
         .setMinApi(parameters.getApiLevel())
         .addKeepAllClassesRule()
         .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
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 3422030..5bbd532 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,8 +5,6 @@
 package com.android.tools.r8.kotlin.sealed;
 
 import static com.android.tools.r8.ToolHelper.getFilesInTestFolderRelativeToClass;
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static org.hamcrest.CoreMatchers.equalTo;
 
 import com.android.tools.r8.CompilationFailedException;
@@ -58,7 +56,7 @@
   public void testRuntime() throws ExecutionException, CompilationFailedException, IOException {
     testForRuntime(parameters)
         .addProgramFiles(compilationResults.getForConfiguration(kotlinc, targetVersion))
-        .addRunClasspathFiles(buildOnDexRuntime(parameters, getKotlinStdlibJar(kotlinc)))
+        .addRunClasspathFiles(buildOnDexRuntime(parameters, kotlinc.getKotlinStdlibJar()))
         .run(parameters.getRuntime(), MAIN)
         .assertSuccessWithOutputLines(EXPECTED);
   }
@@ -67,8 +65,8 @@
   public void testR8() throws ExecutionException, CompilationFailedException, IOException {
     testForR8(parameters.getBackend())
         .addProgramFiles(compilationResults.getForConfiguration(kotlinc, targetVersion))
-        .addProgramFiles(buildOnDexRuntime(parameters, getKotlinStdlibJar(kotlinc)))
-        .addProgramFiles(getKotlinAnnotationJar(kotlinc))
+        .addProgramFiles(buildOnDexRuntime(parameters, kotlinc.getKotlinStdlibJar()))
+        .addProgramFiles(kotlinc.getKotlinAnnotationJar())
         .setMinApi(parameters.getApiLevel())
         .allowAccessModification()
         .allowDiagnosticWarningMessages()
diff --git a/src/test/java/com/android/tools/r8/maindexlist/warnings/MainDexWarningsTest.java b/src/test/java/com/android/tools/r8/maindexlist/warnings/MainDexWarningsTest.java
index 19e75b3..76e19f6 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/warnings/MainDexWarningsTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/warnings/MainDexWarningsTest.java
@@ -4,11 +4,10 @@
 
 package com.android.tools.r8.maindexlist.warnings;
 
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.MatcherAssert.assertThat;
 
-import com.android.tools.r8.ForceInline;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
@@ -16,7 +15,6 @@
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.google.common.collect.ImmutableList;
 import java.util.List;
-import org.hamcrest.CoreMatchers;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -45,8 +43,8 @@
   }
 
   private void classStaticGone(CodeInspector inspector) {
-    assertThat(inspector.clazz(Static.class), CoreMatchers.not(isPresent()));
-    assertThat(inspector.clazz(Static2.class), CoreMatchers.not(isPresent()));
+    assertThat(inspector.clazz(Static.class), isAbsent());
+    assertThat(inspector.clazz(Static2.class), isAbsent());
   }
 
   @Test
@@ -57,7 +55,6 @@
         .addKeepMainRule(mainClass)
         // Include main dex rule for class Static.
         .addMainDexKeepClassRules(Main.class, Static.class)
-        .enableForceInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::classStaticGone)
@@ -74,7 +71,6 @@
         // Include explicit main dex entry for class Static.
         .addMainDexListClasses(Static.class)
         .allowDiagnosticWarningMessages()
-        .enableForceInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::classStaticGone)
@@ -96,7 +92,6 @@
         .addMainDexKeepClassRules(Static2.class)
         .addDontWarn(Static.class)
         .allowDiagnosticWarningMessages()
-        .enableForceInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::classStaticGone)
@@ -116,14 +111,12 @@
 }
 
 class Static {
-  @ForceInline
   public static int m() {
     return 1;
   }
 }
 
 class Static2 {
-  @ForceInline
   public static int m() {
     return 1;
   }
diff --git a/src/test/java/com/android/tools/r8/movestringconstants/MoveStringConstantsTest.java b/src/test/java/com/android/tools/r8/movestringconstants/MoveStringConstantsTest.java
index 61387ea..eaac9ca 100644
--- a/src/test/java/com/android/tools/r8/movestringconstants/MoveStringConstantsTest.java
+++ b/src/test/java/com/android/tools/r8/movestringconstants/MoveStringConstantsTest.java
@@ -4,11 +4,14 @@
 
 package com.android.tools.r8.movestringconstants;
 
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertEquals;
+import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
 
+import com.android.tools.r8.AlwaysInline;
 import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.ForceInline;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.TestBase;
@@ -41,21 +44,20 @@
 
   private void runTest(Consumer<CodeInspector> inspection) throws Exception {
     R8Command.Builder builder = R8Command.builder();
-    builder.addProgramFiles(ToolHelper.getClassFileForTestClass(ForceInline.class));
     builder.addProgramFiles(ToolHelper.getClassFileForTestClass(NeverInline.class));
     builder.addProgramFiles(ToolHelper.getClassFileForTestClass(TestClass.class));
     builder.addProgramFiles(ToolHelper.getClassFileForTestClass(Utils.class));
+    builder.addProgramFiles(ToolHelper.getClassFileForTestClass(AlwaysInline.class));
     builder.addLibraryFiles(runtimeJar(backend));
     builder.setProgramConsumer(emptyConsumer(backend));
     builder.setMode(CompilationMode.RELEASE);
     builder.addProguardConfiguration(
         ImmutableList.of(
-            "-forceinline class * { @com.android.tools.r8.ForceInline *; }",
+            "-alwaysinline class * { @com.android.tools.r8.AlwaysInline *; }",
             "-neverinline class * { @com.android.tools.r8.NeverInline *; }",
             "-keep class " + TestClass.class.getCanonicalName() + "{ *; }",
             "-dontobfuscate",
-            "-allowaccessmodification"
-        ),
+            "-allowaccessmodification"),
         Origin.unknown());
     ToolHelper.allowTestProguardOptions(builder);
     AndroidApp app = ToolHelper.runR8(builder.build());
@@ -125,6 +127,11 @@
         insn -> insn.isConstString("StringConstants::foo#1", JumboStringMode.DISALLOW),
         InstructionSubject::isInvokeStatic,
         InstructionSubject::isThrow);
+
+    ClassSubject utilsClassSubject = inspector.clazz(Utils.class);
+    assertThat(utilsClassSubject, isPresent());
+    assertThat(utilsClassSubject.uniqueMethodWithName("throwException"), isPresent());
+    assertEquals(1, utilsClassSubject.allMethods().size());
   }
 
   @SafeVarargs
diff --git a/src/test/java/com/android/tools/r8/movestringconstants/TestClass.java b/src/test/java/com/android/tools/r8/movestringconstants/TestClass.java
index 2af704d..7df59d9 100644
--- a/src/test/java/com/android/tools/r8/movestringconstants/TestClass.java
+++ b/src/test/java/com/android/tools/r8/movestringconstants/TestClass.java
@@ -4,7 +4,7 @@
 
 package com.android.tools.r8.movestringconstants;
 
-import com.android.tools.r8.ForceInline;
+import com.android.tools.r8.AlwaysInline;
 import com.android.tools.r8.NeverInline;
 
 public class TestClass {
@@ -30,7 +30,7 @@
 }
 
 class Utils {
-  @ForceInline
+  @AlwaysInline
   static void check(Object value, String message) {
     if (value == null) {
       throwException(message);
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 80d865e..1652222 100644
--- a/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.naming;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
 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;
@@ -56,7 +55,7 @@
         testForR8(parameters.getBackend())
             .addProgramFiles(
                 compiledJars.getForConfiguration(kotlinc, targetVersion),
-                getKotlinAnnotationJar(kotlinc))
+                kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(getJavaJarFile(FOLDER))
             .addKeepMainRule(MAIN_CLASS_NAME)
             .addKeepClassRulesWithAllowObfuscation(ENUM_CLASS_NAME)
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 c129553..e6f1c6d 100644
--- a/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
+++ b/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.naming;
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72;
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -94,7 +93,7 @@
         testForR8(Backend.DEX)
             .addProgramFiles(
                 compiledJars.getForConfiguration(kotlinc, targetVersion),
-                getKotlinAnnotationJar(kotlinc))
+                kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(getJavaJarFile(FOLDER))
             .addKeepMainRule(mainClassName)
             .addKeepRules(
@@ -154,7 +153,7 @@
         testForR8(Backend.DEX)
             .addProgramFiles(
                 compiledJars.getForConfiguration(kotlinc, targetVersion),
-                getKotlinAnnotationJar(kotlinc))
+                kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(getJavaJarFile(FOLDER))
             .enableProguardTestOptions()
             .addKeepMainRule(mainClassName)
diff --git a/src/test/java/com/android/tools/r8/naming/b139991218/TestRunner.java b/src/test/java/com/android/tools/r8/naming/b139991218/TestRunner.java
index 22a3470..559f6a0 100644
--- a/src/test/java/com/android/tools/r8/naming/b139991218/TestRunner.java
+++ b/src/test/java/com/android/tools/r8/naming/b139991218/TestRunner.java
@@ -4,16 +4,15 @@
 
 package com.android.tools.r8.naming.b139991218;
 
-import static com.android.tools.r8.ToolHelper.getMostRecentKotlinAnnotationJar;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.KotlinCompilerTool.KotlinTargetVersion;
 import com.android.tools.r8.KotlinTestBase;
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.ir.optimize.Inliner.Reason;
 import com.google.common.collect.ImmutableSet;
 import java.io.IOException;
@@ -62,7 +61,7 @@
     testForR8(parameters.getBackend())
         .addProgramClassFileData(Lambda1.dump(), Lambda2.dump(), Main.dump(), Alpha.dump())
         .addProgramFiles(
-            kotlinJars.getForConfiguration(kotlinParameters), getMostRecentKotlinAnnotationJar())
+            kotlinJars.getForConfiguration(kotlinParameters), kotlinc.getKotlinAnnotationJar())
         .addKeepMainRule(Main.class)
         .addKeepAllAttributes()
         .addOptionsModification(
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/InliningRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/InliningRetraceTest.java
index 344a716..be514ec 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/InliningRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/InliningRetraceTest.java
@@ -12,8 +12,6 @@
 import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.ForceInline;
-import com.android.tools.r8.R8TestBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -83,13 +81,6 @@
   }
 
   @Override
-  public void configure(R8TestBuilder<?> builder) {
-    builder
-        .addForceInliningAnnotations()
-        .applyIf(mode == CompilationMode.RELEASE, R8TestBuilder::enableForceInliningAnnotations);
-  }
-
-  @Override
   public void inspect(CodeInspector inspector) {
     if (mode == CompilationMode.RELEASE) {
       assertEquals(compat ? 2 : 1, inspector.clazz(Main.class).allMethods().size());
@@ -99,13 +90,11 @@
 
 class Main {
 
-  @ForceInline
   public static void method3(long j) {
     System.out.println("In method3");
     throw null;
   }
 
-  @ForceInline
   public static void method2(int j) {
     System.out.println("In method2");
     for (int i = 0; i < 10; i++) {
@@ -113,7 +102,6 @@
     }
   }
 
-  @ForceInline
   public static void method1(String s) {
     System.out.println("In method1");
     for (int i = 0; i < 10; i++) {
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/LineDeltaTest.java b/src/test/java/com/android/tools/r8/naming/retrace/LineDeltaTest.java
index 2583b6e..d91a506 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/LineDeltaTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/LineDeltaTest.java
@@ -6,49 +6,60 @@
 
 import static org.junit.Assert.assertEquals;
 
-import com.android.tools.r8.ForceInline;
 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 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 LineDeltaTest extends TestBase {
-  public String runTest(Backend backend) throws Exception {
-    return testForR8(backend)
-        .enableForceInliningAnnotations()
-        .addProgramClasses(LineDeltaTestClass.class)
-        .addKeepMainRule(LineDeltaTestClass.class)
-        .addKeepRules("-keepattributes LineNumberTable")
-        .run(LineDeltaTestClass.class)
-        .assertSuccessWithOutput(
-            StringUtils.lines(
-                "In test1() - 1",
-                "In test1() - 2",
-                "In test1() - 3",
-                "In test1() - 4",
-                "In test2() - 1",
-                "In test2() - 2",
-                "In test2() - 3",
-                "In test2() - 4"))
-        .proguardMap();
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection parameters() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    String proguardMap =
+        testForR8(parameters.getBackend())
+            .addProgramClasses(LineDeltaTestClass.class)
+            .addKeepMainRule(LineDeltaTestClass.class)
+            .addKeepRules("-keepattributes LineNumberTable")
+            .setMinApi(parameters.getApiLevel())
+            .compile()
+            .inspect(
+                inspector ->
+                    assertEquals(1, inspector.clazz(LineDeltaTestClass.class).allMethods().size()))
+            .run(parameters.getRuntime(), LineDeltaTestClass.class)
+            .assertSuccessWithOutput(
+                StringUtils.lines(
+                    "In test1() - 1",
+                    "In test1() - 2",
+                    "In test1() - 3",
+                    "In test1() - 4",
+                    "In test2() - 1",
+                    "In test2() - 2",
+                    "In test2() - 3",
+                    "In test2() - 4"))
+            .proguardMap();
+    assertEquals(parameters.isCfRuntime() ? 5 : 17, mapLines(proguardMap));
   }
 
   private long mapLines(String map) {
     return StringUtils.splitLines(map).stream().filter(line -> !line.startsWith("#")).count();
   }
-
-  @Test
-  public void testDex() throws Exception {
-    assertEquals(17, mapLines(runTest(Backend.DEX)));
-  }
-
-  @Test
-  public void testCf() throws Exception {
-    assertEquals(5, mapLines(runTest(Backend.CF)));
-  }
 }
 
 class LineDeltaTestClass {
-  @ForceInline
   static void test1() {
     System.out.println("In test1() - 1");
     // One line comment.
@@ -63,7 +74,6 @@
     System.out.println("In test1() - 4");
   }
 
-  @ForceInline
   static void test2() {
     System.out.println("In test2() - 1");
     // Seven line comments.
diff --git a/src/test/java/com/android/tools/r8/naming/retraceproguard/InliningRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retraceproguard/InliningRetraceTest.java
index 8d94fc8..ae6a985 100644
--- a/src/test/java/com/android/tools/r8/naming/retraceproguard/InliningRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retraceproguard/InliningRetraceTest.java
@@ -11,7 +11,6 @@
 import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.ForceInline;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.utils.BooleanUtils;
@@ -85,13 +84,11 @@
 
 class Main {
 
-  @ForceInline
   public static void method3(long j) {
     System.out.println("In method3");
     throw null;
   }
 
-  @ForceInline
   public static void method2(int j) {
     System.out.println("In method2");
     for (int i = 0; i < 10; i++) {
@@ -99,7 +96,6 @@
     }
   }
 
-  @ForceInline
   public static void method1(String s) {
     System.out.println("In method1");
     for (int i = 0; i < 10; i++) {
diff --git a/src/test/java/com/android/tools/r8/naming/retraceproguard/LineDeltaTest.java b/src/test/java/com/android/tools/r8/naming/retraceproguard/LineDeltaTest.java
index a96ad88..273adb0 100644
--- a/src/test/java/com/android/tools/r8/naming/retraceproguard/LineDeltaTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retraceproguard/LineDeltaTest.java
@@ -6,49 +6,60 @@
 
 import static org.junit.Assert.assertEquals;
 
-import com.android.tools.r8.ForceInline;
 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 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 LineDeltaTest extends TestBase {
-  public String runTest(Backend backend) throws Exception {
-    return testForR8(backend)
-        .enableForceInliningAnnotations()
-        .addProgramClasses(LineDeltaTestClass.class)
-        .addKeepMainRule(LineDeltaTestClass.class)
-        .addKeepRules("-keepattributes LineNumberTable")
-        .run(LineDeltaTestClass.class)
-        .assertSuccessWithOutput(
-            StringUtils.lines(
-                "In test1() - 1",
-                "In test1() - 2",
-                "In test1() - 3",
-                "In test1() - 4",
-                "In test2() - 1",
-                "In test2() - 2",
-                "In test2() - 3",
-                "In test2() - 4"))
-        .proguardMap();
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection parameters() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    String proguardMap =
+        testForR8(parameters.getBackend())
+            .addProgramClasses(LineDeltaTestClass.class)
+            .addKeepMainRule(LineDeltaTestClass.class)
+            .addKeepRules("-keepattributes LineNumberTable")
+            .setMinApi(parameters.getApiLevel())
+            .compile()
+            .inspect(
+                inspector ->
+                    assertEquals(1, inspector.clazz(LineDeltaTestClass.class).allMethods().size()))
+            .run(parameters.getRuntime(), LineDeltaTestClass.class)
+            .assertSuccessWithOutput(
+                StringUtils.lines(
+                    "In test1() - 1",
+                    "In test1() - 2",
+                    "In test1() - 3",
+                    "In test1() - 4",
+                    "In test2() - 1",
+                    "In test2() - 2",
+                    "In test2() - 3",
+                    "In test2() - 4"))
+            .proguardMap();
+    assertEquals(parameters.isCfRuntime() ? 5 : 17, mapLines(proguardMap));
   }
 
   private long mapLines(String map) {
     return StringUtils.splitLines(map).stream().filter(line -> !line.startsWith("#")).count();
   }
-
-  @Test
-  public void testDex() throws Exception {
-    assertEquals(17, mapLines(runTest(Backend.DEX)));
-  }
-
-  @Test
-  public void testCf() throws Exception {
-    assertEquals(5, mapLines(runTest(Backend.CF)));
-  }
 }
 
 class LineDeltaTestClass {
-  @ForceInline
   static void test1() {
     System.out.println("In test1() - 1");
     // One line comment.
@@ -63,7 +74,6 @@
     System.out.println("In test1() - 4");
   }
 
-  @ForceInline
   static void test2() {
     System.out.println("In test2() - 1");
     // Seven line comments.
diff --git a/src/test/java/com/android/tools/r8/naming/retraceproguard/RetraceTestBase.java b/src/test/java/com/android/tools/r8/naming/retraceproguard/RetraceTestBase.java
index 62c4de9..c84c685 100644
--- a/src/test/java/com/android/tools/r8/naming/retraceproguard/RetraceTestBase.java
+++ b/src/test/java/com/android/tools/r8/naming/retraceproguard/RetraceTestBase.java
@@ -53,7 +53,6 @@
             .setMode(mode)
             .enableProguardTestOptions()
             .addProgramClasses(getClasses())
-            .addForceInliningAnnotations()
             .addKeepMainRule(getMainClass())
             .addKeepRules(keepRules)
             .apply(this::configure)
diff --git a/src/test/java/com/android/tools/r8/neverreturnsnormally/NeverReturnsNormallyTest.java b/src/test/java/com/android/tools/r8/neverreturnsnormally/NeverReturnsNormallyTest.java
index 42cd37e..d2e1d70 100644
--- a/src/test/java/com/android/tools/r8/neverreturnsnormally/NeverReturnsNormallyTest.java
+++ b/src/test/java/com/android/tools/r8/neverreturnsnormally/NeverReturnsNormallyTest.java
@@ -9,7 +9,6 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.ForceInline;
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -56,14 +55,12 @@
       BiConsumer<CodeInspector, CompilationMode> inspection,
       boolean enableClassInliner, CompilationMode mode) throws Exception {
     R8Command.Builder builder = R8Command.builder();
-    builder.addProgramFiles(ToolHelper.getClassFileForTestClass(ForceInline.class));
     builder.addProgramFiles(ToolHelper.getClassFileForTestClass(TestClass.class));
     builder.setProgramConsumer(emptyConsumer(parameters.getBackend()));
     builder.addLibraryFiles(runtimeJar(parameters.getBackend()));
     builder.setMode(mode);
     builder.addProguardConfiguration(
         ImmutableList.of(
-            "-forceinline class * { @com.android.tools.r8.ForceInline *; }",
             "-keep class " + TestClass.class.getTypeName() + " {",
             "  public static void main(java.lang.String[]);",
             "  *** test*(...);",
diff --git a/src/test/java/com/android/tools/r8/neverreturnsnormally/TestClass.java b/src/test/java/com/android/tools/r8/neverreturnsnormally/TestClass.java
index e354c48..ed62754 100644
--- a/src/test/java/com/android/tools/r8/neverreturnsnormally/TestClass.java
+++ b/src/test/java/com/android/tools/r8/neverreturnsnormally/TestClass.java
@@ -4,8 +4,6 @@
 
 package com.android.tools.r8.neverreturnsnormally;
 
-import com.android.tools.r8.ForceInline;
-
 public class TestClass {
   public static boolean throwNpe(String message) {
     String newMessage = "prefix:" + message + ":suffix";
@@ -13,7 +11,6 @@
     throw new NullPointerException(newMessage);
   }
 
-  @ForceInline
   public static int throwToBeInlined() {
     throwNpe("throwToBeInlined");
     return "Nobody cares".length();
diff --git a/src/test/java/com/android/tools/r8/regress/b191296688/Regress191296688.java b/src/test/java/com/android/tools/r8/regress/b191296688/Regress191296688.java
index 8410836..f3b4ad9 100644
--- a/src/test/java/com/android/tools/r8/regress/b191296688/Regress191296688.java
+++ b/src/test/java/com/android/tools/r8/regress/b191296688/Regress191296688.java
@@ -4,20 +4,18 @@
 
 package com.android.tools.r8.regress.b191296688;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static com.android.tools.r8.utils.codeinspector.CodeMatchers.isInvokeWithTarget;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
+import com.android.tools.r8.KotlinCompilerTool.KotlinTargetVersion;
 import com.android.tools.r8.KotlinTestBase;
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRuntime;
 import com.android.tools.r8.TestRuntime.CfRuntime;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -64,7 +62,7 @@
             .compile();
     Path desugaredJar =
         testForD8(Backend.CF)
-            .addLibraryFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addLibraryFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(ktClasses)
             .addProgramClasses(A.class)
             .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java b/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
index d0cb2bf..d251e28 100644
--- a/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
+++ b/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
@@ -24,7 +24,7 @@
 
   @Parameterized.Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimes().build();
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
   public Regress69825683Test(TestParameters parameters) {
@@ -48,10 +48,13 @@
             .addKeepRules(
                 "-assumemayhavesideeffects class " + inner.getName() + " {",
                 "  synthetic void <init>(...);",
+                "}",
+                "-keepunusedarguments class " + inner.getName() + " {",
+                "  synthetic void <init>(...);",
                 "}")
             .addOptionsModification(options -> options.enableClassInlining = false)
             .noMinification()
-            .setMinApi(parameters.getRuntime())
+            .setMinApi(parameters.getApiLevel())
             .run(parameters.getRuntime(), outer)
             // Run code to check that the constructor with synthetic class as argument is present.
             .assertSuccessWithOutputThatMatches(startsWith(innerName))
@@ -82,7 +85,7 @@
                 "}")
             .noMinification()
             .addOptionsModification(o -> o.enableClassInlining = false)
-            .setMinApi(parameters.getRuntime())
+            .setMinApi(parameters.getApiLevel())
             // Run code to check that the constructor with synthetic class as argument is present.
             .run(parameters.getRuntime(), clazz)
             .assertSuccessWithOutputThatMatches(startsWith(clazz.getName()))
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java
index 4dcead3..48c4221 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java
@@ -11,7 +11,6 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.TestRuntime;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
@@ -41,7 +40,7 @@
 
   @Test
   public void testResolution() throws Exception {
-    assumeTrue(parameters.getRuntime().equals(TestRuntime.getDefaultJavaRuntime()));
+    assumeTrue(parameters.useRuntimeAsNoneRuntime());
     AppView<AppInfoWithLiveness> appView =
         computeAppViewWithLiveness(
             buildClasses(A.class, I.class)
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 e0eb6ca..3d3331b 100644
--- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
@@ -4,8 +4,6 @@
 package com.android.tools.r8.retrace;
 
 import static com.android.tools.r8.ToolHelper.getFilesInTestFolderRelativeToClass;
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static com.android.tools.r8.utils.codeinspector.Matchers.containsLinePositions;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineFrame;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineStack;
@@ -81,7 +79,7 @@
   public void testRuntime() throws Exception {
     testForRuntime(parameters)
         .addProgramFiles(compilationResults.getForConfiguration(kotlinc, targetVersion))
-        .addRunClasspathFiles(buildOnDexRuntime(parameters, getKotlinStdlibJar(kotlinc)))
+        .addRunClasspathFiles(buildOnDexRuntime(parameters, kotlinc.getKotlinStdlibJar()))
         .run(parameters.getRuntime(), MAIN)
         .assertFailureWithErrorThatMatches(containsString("foo"))
         .assertFailureWithErrorThatMatches(
@@ -96,7 +94,7 @@
     CodeInspector kotlinInspector = new CodeInspector(kotlinSources);
     testForR8(parameters.getBackend())
         .addProgramFiles(compilationResults.getForConfiguration(kotlinc, targetVersion))
-        .addProgramFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+        .addProgramFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
         .addKeepAttributes("SourceFile", "LineNumberTable")
         .setMode(CompilationMode.RELEASE)
         .addKeepMainRule(MAIN)
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 032a370..f1000fb 100644
--- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
@@ -5,8 +5,6 @@
 package com.android.tools.r8.retrace;
 
 import static com.android.tools.r8.ToolHelper.getFilesInTestFolderRelativeToClass;
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
 import static com.android.tools.r8.utils.codeinspector.Matchers.containsLinePositions;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineFrame;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineStack;
@@ -91,7 +89,7 @@
   public void testRuntime() throws Exception {
     testForRuntime(parameters)
         .addProgramFiles(compilationResults.getForConfiguration(kotlinc, targetVersion))
-        .addRunClasspathFiles(buildOnDexRuntime(parameters, getKotlinStdlibJar(kotlinc)))
+        .addRunClasspathFiles(buildOnDexRuntime(parameters, kotlinc.getKotlinStdlibJar()))
         .run(parameters.getRuntime(), "retrace.MainKt")
         .assertFailureWithErrorThatMatches(containsString("inlineExceptionStatic"))
         .assertFailureWithErrorThatMatches(containsString("at retrace.MainKt.main(Main.kt:15)"));
@@ -105,7 +103,7 @@
     CodeInspector kotlinInspector = new CodeInspector(kotlinSources);
     testForR8(parameters.getBackend())
         .addProgramFiles(
-            kotlinSources, getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            kotlinSources, kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
         .addKeepAttributes("SourceFile", "LineNumberTable")
         .allowDiagnosticWarningMessages()
         .setMode(CompilationMode.RELEASE)
@@ -135,7 +133,7 @@
     CodeInspector kotlinInspector = new CodeInspector(kotlinSources);
     testForR8(parameters.getBackend())
         .addProgramFiles(
-            kotlinSources, getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            kotlinSources, kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
         .addKeepAttributes("SourceFile", "LineNumberTable")
         .allowDiagnosticWarningMessages()
         .setMode(CompilationMode.RELEASE)
@@ -168,7 +166,7 @@
     CodeInspector kotlinInspector = new CodeInspector(kotlinSources);
     testForR8(parameters.getBackend())
         .addProgramFiles(
-            kotlinSources, getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            kotlinSources, kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
         .addKeepAttributes("SourceFile", "LineNumberTable")
         .allowDiagnosticWarningMessages()
         .setMode(CompilationMode.RELEASE)
@@ -200,7 +198,7 @@
     CodeInspector kotlinInspector = new CodeInspector(kotlinSources);
     testForR8(parameters.getBackend())
         .addProgramFiles(
-            kotlinSources, getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            kotlinSources, kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
         .addKeepAttributes("SourceFile", "LineNumberTable")
         .allowDiagnosticWarningMessages()
         .setMode(CompilationMode.RELEASE)
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
index 3a8fd5b..d1a074b 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -42,6 +42,7 @@
 import com.android.tools.r8.retrace.stacktraces.NamedModuleStackTrace;
 import com.android.tools.r8.retrace.stacktraces.NoObfuscatedLineNumberWithOverrideTest;
 import com.android.tools.r8.retrace.stacktraces.NoObfuscationRangeMappingWithStackTrace;
+import com.android.tools.r8.retrace.stacktraces.NpeInlineRetraceStackTrace;
 import com.android.tools.r8.retrace.stacktraces.NullStackTrace;
 import com.android.tools.r8.retrace.stacktraces.ObfucatedExceptionClassStackTrace;
 import com.android.tools.r8.retrace.stacktraces.ObfuscatedRangeToSingleLineStackTrace;
@@ -309,6 +310,11 @@
     runRetraceTest(new AmbiguousWithSignatureVerboseStackTrace());
   }
 
+  @Test
+  public void testNpeInlineRetraceStackTrace() throws Exception {
+    runExperimentalRetraceTest(new NpeInlineRetraceStackTrace());
+  }
+
   private void inspectRetraceTest(
       StackTraceForTest stackTraceForTest, Consumer<Retracer> inspection) {
     inspection.accept(
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiRewriteFrameInlineNpeTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiRewriteFrameInlineNpeTest.java
new file mode 100644
index 0000000..41c56cc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiRewriteFrameInlineNpeTest.java
@@ -0,0 +1,113 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace.api;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.RetraceClassElement;
+import com.android.tools.r8.retrace.RetraceFrameElement;
+import com.android.tools.r8.retrace.RetraceStackTraceContext;
+import com.android.tools.r8.retrace.RetracedMethodReference;
+import com.android.tools.r8.retrace.Retracer;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RetraceApiRewriteFrameInlineNpeTest extends RetraceApiTestBase {
+
+  public RetraceApiRewriteFrameInlineNpeTest(TestParameters parameters) {
+    super(parameters);
+  }
+
+  @Override
+  protected Class<? extends RetraceApiBinaryTest> binaryTestClass() {
+    return ApiTest.class;
+  }
+
+  public static class ApiTest implements RetraceApiBinaryTest {
+
+    private final String npeDescriptor = "Ljava/lang/NullPointerException;";
+
+    private final String mapping =
+        "# { id: 'com.android.tools.r8.mapping', version: 'experimental' }\n"
+            + "some.Class -> a:\n"
+            + "  4:4:void other.Class.inlinee():23:23 -> a\n"
+            + "  4:4:void caller(other.Class):7 -> a\n"
+            + "  # { id: 'com.android.tools.r8.rewriteFrame', "
+            + "      conditions: ['throws("
+            + npeDescriptor
+            + ")'], "
+            + "      actions: ['removeInnerFrames(1)']"
+            + "    }";
+
+    @Test
+    public void testFirstStackLineIsRemoved() {
+      TestDiagnosticsHandler testDiagnosticsHandler = new TestDiagnosticsHandler();
+      Retracer retracer =
+          Retracer.createExperimental(
+              ProguardMapProducer.fromString(mapping), testDiagnosticsHandler);
+
+      List<RetraceClassElement> npeRetraced =
+          retracer.retraceClass(Reference.classFromDescriptor(npeDescriptor)).stream()
+              .collect(Collectors.toList());
+      assertEquals(1, npeRetraced.size());
+
+      RetraceStackTraceContext context = npeRetraced.get(0).getContextWhereClassWasThrown();
+
+      List<RetraceFrameElement> retraceFrameElements =
+          retracer.retraceClass(Reference.classFromTypeName("a")).stream()
+              .flatMap(element -> element.lookupFrame(Optional.of(4), "a").stream())
+              .collect(Collectors.toList());
+      assertEquals(1, retraceFrameElements.size());
+
+      RetraceFrameElement retraceFrameElement = retraceFrameElements.get(0);
+      // Check that rewriting the frames will remove the top 1 frames if the condition is active.
+      Map<Integer, RetracedMethodReference> results = new LinkedHashMap<>();
+      retraceFrameElement.visitRewrittenFrames(
+          context,
+          (methodReference, index) -> {
+            RetracedMethodReference existingValue = results.put(index, methodReference);
+            assertNull(existingValue);
+          });
+      assertEquals(1, results.size());
+      assertEquals(7, results.get(0).getOriginalPositionOrDefault(4));
+      assertEquals(results.get(0).getMethodName(), "caller");
+    }
+
+    private static class TestDiagnosticsHandler implements com.android.tools.r8.DiagnosticsHandler {
+
+      private List<Diagnostic> infoMessages = new ArrayList<>();
+
+      @Override
+      public void warning(Diagnostic warning) {
+        throw new RuntimeException("Warning not expected");
+      }
+
+      @Override
+      public void error(Diagnostic error) {
+        throw new RuntimeException("Error not expected");
+      }
+
+      @Override
+      public void info(Diagnostic info) {
+        DiagnosticsHandler.super.info(info);
+        infoMessages.add(info);
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedFrameTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedFrameTest.java
index e1ad580..376a274 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedFrameTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedFrameTest.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.retrace.ProguardMapProducer;
 import com.android.tools.r8.retrace.RetraceFrameElement;
+import com.android.tools.r8.retrace.RetraceStackTraceContext;
 import com.android.tools.r8.retrace.RetracedMethodReference;
 import com.android.tools.r8.retrace.Retracer;
 import java.util.ArrayList;
@@ -57,7 +58,8 @@
       retraceFrameElement.visitAllFrames((method, ignored) -> allFrames.add(method));
       assertEquals(2, allFrames.size());
       List<RetracedMethodReference> nonSyntheticFrames = new ArrayList<>();
-      retraceFrameElement.visitNonCompilerSynthesizedFrames(
+      retraceFrameElement.visitRewrittenFrames(
+          RetraceStackTraceContext.getInitialContext(),
           (method, ignored) -> nonSyntheticFrames.add(method));
       assertEquals(1, nonSyntheticFrames.size());
       assertEquals(nonSyntheticFrames.get(0), allFrames.get(0));
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedInnerFrameTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedInnerFrameTest.java
index 73367a6..c7b8d7a 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedInnerFrameTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedInnerFrameTest.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.retrace.ProguardMapProducer;
 import com.android.tools.r8.retrace.RetraceFrameElement;
+import com.android.tools.r8.retrace.RetraceStackTraceContext;
 import com.android.tools.r8.retrace.RetracedMethodReference;
 import com.android.tools.r8.retrace.Retracer;
 import java.util.ArrayList;
@@ -57,7 +58,8 @@
       retraceFrameElement.visitAllFrames((method, ignored) -> allFrames.add(method));
       assertEquals(2, allFrames.size());
       List<RetracedMethodReference> nonSyntheticFrames = new ArrayList<>();
-      retraceFrameElement.visitNonCompilerSynthesizedFrames(
+      retraceFrameElement.visitRewrittenFrames(
+          RetraceStackTraceContext.getInitialContext(),
           (method, ignored) -> nonSyntheticFrames.add(method));
       assertEquals(allFrames, nonSyntheticFrames);
     }
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestHelper.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestHelper.java
index 9c84754..98ccb02 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestHelper.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestHelper.java
@@ -44,7 +44,8 @@
           RetraceApiSynthesizedMethodTest.ApiTest.class,
           RetraceApiSynthesizedFrameTest.ApiTest.class,
           RetraceApiSynthesizedInnerFrameTest.ApiTest.class,
-          RetraceApiUnknownJsonTest.ApiTest.class);
+          RetraceApiUnknownJsonTest.ApiTest.class,
+          RetraceApiRewriteFrameInlineNpeTest.ApiTest.class);
   public static List<Class<? extends RetraceApiBinaryTest>> CLASSES_PENDING_BINARY_COMPATIBILITY =
       ImmutableList.of();
 
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousMethodVerboseStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousMethodVerboseStackTrace.java
index 5ce9e89..3facf5f 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousMethodVerboseStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousMethodVerboseStackTrace.java
@@ -47,8 +47,7 @@
             + " main(java.lang.String[])(Main.java)",
         "\tat com.android.tools.r8.naming.retrace.Main.com.android.Foo"
             + " main(java.lang.String[],com.android.Bar)(Main.java)",
-        "< OR >",
-        "Exception in thread \"main\" java.lang.NullPointerException",
+        "<OR> Exception in thread \"main\" java.lang.NullPointerException",
         "\tat com.android.tools.r8.naming.retrace.Main.c(Main.java)",
         "\tat com.android.tools.r8.naming.retrace.Main.void main(com.android.Bar)(Main.java)",
         "\tat com.android.tools.r8.naming.retrace.Main.com.android.Foo"
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousMissingLineStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousMissingLineStackTrace.java
index 3442711..484fb5f 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousMissingLineStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousMissingLineStackTrace.java
@@ -25,13 +25,61 @@
   @Override
   public List<String> retracedStackTrace() {
     return Arrays.asList(
-        "There are 8 ambiguous stack traces. Use --verbose to have all listed.",
         "com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.bar(R8.java:7)",
         "    at com.android.tools.r8.R8.bar(R8.java:8)",
         "    at com.android.tools.r8.R8.main(Unknown Source)",
         "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.bar(R8.java:9)",
+        "    ... 42 more",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.bar(R8.java:7)",
+        "    at com.android.tools.r8.R8.bar(R8.java:8)",
+        "    at com.android.tools.r8.R8.main(Unknown Source)",
+        "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.foo(R8.java:9)",
+        "    ... 42 more",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.bar(R8.java:7)",
+        "    at com.android.tools.r8.R8.foo(R8.java:8)",
+        "    at com.android.tools.r8.R8.main(Unknown Source)",
+        "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.bar(R8.java:9)",
+        "    ... 42 more",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.bar(R8.java:7)",
+        "    at com.android.tools.r8.R8.foo(R8.java:8)",
+        "    at com.android.tools.r8.R8.main(Unknown Source)",
+        "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.foo(R8.java:9)",
+        "    ... 42 more",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.foo(R8.java:7)",
+        "    at com.android.tools.r8.R8.bar(R8.java:8)",
+        "    at com.android.tools.r8.R8.main(Unknown Source)",
+        "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.bar(R8.java:9)",
+        "    ... 42 more",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.foo(R8.java:7)",
+        "    at com.android.tools.r8.R8.bar(R8.java:8)",
+        "    at com.android.tools.r8.R8.main(Unknown Source)",
+        "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.foo(R8.java:9)",
+        "    ... 42 more",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.foo(R8.java:7)",
+        "    at com.android.tools.r8.R8.foo(R8.java:8)",
+        "    at com.android.tools.r8.R8.main(Unknown Source)",
+        "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.bar(R8.java:9)",
+        "    ... 42 more",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.foo(R8.java:7)",
+        "    at com.android.tools.r8.R8.foo(R8.java:8)",
+        "    at com.android.tools.r8.R8.main(Unknown Source)",
+        "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.foo(R8.java:9)",
         "    ... 42 more");
   }
 
@@ -46,56 +94,49 @@
         "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java:9)",
         "    ... 42 more",
-        "< OR >",
-        "com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java:7)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java:8)",
         "    at com.android.tools.r8.R8.main(Unknown Source)",
         "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java:9)",
         "    ... 42 more",
-        "< OR >",
-        "com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java:7)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java:8)",
         "    at com.android.tools.r8.R8.main(Unknown Source)",
         "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java:9)",
         "    ... 42 more",
-        "< OR >",
-        "com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java:7)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java:8)",
         "    at com.android.tools.r8.R8.main(Unknown Source)",
         "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java:9)",
         "    ... 42 more",
-        "< OR >",
-        "com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java:7)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java:8)",
         "    at com.android.tools.r8.R8.main(Unknown Source)",
         "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java:9)",
         "    ... 42 more",
-        "< OR >",
-        "com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java:7)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java:8)",
         "    at com.android.tools.r8.R8.main(Unknown Source)",
         "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java:9)",
         "    ... 42 more",
-        "< OR >",
-        "com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java:7)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java:8)",
         "    at com.android.tools.r8.R8.main(Unknown Source)",
         "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java:9)",
         "    ... 42 more",
-        "< OR >",
-        "com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java:7)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java:8)",
         "    at com.android.tools.r8.R8.main(Unknown Source)",
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousStackTrace.java
index 989868f..f6704a4 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousStackTrace.java
@@ -25,13 +25,61 @@
   @Override
   public List<String> retracedStackTrace() {
     return Arrays.asList(
-        "There are 8 ambiguous stack traces. Use --verbose to have all listed.",
         "com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.bar(R8.java)",
         "    at com.android.tools.r8.R8.bar(R8.java)",
         "    at com.android.tools.r8.R8.main(Unknown Source)",
         "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.bar(R8.java)",
+        "    ... 42 more",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.bar(R8.java)",
+        "    at com.android.tools.r8.R8.bar(R8.java)",
+        "    at com.android.tools.r8.R8.main(Unknown Source)",
+        "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.foo(R8.java)",
+        "    ... 42 more",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.bar(R8.java)",
+        "    at com.android.tools.r8.R8.foo(R8.java)",
+        "    at com.android.tools.r8.R8.main(Unknown Source)",
+        "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.bar(R8.java)",
+        "    ... 42 more",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.bar(R8.java)",
+        "    at com.android.tools.r8.R8.foo(R8.java)",
+        "    at com.android.tools.r8.R8.main(Unknown Source)",
+        "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.foo(R8.java)",
+        "    ... 42 more",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.foo(R8.java)",
+        "    at com.android.tools.r8.R8.bar(R8.java)",
+        "    at com.android.tools.r8.R8.main(Unknown Source)",
+        "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.bar(R8.java)",
+        "    ... 42 more",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.foo(R8.java)",
+        "    at com.android.tools.r8.R8.bar(R8.java)",
+        "    at com.android.tools.r8.R8.main(Unknown Source)",
+        "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.foo(R8.java)",
+        "    ... 42 more",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.foo(R8.java)",
+        "    at com.android.tools.r8.R8.foo(R8.java)",
+        "    at com.android.tools.r8.R8.main(Unknown Source)",
+        "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.bar(R8.java)",
+        "    ... 42 more",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.foo(R8.java)",
+        "    at com.android.tools.r8.R8.foo(R8.java)",
+        "    at com.android.tools.r8.R8.main(Unknown Source)",
+        "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.foo(R8.java)",
         "    ... 42 more");
   }
 
@@ -46,56 +94,49 @@
         "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java)",
         "    ... 42 more",
-        "< OR >",
-        "com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java)",
         "    at com.android.tools.r8.R8.main(Unknown Source)",
         "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java)",
         "    ... 42 more",
-        "< OR >",
-        "com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java)",
         "    at com.android.tools.r8.R8.main(Unknown Source)",
         "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java)",
         "    ... 42 more",
-        "< OR >",
-        "com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java)",
         "    at com.android.tools.r8.R8.main(Unknown Source)",
         "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java)",
         "    ... 42 more",
-        "< OR >",
-        "com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java)",
         "    at com.android.tools.r8.R8.main(Unknown Source)",
         "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java)",
         "    ... 42 more",
-        "< OR >",
-        "com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java)",
         "    at com.android.tools.r8.R8.main(Unknown Source)",
         "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java)",
         "    ... 42 more",
-        "< OR >",
-        "com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java)",
         "    at com.android.tools.r8.R8.main(Unknown Source)",
         "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java)",
         "    ... 42 more",
-        "< OR >",
-        "com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java)",
         "    at com.android.tools.r8.R8.main(Unknown Source)",
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousWithSignatureNonVerboseStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousWithSignatureNonVerboseStackTrace.java
index 97f2162..7c0c0c2 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousWithSignatureNonVerboseStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousWithSignatureNonVerboseStackTrace.java
@@ -43,16 +43,13 @@
         "java.lang.IndexOutOfBoundsException",
         "\tat java.util.ArrayList.get(ArrayList.java:411)",
         "\tat com.android.tools.r8.Internal.boolean foo(int,int)(Internal.java)",
-        "< OR >",
-        "java.lang.IndexOutOfBoundsException",
+        "<OR> java.lang.IndexOutOfBoundsException",
         "\tat java.util.ArrayList.get(ArrayList.java:411)",
         "\tat com.android.tools.r8.Internal.void foo(int)(Internal.java)",
-        "< OR >",
-        "java.lang.IndexOutOfBoundsException",
+        "<OR> java.lang.IndexOutOfBoundsException",
         "\tat java.util.ArrayList.get(ArrayList.java:411)",
         "\tat com.android.tools.r8.Internal.void foo(int,boolean)(Internal.java)",
-        "< OR >",
-        "java.lang.IndexOutOfBoundsException",
+        "<OR> java.lang.IndexOutOfBoundsException",
         "\tat java.util.ArrayList.get(ArrayList.java:411)",
         "\tat com.android.tools.r8.Internal.void foo(int,int)(Internal.java)");
   }
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousWithSignatureVerboseStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousWithSignatureVerboseStackTrace.java
index 23991fb..a00b7bf 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousWithSignatureVerboseStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousWithSignatureVerboseStackTrace.java
@@ -43,16 +43,13 @@
         "java.lang.IndexOutOfBoundsException",
         "\tat java.util.ArrayList.get(ArrayList.java:411)",
         "\tat com.android.tools.r8.Internal.boolean foo(int,int)(Internal.java)",
-        "< OR >",
-        "java.lang.IndexOutOfBoundsException",
+        "<OR> java.lang.IndexOutOfBoundsException",
         "\tat java.util.ArrayList.get(ArrayList.java:411)",
         "\tat com.android.tools.r8.Internal.void foo(int)(Internal.java)",
-        "< OR >",
-        "java.lang.IndexOutOfBoundsException",
+        "<OR> java.lang.IndexOutOfBoundsException",
         "\tat java.util.ArrayList.get(ArrayList.java:411)",
         "\tat com.android.tools.r8.Internal.void foo(int,boolean)(Internal.java)",
-        "< OR >",
-        "java.lang.IndexOutOfBoundsException",
+        "<OR> java.lang.IndexOutOfBoundsException",
         "\tat java.util.ArrayList.get(ArrayList.java:411)",
         "\tat com.android.tools.r8.Internal.void foo(int,int)(Internal.java)");
   }
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/MultipleLinesNoLineNumberStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/MultipleLinesNoLineNumberStackTrace.java
index 004b7d5..6df2b28 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/MultipleLinesNoLineNumberStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/MultipleLinesNoLineNumberStackTrace.java
@@ -29,8 +29,10 @@
   @Override
   public List<String> retracedStackTrace() {
     return Arrays.asList(
-        "There are 2 ambiguous stack traces. Use --verbose to have all listed.",
         "Exception in thread \"main\" java.lang.NullPointerException",
+        "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java)",
+        "<OR> Exception in thread \"main\" java.lang.NullPointerException",
+        "\tat com.android.tools.r8.naming.retrace.Main.method1(Main.java)",
         "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java)");
   }
 
@@ -40,8 +42,7 @@
         "There are 2 ambiguous stack traces.",
         "Exception in thread \"main\" java.lang.NullPointerException",
         "\tat com.android.tools.r8.naming.retrace.Main.void main(java.lang.String[])(Main.java)",
-        "< OR >",
-        "Exception in thread \"main\" java.lang.NullPointerException",
+        "<OR> Exception in thread \"main\" java.lang.NullPointerException",
         "\tat com.android.tools.r8.naming.retrace.Main.void method1(java.lang.String)(Main.java)",
         "\tat com.android.tools.r8.naming.retrace.Main.void main(java.lang.String[])(Main.java)");
   }
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/NoObfuscatedLineNumberWithOverrideTest.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/NoObfuscatedLineNumberWithOverrideTest.java
index 858c06b..2e57740 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/NoObfuscatedLineNumberWithOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/NoObfuscatedLineNumberWithOverrideTest.java
@@ -35,11 +35,25 @@
   @Override
   public List<String> retracedStackTrace() {
     return Arrays.asList(
-        "There are 4 ambiguous stack traces. Use --verbose to have all listed.",
         "Exception in thread \"main\" java.lang.NullPointerException",
         "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java:3)",
         "\tat com.android.tools.r8.naming.retrace.Main.overload1(Main.java:7)",
         "\tat com.android.tools.r8.naming.retrace.Main.definedOverload(Main.java:7)",
+        "\tat com.android.tools.r8.naming.retrace.Main.mainPC(Main.java:42)",
+        "<OR> Exception in thread \"main\" java.lang.NullPointerException",
+        "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java:3)",
+        "\tat com.android.tools.r8.naming.retrace.Main.overload1(Main.java:7)",
+        "\tat com.android.tools.r8.naming.retrace.Main.definedOverload(Main.java:11)",
+        "\tat com.android.tools.r8.naming.retrace.Main.mainPC(Main.java:42)",
+        "<OR> Exception in thread \"main\" java.lang.NullPointerException",
+        "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java:3)",
+        "\tat com.android.tools.r8.naming.retrace.Main.overload2(Main.java:11)",
+        "\tat com.android.tools.r8.naming.retrace.Main.definedOverload(Main.java:7)",
+        "\tat com.android.tools.r8.naming.retrace.Main.mainPC(Main.java:42)",
+        "<OR> Exception in thread \"main\" java.lang.NullPointerException",
+        "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java:3)",
+        "\tat com.android.tools.r8.naming.retrace.Main.overload2(Main.java:11)",
+        "\tat com.android.tools.r8.naming.retrace.Main.definedOverload(Main.java:11)",
         "\tat com.android.tools.r8.naming.retrace.Main.mainPC(Main.java:42)");
   }
 
@@ -53,24 +67,21 @@
         "\tat com.android.tools.r8.naming.retrace.Main.void definedOverload()(Main.java:7)",
         "\tat com.android.tools.r8.naming.retrace.Main.void"
             + " mainPC(java.lang.String[])(Main.java:42)",
-        "< OR >",
-        "Exception in thread \"main\" java.lang.NullPointerException",
+        "<OR> Exception in thread \"main\" java.lang.NullPointerException",
         "\tat com.android.tools.r8.naming.retrace.Main.void main(java.lang.String)(Main.java:3)",
         "\tat com.android.tools.r8.naming.retrace.Main.void overload1()(Main.java:7)",
         "\tat com.android.tools.r8.naming.retrace.Main.void"
             + " definedOverload(java.lang.String)(Main.java:11)",
         "\tat com.android.tools.r8.naming.retrace.Main.void"
             + " mainPC(java.lang.String[])(Main.java:42)",
-        "< OR >",
-        "Exception in thread \"main\" java.lang.NullPointerException",
+        "<OR> Exception in thread \"main\" java.lang.NullPointerException",
         "\tat com.android.tools.r8.naming.retrace.Main.void main(java.lang.String)(Main.java:3)",
         "\tat com.android.tools.r8.naming.retrace.Main.void"
             + " overload2(java.lang.String)(Main.java:11)",
         "\tat com.android.tools.r8.naming.retrace.Main.void definedOverload()(Main.java:7)",
         "\tat com.android.tools.r8.naming.retrace.Main.void"
             + " mainPC(java.lang.String[])(Main.java:42)",
-        "< OR >",
-        "Exception in thread \"main\" java.lang.NullPointerException",
+        "<OR> Exception in thread \"main\" java.lang.NullPointerException",
         "\tat com.android.tools.r8.naming.retrace.Main.void main(java.lang.String)(Main.java:3)",
         "\tat com.android.tools.r8.naming.retrace.Main.void"
             + " overload2(java.lang.String)(Main.java:11)",
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/NpeInlineRetraceStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/NpeInlineRetraceStackTrace.java
new file mode 100644
index 0000000..3857154
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/NpeInlineRetraceStackTrace.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace.stacktraces;
+
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
+import java.util.List;
+
+public class NpeInlineRetraceStackTrace implements StackTraceForTest {
+
+  @Override
+  public List<String> obfuscatedStackTrace() {
+    return Arrays.asList(
+        "Exception in thread \"main\" java.lang.NullPointerException", "\tat a.a(:4)");
+  }
+
+  @Override
+  public String mapping() {
+    return StringUtils.lines(
+        "# { id: 'com.android.tools.r8.mapping', version: 'experimental' }",
+        "some.Class -> a:",
+        "  4:4:void other.Class():23:23 -> a",
+        "  4:4:void caller(other.Class):7 -> a",
+        "  # { id: 'com.android.tools.r8.rewriteFrame', "
+            + "conditions: ['throws(Ljava/lang/NullPointerException;)'],  "
+            + "actions: ['removeInnerFrames(1)'] }");
+  }
+
+  @Override
+  public List<String> retracedStackTrace() {
+    return Arrays.asList(
+        "Exception in thread \"main\" java.lang.NullPointerException",
+        "\tat some.Class.caller(Class.java:7)");
+  }
+
+  @Override
+  public List<String> retraceVerboseStackTrace() {
+    return Arrays.asList(
+        "Exception in thread \"main\" java.lang.NullPointerException",
+        "\tat some.Class.void caller(other.Class)(Class.java:7)");
+  }
+
+  @Override
+  public int expectedWarnings() {
+    return 0;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/OverloadSameLineTest.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/OverloadSameLineTest.java
index c5fe164..277e4b3 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/OverloadSameLineTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/OverloadSameLineTest.java
@@ -29,9 +29,12 @@
   @Override
   public List<String> retracedStackTrace() {
     return Arrays.asList(
-        "There are 3 ambiguous stack traces. Use --verbose to have all listed.",
         "Exception in thread \"main\" java.lang.NullPointerException",
-        "\tat com.android.tools.r8.naming.retrace.Main.overload(Main.java:7)");
+        "\tat com.android.tools.r8.naming.retrace.Main.overload(Main.java:7)",
+        "<OR> Exception in thread \"main\" java.lang.NullPointerException",
+        "\tat com.android.tools.r8.naming.retrace.Main.overload(Main.java:15)",
+        "<OR> Exception in thread \"main\" java.lang.NullPointerException",
+        "\tat com.android.tools.r8.naming.retrace.Main.overload(Main.java:13)");
   }
 
   @Override
@@ -40,11 +43,9 @@
         "There are 3 ambiguous stack traces.",
         "Exception in thread \"main\" java.lang.NullPointerException",
         "\tat com.android.tools.r8.naming.retrace.Main.void overload()(Main.java:7)",
-        "< OR >",
-        "Exception in thread \"main\" java.lang.NullPointerException",
+        "<OR> Exception in thread \"main\" java.lang.NullPointerException",
         "\tat com.android.tools.r8.naming.retrace.Main.void overload(int)(Main.java:15)",
-        "< OR >",
-        "Exception in thread \"main\" java.lang.NullPointerException",
+        "<OR> Exception in thread \"main\" java.lang.NullPointerException",
         "\tat com.android.tools.r8.naming.retrace.Main.void"
             + " overload(java.lang.String)(Main.java:13)");
   }
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/SingleLineNoLineNumberStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/SingleLineNoLineNumberStackTrace.java
index 120cd8e..8f6e183 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/SingleLineNoLineNumberStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/SingleLineNoLineNumberStackTrace.java
@@ -36,13 +36,19 @@
   @Override
   public List<String> retracedStackTrace() {
     return Arrays.asList(
-        "There are 2 ambiguous stack traces. Use --verbose to have all listed.",
         "Exception in thread \"main\" java.lang.NullPointerException",
         "\tat com.android.tools.r8.naming.retrace.Main.method1(Main.java:42)",
         "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java:28)",
         "\tat com.android.tools.r8.naming.retrace.Main.method2(Main.java:42)",
         "\tat com.android.tools.r8.naming.retrace.Main.main2(Main.java:29)",
         "\tat com.android.tools.r8.naming.retrace.Main.main3(Main.java:30)",
+        "\tat com.android.tools.r8.naming.retrace.Main.main4(Main.java:153)",
+        "<OR> Exception in thread \"main\" java.lang.NullPointerException",
+        "\tat com.android.tools.r8.naming.retrace.Main.method1(Main.java:42)",
+        "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java:28)",
+        "\tat com.android.tools.r8.naming.retrace.Main.method2(Main.java:42)",
+        "\tat com.android.tools.r8.naming.retrace.Main.main2(Main.java:29)",
+        "\tat com.android.tools.r8.naming.retrace.Main.method3(Main.java:72)",
         "\tat com.android.tools.r8.naming.retrace.Main.main4(Main.java:153)");
   }
 
@@ -62,8 +68,7 @@
             + " main3(java.lang.String[])(Main.java:30)",
         "\tat com.android.tools.r8.naming.retrace.Main.void"
             + " main4(java.lang.String[])(Main.java:153)",
-        "< OR >",
-        "Exception in thread \"main\" java.lang.NullPointerException",
+        "<OR> Exception in thread \"main\" java.lang.NullPointerException",
         "\tat com.android.tools.r8.naming.retrace.Main.void"
             + " method1(java.lang.String)(Main.java:42)",
         "\tat com.android.tools.r8.naming.retrace.Main.void main(java.lang.String[])(Main.java:28)",
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/UnknownSourceStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/UnknownSourceStackTrace.java
index 2f946f8..3697b0c 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/UnknownSourceStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/UnknownSourceStackTrace.java
@@ -25,13 +25,61 @@
   @Override
   public List<String> retracedStackTrace() {
     return Arrays.asList(
-        "There are 8 ambiguous stack traces. Use --verbose to have all listed.",
         "com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.bar(R8.java)",
         "    at com.android.tools.r8.R8.bar(R8.java)",
         "    at com.android.tools.r8.R8.main(Unknown Source)",
         "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.bar(R8.java)",
+        "    ... 42 more",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.bar(R8.java)",
+        "    at com.android.tools.r8.R8.bar(R8.java)",
+        "    at com.android.tools.r8.R8.main(Unknown Source)",
+        "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.foo(R8.java)",
+        "    ... 42 more",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.bar(R8.java)",
+        "    at com.android.tools.r8.R8.foo(R8.java)",
+        "    at com.android.tools.r8.R8.main(Unknown Source)",
+        "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.bar(R8.java)",
+        "    ... 42 more",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.bar(R8.java)",
+        "    at com.android.tools.r8.R8.foo(R8.java)",
+        "    at com.android.tools.r8.R8.main(Unknown Source)",
+        "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.foo(R8.java)",
+        "    ... 42 more",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.foo(R8.java)",
+        "    at com.android.tools.r8.R8.bar(R8.java)",
+        "    at com.android.tools.r8.R8.main(Unknown Source)",
+        "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.bar(R8.java)",
+        "    ... 42 more",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.foo(R8.java)",
+        "    at com.android.tools.r8.R8.bar(R8.java)",
+        "    at com.android.tools.r8.R8.main(Unknown Source)",
+        "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.foo(R8.java)",
+        "    ... 42 more",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.foo(R8.java)",
+        "    at com.android.tools.r8.R8.foo(R8.java)",
+        "    at com.android.tools.r8.R8.main(Unknown Source)",
+        "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.bar(R8.java)",
+        "    ... 42 more",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.foo(R8.java)",
+        "    at com.android.tools.r8.R8.foo(R8.java)",
+        "    at com.android.tools.r8.R8.main(Unknown Source)",
+        "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "    at com.android.tools.r8.R8.foo(R8.java)",
         "    ... 42 more");
   }
 
@@ -46,56 +94,49 @@
         "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java)",
         "    ... 42 more",
-        "< OR >",
-        "com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java)",
         "    at com.android.tools.r8.R8.main(Unknown Source)",
         "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java)",
         "    ... 42 more",
-        "< OR >",
-        "com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java)",
         "    at com.android.tools.r8.R8.main(Unknown Source)",
         "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java)",
         "    ... 42 more",
-        "< OR >",
-        "com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java)",
         "    at com.android.tools.r8.R8.main(Unknown Source)",
         "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java)",
         "    ... 42 more",
-        "< OR >",
-        "com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java)",
         "    at com.android.tools.r8.R8.main(Unknown Source)",
         "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java)",
         "    ... 42 more",
-        "< OR >",
-        "com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java)",
         "    at com.android.tools.r8.R8.main(Unknown Source)",
         "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java)",
         "    ... 42 more",
-        "< OR >",
-        "com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java)",
         "    at com.android.tools.r8.R8.main(Unknown Source)",
         "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void bar(int,int)(R8.java)",
         "    ... 42 more",
-        "< OR >",
-        "com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+        "<OR> com.android.tools.r8.CompilationException: foo[parens](Source:3)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java)",
         "    at com.android.tools.r8.R8.void foo(int)(R8.java)",
         "    at com.android.tools.r8.R8.main(Unknown Source)",
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java
index 22ba711..4d2837b 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java
@@ -14,13 +14,12 @@
 
 import com.android.tools.r8.AssertionsConfiguration;
 import com.android.tools.r8.D8TestBuilder;
+import com.android.tools.r8.KotlinCompilerTool.KotlinTargetVersion;
 import com.android.tools.r8.KotlinTestBase;
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ThrowableConsumer;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.ThrowingConsumer;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -93,7 +92,7 @@
   }
 
   private Path kotlinStdlibLibraryForRuntime() throws Exception {
-    Path kotlinStdlibCf = ToolHelper.getKotlinStdlibJar(kotlinc);
+    Path kotlinStdlibCf = kotlinc.getKotlinStdlibJar();
     if (parameters.getRuntime().isCf()) {
       return kotlinStdlibCf;
     }
@@ -114,7 +113,7 @@
       throws Exception {
     if (kotlinStdlibAsLibrary) {
       testForD8()
-          .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
+          .addClasspathFiles(kotlinc.getKotlinStdlibJar())
           .addProgramFiles(compiledForAssertions.getForConfiguration(kotlinc, targetVersion))
           .setMinApi(parameters.getApiLevel())
           .apply(builderConsumer)
@@ -126,7 +125,7 @@
           .assertSuccessWithOutputLines(outputLines);
     } else {
       testForD8()
-          .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
+          .addProgramFiles(kotlinc.getKotlinStdlibJar())
           .addProgramFiles(compiledForAssertions.getForConfiguration(kotlinc, targetVersion))
           .setMinApi(parameters.getApiLevel())
           .apply(builderConsumer)
@@ -157,11 +156,11 @@
         .applyIf(
             kotlinStdlibAsLibrary,
             b -> {
-              b.addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc));
+              b.addClasspathFiles(kotlinc.getKotlinStdlibJar());
               b.addRunClasspathFiles(kotlinStdlibLibraryForRuntime());
             },
-            b -> b.addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc)))
-        .addClasspathFiles(ToolHelper.getKotlinAnnotationJar(kotlinc))
+            b -> b.addProgramFiles(kotlinc.getKotlinStdlibJar()))
+        .addClasspathFiles(kotlinc.getKotlinAnnotationJar())
         .addProgramFiles(compiledForAssertions.getForConfiguration(kotlinc, targetVersion))
         .addKeepMainRule(testClassKt)
         .addKeepClassAndMembersRules(class1, class2)
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java b/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
index a9574e8..76e8f34 100644
--- a/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.rewrite.enums;
 
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static java.util.Collections.emptyList;
 import static java.util.stream.Collectors.toList;
@@ -58,7 +59,6 @@
         .addProgramClassesAndInnerClasses(Ordinals.class)
         .addKeepMainRule(Ordinals.class)
         .enableConstantArgumentAnnotations()
-        .enableForceInliningAnnotations()
         .enableInliningAnnotations()
         .enableSideEffectAnnotations()
         .addOptionsModification(this::configure)
@@ -100,6 +100,8 @@
 
     assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("libraryType"));
     assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("phi"));
+
+    assertThat(clazz.uniqueMethodWithName("inlined2"), isAbsent());
   }
 
   @Test
@@ -108,7 +110,6 @@
         .addProgramClassesAndInnerClasses(Names.class)
         .addKeepMainRule(Names.class)
         .enableConstantArgumentAnnotations()
-        .enableForceInliningAnnotations()
         .enableInliningAnnotations()
         .enableSideEffectAnnotations()
         .addOptionsModification(this::configure)
@@ -147,6 +148,8 @@
     assertNameWasNotReplaced(clazz.uniqueMethodWithName("libraryType"));
 
     assertNameWasNotReplaced(clazz.uniqueMethodWithName("phi"));
+
+    assertThat(clazz.uniqueMethodWithName("inlined2"), isAbsent());
   }
 
   @Test
@@ -155,7 +158,6 @@
         .addProgramClassesAndInnerClasses(ToStrings.class)
         .addKeepMainRule(ToStrings.class)
         .enableConstantArgumentAnnotations()
-        .enableForceInliningAnnotations()
         .enableInliningAnnotations()
         .enableSideEffectAnnotations()
         .addOptionsModification(this::configure)
@@ -197,6 +199,8 @@
 
     assertToStringWasNotReplaced(clazz.uniqueMethodWithName("libraryType"));
     assertToStringWasNotReplaced(clazz.uniqueMethodWithName("phi"));
+
+    assertThat(clazz.uniqueMethodWithName("inlined2"), isAbsent());
   }
 
   private static void assertOrdinalReplacedWithConst(MethodSubject method, int expectedConst) {
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/Names.java b/src/test/java/com/android/tools/r8/rewrite/enums/Names.java
index 1c2964c..10a70c4a 100644
--- a/src/test/java/com/android/tools/r8/rewrite/enums/Names.java
+++ b/src/test/java/com/android/tools/r8/rewrite/enums/Names.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.rewrite.enums;
 
 import com.android.tools.r8.AssumeMayHaveSideEffects;
-import com.android.tools.r8.ForceInline;
 import com.android.tools.r8.KeepConstantArguments;
 import com.android.tools.r8.NeverInline;
 import java.util.concurrent.TimeUnit;
@@ -48,7 +47,6 @@
     return inlined2(Number.TWO);
   }
 
-  @ForceInline
   private static String inlined2(Number number) {
     return number.name();
   }
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/Ordinals.java b/src/test/java/com/android/tools/r8/rewrite/enums/Ordinals.java
index c3fbffc..de4677d 100644
--- a/src/test/java/com/android/tools/r8/rewrite/enums/Ordinals.java
+++ b/src/test/java/com/android/tools/r8/rewrite/enums/Ordinals.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.rewrite.enums;
 
 import com.android.tools.r8.AssumeMayHaveSideEffects;
-import com.android.tools.r8.ForceInline;
 import com.android.tools.r8.KeepConstantArguments;
 import com.android.tools.r8.NeverInline;
 import java.util.concurrent.TimeUnit;
@@ -44,7 +43,7 @@
   private static long inlined() {
     return inlined2(Number.TWO);
   }
-  @ForceInline
+
   private static long inlined2(Number number) {
     return number.ordinal();
   }
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/ToStrings.java b/src/test/java/com/android/tools/r8/rewrite/enums/ToStrings.java
index 020f5fd..957a5e1 100644
--- a/src/test/java/com/android/tools/r8/rewrite/enums/ToStrings.java
+++ b/src/test/java/com/android/tools/r8/rewrite/enums/ToStrings.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.rewrite.enums;
 
 import com.android.tools.r8.AssumeMayHaveSideEffects;
-import com.android.tools.r8.ForceInline;
 import com.android.tools.r8.KeepConstantArguments;
 import com.android.tools.r8.NeverInline;
 import java.util.Locale;
@@ -85,7 +84,6 @@
     return inlined2(NoToString.TWO);
   }
 
-  @ForceInline
   private static String inlined2(NoToString number) {
     return number.toString();
   }
diff --git a/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
index 7f6323a..b99494e 100644
--- a/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
@@ -80,17 +80,17 @@
 @RunWith(Parameterized.class)
 public class ParameterTypeTest extends TestBase {
 
-  private final boolean enableArgumentRemoval;
+  private final boolean enableArgumentPropagation;
   private final TestParameters parameters;
 
-  @Parameters(name = "{1}, argument removal: {0}")
+  @Parameters(name = "{1}, argument propagation: {0}")
   public static List<Object[]> data() {
     return buildParameters(
         BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
   }
 
-  public ParameterTypeTest(boolean enableArgumentRemoval, TestParameters parameters) {
-    this.enableArgumentRemoval = enableArgumentRemoval;
+  public ParameterTypeTest(boolean enableArgumentPropagation, TestParameters parameters) {
+    this.enableArgumentPropagation = enableArgumentPropagation;
     this.parameters = parameters;
   }
 
@@ -228,7 +228,7 @@
             options -> {
               // Disable inlining to avoid the (short) tested method from being inlined and removed.
               options.enableInlining = false;
-              options.enableArgumentRemoval = enableArgumentRemoval;
+              options.callSiteOptimizationOptions().setEnabled(enableArgumentPropagation);
             });
 
     // Run processed (output) program on ART
@@ -239,7 +239,7 @@
 
     CodeInspector inspector = new CodeInspector(processedApp);
     ClassSubject subSubject = inspector.clazz(sub.name);
-    assertNotEquals(enableArgumentRemoval, subSubject.isPresent());
+    assertNotEquals(enableArgumentPropagation, subSubject.isPresent());
   }
 
   @Test
@@ -307,7 +307,7 @@
             options -> {
               // Disable inlining to avoid the (short) tested method from being inlined and removed.
               options.enableInlining = false;
-              options.enableArgumentRemoval = enableArgumentRemoval;
+              options.callSiteOptimizationOptions().setEnabled(enableArgumentPropagation);
             })
         .noMinification()
         .setMinApi(parameters.getApiLevel())
@@ -315,11 +315,11 @@
         .inspect(
             inspector -> {
               ClassSubject subSubject = inspector.clazz(sub.name);
-              assertNotEquals(enableArgumentRemoval, subSubject.isPresent());
+              assertNotEquals(enableArgumentPropagation, subSubject.isPresent());
             })
         .run(parameters.getRuntime(), mainClassName)
         .applyIf(
-            enableArgumentRemoval || parameters.isCfRuntime(),
+            enableArgumentPropagation || parameters.isCfRuntime(),
             SingleTestRunResult::assertSuccess,
             result ->
                 result.assertFailureWithErrorThatMatches(
@@ -410,7 +410,7 @@
             options -> {
               // Disable inlining to avoid the (short) tested method from being inlined and removed.
               options.enableInlining = false;
-              options.enableArgumentRemoval = enableArgumentRemoval;
+              options.callSiteOptimizationOptions().setEnabled(enableArgumentPropagation);
             });
 
     // Run processed (output) program on ART
@@ -422,6 +422,6 @@
 
     CodeInspector inspector = new CodeInspector(processedApp);
     ClassSubject subSubject = inspector.clazz(sub.name);
-    assertNotEquals(enableArgumentRemoval, subSubject.isPresent());
+    assertNotEquals(enableArgumentPropagation, subSubject.isPresent());
   }
 }
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index c93200c..a296b9b 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -1720,8 +1720,7 @@
 
   @Test
   public void parse_testInlineOptions() {
-    List<String> options = ImmutableList.of(
-        "-neverinline", "-forceinline");
+    List<String> options = ImmutableList.of("-neverinline");
     for (String option : options) {
       try {
         reset();
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 662ad5e..852c86f 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
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking.annotations;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
 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;
@@ -98,7 +97,7 @@
         testForR8Compat(parameters.getBackend())
             .addProgramFiles(
                 compiledJars.getForConfiguration(kotlinc, targetVersion),
-                getKotlinAnnotationJar(kotlinc))
+                kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(getJavaJarFile(FOLDER))
             .addKeepMainRule(MAIN_CLASS_NAME)
             .addKeepRules(KEEP_ANNOTATIONS)
@@ -136,7 +135,7 @@
         testForR8Compat(parameters.getBackend())
             .addProgramFiles(
                 compiledJars.getForConfiguration(kotlinc, targetVersion),
-                getKotlinAnnotationJar(kotlinc))
+                kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(getJavaJarFile(FOLDER))
             .addKeepMainRule(MAIN_CLASS_NAME)
             .addKeepRules(KEEP_ANNOTATIONS)
@@ -178,7 +177,7 @@
         testForR8Compat(parameters.getBackend())
             .addProgramFiles(
                 compiledJars.getForConfiguration(kotlinc, targetVersion),
-                getKotlinAnnotationJar(kotlinc))
+                kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(getJavaJarFile(FOLDER))
             .addKeepMainRule(MAIN_CLASS_NAME)
             .addKeepRules(KEEP_ANNOTATIONS)
@@ -216,7 +215,7 @@
         testForR8(parameters.getBackend())
             .addProgramFiles(
                 compiledJars.getForConfiguration(kotlinc, targetVersion),
-                getKotlinAnnotationJar(kotlinc))
+                kotlinc.getKotlinAnnotationJar())
             .addProgramFiles(getJavaJarFile(FOLDER))
             .addKeepMainRule(MAIN_CLASS_NAME)
             .allowDiagnosticWarningMessages()
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java b/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java
index 184cd68..161d685 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java
@@ -4,13 +4,12 @@
 
 package com.android.tools.r8.shaking.annotations.b137392797;
 
-import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
-import static com.android.tools.r8.ToolHelper.getKotlinC_1_3_72;
-import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.utils.BooleanUtils;
@@ -59,14 +58,14 @@
 
   @Test
   public void testR8() throws Exception {
+    KotlinCompiler compiler = KOTLINC_1_3_72.getCompiler();
     testForR8(parameters.getBackend())
         .addProgramClassFileData(
             classWireField(defaultEnumValueInAnnotation),
             classWireFieldLabel(),
             classTest(defaultEnumValueInAnnotation))
         .addProgramClasses(TestClass.class)
-        .addClasspathFiles(
-            getKotlinStdlibJar(getKotlinC_1_3_72()), getKotlinAnnotationJar(getKotlinC_1_3_72()))
+        .addClasspathFiles(compiler.getKotlinStdlibJar(), compiler.getKotlinAnnotationJar())
         .addKeepClassAndMembersRules(
             "com.squareup.wire.WireField", "com.squareup.demo.myapplication.Test")
         .addKeepMainRule(TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisherTest.java b/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisherTest.java
index 5d5a4b9..422ec12 100644
--- a/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisherTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisherTest.java
@@ -4,13 +4,11 @@
 
 package com.android.tools.r8.shaking.b134858535;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
-
 import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinCompilerTool.KotlinTargetVersion;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
 import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -20,18 +18,21 @@
 @RunWith(Parameterized.class)
 public class EventPublisherTest extends TestBase {
 
-  private final KotlinCompiler kotlinc;
+  private final KotlinTestParameters kotlinTestParameters;
   private final TestParameters parameters;
 
   @Parameters(name = "{1}, {0}")
   public static List<Object[]> data() {
     return buildParameters(
-        getKotlinCompilers(),
+        getKotlinTestParameters()
+            .withAllCompilers()
+            .withTargetVersion(KotlinTargetVersion.JAVA_8)
+            .build(),
         TestBase.getTestParameters().withDexRuntimes().withAllApiLevels().build());
   }
 
-  public EventPublisherTest(KotlinCompiler kotlinc, TestParameters parameters) {
-    this.kotlinc = kotlinc;
+  public EventPublisherTest(KotlinTestParameters kotlinTestParameters, TestParameters parameters) {
+    this.kotlinTestParameters = kotlinTestParameters;
     this.parameters = parameters;
   }
 
@@ -47,7 +48,7 @@
             SdkConfiguration.class,
             TrackBatchEventResponse.class)
         .addProgramClassFileData(EventPublisher$bDump.dump())
-        .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
+        .addProgramFiles(kotlinTestParameters.getCompiler().getKotlinStdlibJar())
         .addKeepClassRules(Interface.class)
         .addKeepMainRule(Main.class)
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/shaking/testrules/A.java b/src/test/java/com/android/tools/r8/shaking/testrules/A.java
deleted file mode 100644
index 23aa04e..0000000
--- a/src/test/java/com/android/tools/r8/shaking/testrules/A.java
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (c) 2018, 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.shaking.testrules;
-
-import com.android.tools.r8.NoHorizontalClassMerging;
-
-@NoHorizontalClassMerging
-public class A {
-
-  public static int m(int a, int b) {
-    int r = a + b;
-    System.out.println(a + " + " + b + " = " + r);
-    return r;
-  }
-
-  public static int method() {
-    return m(m(m(1, 2), m(3, 4)), m(m(5, 6), m(7, 8)));
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/shaking/testrules/B.java b/src/test/java/com/android/tools/r8/shaking/testrules/B.java
deleted file mode 100644
index 4d1f085..0000000
--- a/src/test/java/com/android/tools/r8/shaking/testrules/B.java
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright (c) 2018, 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.shaking.testrules;
-
-public class B {
-
-  public int m(int a, int b) {
-    int r = a + b;
-    System.out.println(a + " + " + b + " = " + r);
-    return r;
-  }
-  public int method() {
-    return m(m(m(1, 2), m(3, 4)), m(m(5, 6), m(7, 8)));
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/shaking/testrules/C.java b/src/test/java/com/android/tools/r8/shaking/testrules/C.java
deleted file mode 100644
index 497c218..0000000
--- a/src/test/java/com/android/tools/r8/shaking/testrules/C.java
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright (c) 2018, 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.shaking.testrules;
-
-import com.android.tools.r8.NeverInline;
-
-public class C {
-
-  private static int i;
-
-  @NeverInline
-  public static int x() {
-    return i;
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/shaking/testrules/ForceInlineTest.java b/src/test/java/com/android/tools/r8/shaking/testrules/ForceInlineTest.java
deleted file mode 100644
index aaafe0c..0000000
--- a/src/test/java/com/android/tools/r8/shaking/testrules/ForceInlineTest.java
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright (c) 2018, 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.
-
-// Copyright (c) 2018, 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.shaking.testrules;
-
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.fail;
-
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.google.common.collect.ImmutableList;
-import java.util.List;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class ForceInlineTest extends TestBase {
-
-  private TestParameters parameters;
-
-  @Parameterized.Parameters(name = "{0}")
-  public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimesAndApiLevels().build();
-  }
-
-  public ForceInlineTest(TestParameters parameters) {
-    this.parameters = parameters;
-  }
-
-  private CodeInspector runTest(List<String> proguardConfiguration) throws Exception {
-    return testForR8(parameters.getBackend())
-        .addProgramClasses(Main.class, A.class, B.class, C.class)
-        .addKeepRules(proguardConfiguration)
-        .enableInliningAnnotations()
-        .enableNoHorizontalClassMergingAnnotations()
-        .enableNoHorizontalClassMergingAnnotations()
-        .enableProguardTestOptions()
-        .compile()
-        .inspector();
-  }
-
-  @Test
-  public void testDefaultInlining() throws Exception {
-    CodeInspector inspector =
-        runTest(
-            ImmutableList.of(
-                "-keep class **.Main { *; }",
-                "-neverinline class *{ @com.android.tools.r8.NeverInline <methods>;}",
-                "-dontobfuscate"));
-
-    ClassSubject classA = inspector.clazz(A.class);
-    ClassSubject classB = inspector.clazz(B.class);
-    ClassSubject classC = inspector.clazz(C.class);
-    ClassSubject classMain = inspector.clazz(Main.class);
-    assertThat(classA, isPresent());
-    assertThat(classB, isPresent());
-    assertThat(classC, not(isPresent()));
-    assertThat(classMain, isPresent());
-
-    // By default A.m *will not* be inlined (called several times and not small).
-    assertThat(classA.method("int", "m", ImmutableList.of("int", "int")), isPresent());
-    // By default A.method *will* be inlined (called only once).
-    assertThat(classA.method("int", "method", ImmutableList.of()), not(isPresent()));
-    // By default B.m *will not* be inlined (called several times and not small).
-    assertThat(classB.method("int", "m", ImmutableList.of("int", "int")), isPresent());
-    // By default B.method *will* be inlined (called only once).
-    assertThat(classB.method("int", "method", ImmutableList.of()), not(isPresent()));
-  }
-
-  @Test
-  public void testNeverInline() throws Exception {
-    CodeInspector inspector =
-        runTest(
-            ImmutableList.of(
-                "-neverinline class **.A { method(); }",
-                "-neverinline class **.B { method(); }",
-                "-keep class **.Main { *; }",
-                "-neverinline class *{ @com.android.tools.r8.NeverInline <methods>;}",
-                "-dontobfuscate"));
-
-    ClassSubject classA = inspector.clazz(A.class);
-    ClassSubject classB = inspector.clazz(B.class);
-    ClassSubject classC = inspector.clazz(C.class);
-    ClassSubject classMain = inspector.clazz(Main.class);
-    assertThat(classA, isPresent());
-    assertThat(classB, isPresent());
-    assertThat(classC, not(isPresent()));
-    assertThat(classMain, isPresent());
-
-    // Compared to the default method is no longer inlined.
-    assertThat(classA.method("int", "m", ImmutableList.of("int", "int")), isPresent());
-    assertThat(classA.method("int", "method", ImmutableList.of()), isPresent());
-    assertThat(classB.method("int", "m", ImmutableList.of("int", "int")), isPresent());
-    assertThat(classB.method("int", "method", ImmutableList.of()), isPresent());
-  }
-
-  @Test
-  public void testForceInline() throws Exception {
-    CodeInspector inspector =
-        runTest(
-            ImmutableList.of(
-                "-forceinline class **.A { int m(int, int); }",
-                "-forceinline class **.B { int m(int, int); }",
-                "-keep class **.Main { *; }",
-                "-neverinline class *{ @com.android.tools.r8.NeverInline <methods>;}",
-                "-dontobfuscate"));
-
-    // Compared to the default m is now inlined and method still is, so classes A and B are gone.
-    assertThat(inspector.clazz(A.class), not(isPresent()));
-    assertThat(inspector.clazz(B.class), not(isPresent()));
-    assertThat(inspector.clazz(C.class), not(isPresent()));
-    assertThat(inspector.clazz(Main.class), isPresent());
-  }
-
-  @Test
-  public void testForceInlineFails() {
-    try {
-      runTest(
-          ImmutableList.of(
-              "-forceinline class **.A { int x(); }",
-              "-keep class **.Main { *; }",
-              "-dontobfuscate"));
-      fail("Force inline of non-inlinable method succeeded");
-    } catch (Throwable t) {
-      // Ignore assertion error.
-    }
-  }
-}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/shaking/testrules/Main.java b/src/test/java/com/android/tools/r8/shaking/testrules/Main.java
deleted file mode 100644
index d326216..0000000
--- a/src/test/java/com/android/tools/r8/shaking/testrules/Main.java
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright (c) 2018, 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.shaking.testrules;
-
-public class Main {
-
-  public static void main(String[] args) {
-    System.out.println(A.method());
-    System.out.println(new B().method());
-    System.out.println(C.x());
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
index b100332..9c057e6 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.utils.codeinspector;
 
+import static com.google.common.base.Predicates.alwaysTrue;
+
 import com.android.tools.r8.graph.ClassAccessFlags;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
@@ -17,7 +19,6 @@
 import com.android.tools.r8.smali.SmaliBuilder;
 import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
 import com.android.tools.r8.utils.ListUtils;
-import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableList;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
@@ -41,7 +42,7 @@
   public abstract void forAllMethods(Consumer<FoundMethodSubject> inspection);
 
   public final List<FoundMethodSubject> allMethods() {
-    return allMethods(Predicates.alwaysTrue());
+    return allMethods(alwaysTrue());
   }
 
   public final List<FoundMethodSubject> allMethods(Predicate<FoundMethodSubject> predicate) {
@@ -58,7 +59,7 @@
   public abstract void forAllVirtualMethods(Consumer<FoundMethodSubject> inspection);
 
   public final List<FoundMethodSubject> virtualMethods() {
-    return virtualMethods(Predicates.alwaysTrue());
+    return virtualMethods(alwaysTrue());
   }
 
   public final List<FoundMethodSubject> virtualMethods(Predicate<FoundMethodSubject> predicate) {
@@ -111,6 +112,10 @@
     return method("void", "main", ImmutableList.of("java.lang.String[]"));
   }
 
+  public MethodSubject uniqueMethod() {
+    return uniqueMethodThatMatches(alwaysTrue());
+  }
+
   public MethodSubject clinit() {
     return method("void", "<clinit>", ImmutableList.of());
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index 76d76e5..d753ca5 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -177,7 +177,7 @@
 
   public Retracer getRetracer() {
     if (lazyRetracer == null) {
-      lazyRetracer = new RetracerImpl(mapping);
+      lazyRetracer = new RetracerImpl(mapping, new TestDiagnosticMessagesImpl());
     }
     return lazyRetracer;
   }
diff --git a/third_party/kotlin/kotlin-compiler-dev.tar.gz.sha1 b/third_party/kotlin/kotlin-compiler-dev.tar.gz.sha1
new file mode 100644
index 0000000..9aefa3f
--- /dev/null
+++ b/third_party/kotlin/kotlin-compiler-dev.tar.gz.sha1
@@ -0,0 +1 @@
+d78e3bea626ae7f07cce1160a0af5c5c6812468e
\ No newline at end of file
diff --git a/third_party/retrace/binary_compatibility.tar.gz.sha1 b/third_party/retrace/binary_compatibility.tar.gz.sha1
index c3fc5b5..7fbd228 100644
--- a/third_party/retrace/binary_compatibility.tar.gz.sha1
+++ b/third_party/retrace/binary_compatibility.tar.gz.sha1
@@ -1 +1 @@
-8a3bfb12c41dc6fc56545c186729f234f2680b55
\ No newline at end of file
+6b7ccd6aa40c22bea72017bfabca022d4d90d70a
\ No newline at end of file
diff --git a/tools/download_kotlin_dev.py b/tools/download_kotlin_dev.py
new file mode 100755
index 0000000..c6c127e
--- /dev/null
+++ b/tools/download_kotlin_dev.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+# Copyright (c) 2021, 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.
+
+from HTMLParser import HTMLParser
+import os
+import sys
+import urllib
+import utils
+
+JETBRAINS_KOTLIN_MAVEN_URL = "https://maven.pkg.jetbrains.space/kotlin/p/" \
+                             "kotlin/bootstrap/org/jetbrains/kotlin/"
+KOTLIN_RELEASE_URL = JETBRAINS_KOTLIN_MAVEN_URL + "kotlin-compiler/"
+
+def download_newest():
+  response = urllib.urlopen(KOTLIN_RELEASE_URL)
+  if response.getcode() != 200:
+    raise Exception('Url: %s \n returned %s'
+                    % (KOTLIN_RELEASE_URL, response.getcode()))
+  content = response.read()
+  release_candidates = []
+
+  class HTMLContentParser(HTMLParser):
+    def handle_data(self, data):
+      if ('-dev-' in data):
+        release_candidates.append(data)
+
+  parser = HTMLContentParser()
+  parser.feed(content)
+
+  top_most_version = (0, 0, 0, 0)
+  top_most_version_and_build = None
+
+  for version in release_candidates:
+    # The compiler version is on the form <major>.<minor>.<revision>-dev-<build>/
+    version = version.replace('/', '')
+    version_build_args = version.split('-')
+    version_components = version_build_args[0].split('.')
+    version_components.append(version_build_args[2])
+    current_version = tuple(map(int, version_components))
+    if (current_version > top_most_version):
+      top_most_version = current_version
+      top_most_version_and_build = version
+
+  if (top_most_version_and_build is None):
+      raise Exception('Url: %s \n returned %s'
+                      % (KOTLIN_RELEASE_URL, response.getcode()))
+
+  # We can now download all files related to the kotlin compiler version.
+  print("Downloading version: " + top_most_version_and_build)
+
+  kotlinc_lib = os.path.join(
+      utils.THIRD_PARTY, "kotlin", "kotlin-compiler-dev", "kotlinc", "lib")
+
+  utils.DownloadFromGoogleCloudStorage(
+      os.path.join(
+          utils.THIRD_PARTY, "kotlin", "kotlin-compiler-dev.tar.gz.sha1"))
+
+  download_and_save(
+      JETBRAINS_KOTLIN_MAVEN_URL + "kotlin-compiler/{0}/kotlin-compiler-{0}.jar"
+      .format(top_most_version_and_build), kotlinc_lib, "kotlin-compiler.jar")
+  download_and_save(
+      JETBRAINS_KOTLIN_MAVEN_URL + "kotlin-stdlib/{0}/kotlin-stdlib-{0}.jar"
+      .format(top_most_version_and_build), kotlinc_lib, "kotlin-stdlib.jar")
+  download_and_save(
+      JETBRAINS_KOTLIN_MAVEN_URL + "kotlin-reflect/{0}/kotlin-reflect-{0}.jar"
+      .format(top_most_version_and_build), kotlinc_lib, "kotlin-reflect.jar")
+
+
+def download_and_save(url, path, name):
+  print('Downloading: ' + url)
+  urllib.urlretrieve(url, os.path.join(path, name))
+
+
+if __name__ == '__main__':
+  sys.exit(download_newest())
+
diff --git a/tools/test.py b/tools/test.py
index 6553358..c719ea2 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -8,6 +8,7 @@
 # force the tests to run, even if no input changed.
 
 import archive_desugar_jdk_libs
+import download_kotlin_dev
 import notify
 import optparse
 import os
@@ -187,6 +188,10 @@
       '--stacktrace',
       help='Pass --stacktrace to the gradle run',
       default=False, action='store_true')
+  result.add_option('--kotlin-compiler-dev',
+                    help='Specify to download a kotlin dev compiler and run '
+                         'tests with that',
+                    default=False, action='store_true')
   return result.parse_args()
 
 def archive_failures():
@@ -275,6 +280,9 @@
     gradle_args.append('-Pprint_full_stacktraces')
   if options.print_obfuscated_stacktraces:
     gradle_args.append('-Pprint_obfuscated_stacktraces')
+  if options.kotlin_compiler_dev:
+    gradle_args.append('-Dcom.android.tools.r8.kotlincompilerdev=1')
+    download_kotlin_dev.download_newest()
   if os.name == 'nt':
     # temporary hack
     gradle_args.append('-Pno_internal')