Merge commit 'dad89985ee69f78f00d4559d230fac0ddf71d3c8' into dev-release
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index afa5f60..87925c3 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -143,6 +143,7 @@
       name: "linux"
       mixins: "linux"
       mixins: "normal"
+      priority: 26
       recipe {
         properties: "tool:r8"
       }
diff --git a/infra/config/global/luci-scheduler.cfg b/infra/config/global/luci-scheduler.cfg
index ab75708..f7f3305 100644
--- a/infra/config/global/luci-scheduler.cfg
+++ b/infra/config/global/luci-scheduler.cfg
@@ -99,6 +99,7 @@
   acl_sets: "default"
   triggering_policy: {
     max_batch_size: 1
+    max_concurrent_invocations: 3
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
@@ -125,7 +126,7 @@
   id: "linux"
   acl_sets: "default"
   triggering_policy: {
-    max_concurrent_invocations: 3
+    max_concurrent_invocations: 6
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
@@ -138,7 +139,7 @@
   id: "linux-android-4.0.4"
   acl_sets: "default"
   triggering_policy: {
-    max_concurrent_invocations: 3
+    max_concurrent_invocations: 6
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
@@ -152,6 +153,7 @@
   acl_sets: "default"
   triggering_policy: {
     max_batch_size: 1
+    max_concurrent_invocations: 3
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
@@ -164,7 +166,7 @@
   id: "linux-android-4.4.4"
   acl_sets: "default"
   triggering_policy: {
-    max_concurrent_invocations: 2
+    max_concurrent_invocations: 6
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
@@ -178,6 +180,7 @@
   acl_sets: "default"
   triggering_policy: {
     max_batch_size: 1
+    max_concurrent_invocations: 3
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
@@ -190,7 +193,7 @@
   id: "linux-android-5.1.1"
   acl_sets: "default"
   triggering_policy: {
-    max_concurrent_invocations: 2
+    max_concurrent_invocations: 6
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
@@ -204,6 +207,7 @@
   acl_sets: "default"
   triggering_policy: {
     max_batch_size: 1
+    max_concurrent_invocations: 3
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
@@ -216,7 +220,7 @@
   id: "linux-android-6.0.1"
   acl_sets: "default"
   triggering_policy: {
-    max_concurrent_invocations: 3
+    max_concurrent_invocations: 6
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
@@ -230,6 +234,7 @@
   acl_sets: "default"
   triggering_policy: {
     max_batch_size: 1
+    max_concurrent_invocations: 3
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
@@ -242,7 +247,7 @@
   id: "linux-android-7.0.0"
   acl_sets: "default"
   triggering_policy: {
-    max_concurrent_invocations: 2
+    max_concurrent_invocations: 6
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
@@ -256,6 +261,7 @@
   acl_sets: "default"
   triggering_policy: {
     max_batch_size: 1
+    max_concurrent_invocations: 3
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
@@ -268,7 +274,7 @@
   id: "linux-android-8.1.0"
   acl_sets: "default"
   triggering_policy: {
-    max_concurrent_invocations: 2
+    max_concurrent_invocations: 6
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
@@ -282,6 +288,7 @@
   acl_sets: "default"
   triggering_policy: {
     max_batch_size: 1
+    max_concurrent_invocations: 3
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
@@ -295,7 +302,7 @@
   id: "linux-android-9.0.0"
   acl_sets: "default"
   triggering_policy: {
-    max_concurrent_invocations: 2
+    max_concurrent_invocations: 6
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
@@ -309,6 +316,7 @@
   acl_sets: "default"
   triggering_policy: {
     max_batch_size: 1
+    max_concurrent_invocations: 3
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
@@ -321,7 +329,7 @@
   id: "linux-android-10.0.0"
   acl_sets: "default"
   triggering_policy: {
-    max_concurrent_invocations: 2
+    max_concurrent_invocations: 6
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
@@ -335,6 +343,7 @@
   acl_sets: "default"
   triggering_policy: {
     max_batch_size: 1
+    max_concurrent_invocations: 3
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
@@ -366,6 +375,9 @@
 job {
   id: "linux-run-on-as-app"
   acl_sets: "default"
+  triggering_policy: {
+    max_concurrent_invocations: 3
+  }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
     bucket: "luci.r8.ci"
@@ -376,6 +388,9 @@
 job {
   id: "linux-run-on-as-app-recompilation"
   acl_sets: "default"
+  triggering_policy: {
+    max_concurrent_invocations: 3
+  }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
     bucket: "luci.r8.ci"
@@ -388,6 +403,7 @@
   acl_sets: "default"
   triggering_policy: {
     max_batch_size: 1
+    max_concurrent_invocations: 3
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
@@ -425,6 +441,7 @@
   acl_sets: "default"
   triggering_policy: {
     max_batch_size: 1
+    max_concurrent_invocations: 3
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
diff --git a/src/library_desugar/desugar_jdk_libs.json b/src/library_desugar/desugar_jdk_libs.json
index 5bdc91e..049699a 100644
--- a/src/library_desugar/desugar_jdk_libs.json
+++ b/src/library_desugar/desugar_jdk_libs.json
@@ -2,7 +2,7 @@
   "configuration_format_version": 3,
   "group_id" : "com.tools.android",
   "artifact_id" : "desugar_jdk_libs",
-  "version": "1.0.9",
+  "version": "1.0.10",
   "required_compilation_api_level": 26,
   "synthesized_library_classes_package_prefix": "j$.",
   "common_flags": [
@@ -84,7 +84,8 @@
       },
       "retarget_lib_member": {
         "java.util.Date#toInstant": "java.util.DesugarDate",
-        "java.util.GregorianCalendar#toZonedDateTime": "java.util.DesugarGregorianCalendar"
+        "java.util.GregorianCalendar#toZonedDateTime": "java.util.DesugarGregorianCalendar",
+        "java.util.TimeZone#toZoneId": "java.util.DesugarTimeZone"
       },
       "custom_conversion": {
         "java.time.ZonedDateTime": "java.time.TimeConversions",
@@ -163,7 +164,9 @@
         "java.util.Date#from": "java.util.DesugarDate",
         "java.util.Date#toInstant": "java.util.DesugarDate",
         "java.util.GregorianCalendar#from": "java.util.DesugarGregorianCalendar",
-        "java.util.GregorianCalendar#toZonedDateTime": "java.util.DesugarGregorianCalendar"
+        "java.util.GregorianCalendar#toZonedDateTime": "java.util.DesugarGregorianCalendar",
+        "java.util.TimeZone#getTimeZone": "java.util.DesugarTimeZone",
+        "java.util.TimeZone#toZoneId": "java.util.DesugarTimeZone"
       },
       "custom_conversion": {
         "java.time.ZonedDateTime": "java.time.TimeConversions",
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index c40d755..b456ecb 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -187,7 +187,7 @@
             executor);
       }
 
-      AppView<?> appView = AppView.createForD8(appInfo, options, rewritePrefix);
+      AppView<?> appView = AppView.createForD8(appInfo, rewritePrefix);
 
       IRConverter converter = new IRConverter(appView, timing, printer);
       app = converter.convert(app, executor);
@@ -354,7 +354,7 @@
       throws IOException, ExecutionException {
     final CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
 
-    IRConverter converter = new IRConverter(appInfo, options, timing, printer);
+    IRConverter converter = new IRConverter(appInfo, timing, printer);
     application = converter.convert(application, executor);
 
     if (options.printCfg) {
diff --git a/src/main/java/com/android/tools/r8/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
index dcf33f1..bbee24d 100644
--- a/src/main/java/com/android/tools/r8/GenerateLintFiles.java
+++ b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
@@ -312,7 +312,7 @@
 
     // Write a header jar with the desugared APIs.
     AppInfo appInfo = new AppInfo(app);
-    AppView<?> appView = AppView.createForD8(appInfo, options);
+    AppView<?> appView = AppView.createForD8(appInfo);
     CfApplicationWriter writer =
         new CfApplicationWriter(
             builder.build(),
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
index 42290d2..bacd375 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -50,7 +50,7 @@
       DirectMappedDexApplication application =
           new ApplicationReader(app, options, timing).read(executor).toDirect();
       AppView<? extends AppInfoWithClassHierarchy> appView =
-          AppView.createForR8(new AppInfoWithClassHierarchy(application), options);
+          AppView.createForR8(new AppInfoWithClassHierarchy(application));
       appView.setAppServices(AppServices.builder(appView).build());
 
       MainDexListBuilder.checkForAssumedLibraryTypes(appView.appInfo());
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index 4b35d17..f5059e4 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -128,7 +128,7 @@
       DexApplication app = new L8TreePruner(options).prune(lazyApp, rewritePrefix);
       AppInfo appInfo = new AppInfo(app);
 
-      AppView<?> appView = AppView.createForL8(appInfo, options, rewritePrefix);
+      AppView<?> appView = AppView.createForL8(appInfo, rewritePrefix);
       IRConverter converter = new IRConverter(appView, timing);
 
       if (!options.testing.disableL8AnnotationRemoval) {
diff --git a/src/main/java/com/android/tools/r8/PrintSeeds.java b/src/main/java/com/android/tools/r8/PrintSeeds.java
index 7c2302a..3f8b70e 100644
--- a/src/main/java/com/android/tools/r8/PrintSeeds.java
+++ b/src/main/java/com/android/tools/r8/PrintSeeds.java
@@ -88,7 +88,7 @@
       DirectMappedDexApplication application =
           new ApplicationReader(command.getInputApp(), options, timing).read(executor).toDirect();
       AppView<? extends AppInfoWithClassHierarchy> appView =
-          AppView.createForR8(new AppInfoWithClassHierarchy(application), options);
+          AppView.createForR8(new AppInfoWithClassHierarchy(application));
       appView.setAppServices(AppServices.builder(appView).build());
       SubtypingInfo subtypingInfo = new SubtypingInfo(application.allClasses(), application);
       RootSet rootSet =
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 5998450..07948a1 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -100,6 +100,7 @@
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
 import com.google.common.io.ByteStreams;
@@ -110,7 +111,6 @@
 import java.io.PrintStream;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -209,6 +209,10 @@
     try {
       Marker marker = options.getMarker(Tool.R8);
       assert marker != null;
+      // Get the markers from the input which are different from the one created for this
+      // compilation
+      Set<Marker> markers = new HashSet<>(options.itemFactory.extractMarkers());
+      markers.remove(marker);
       if (options.isGeneratingClassFiles()) {
         new CfApplicationWriter(
                 application, appView, options, marker, graphLense, namingLens, proguardMapSupplier)
@@ -218,7 +222,8 @@
                 application,
                 appView,
                 options,
-                Collections.singletonList(marker),
+                // Ensure that the marker for this compilation is the first in the list.
+                ImmutableList.<Marker>builder().add(marker).addAll(markers).build(),
                 graphLense,
                 initClassLens,
                 namingLens,
@@ -274,7 +279,7 @@
       inputApp.closeInternalArchiveProviders();
 
       AppView<AppInfoWithClassHierarchy> appView =
-          AppView.createForR8(new AppInfoWithClassHierarchy(application), options);
+          AppView.createForR8(new AppInfoWithClassHierarchy(application));
       appView.setAppServices(AppServices.builder(appView).build());
 
       // Check for potentially having pass-through of Cf-code for kotlin libraries.
@@ -606,7 +611,7 @@
 
       // Overwrite SourceFile if specified. This step should be done after IR conversion.
       timing.begin("Rename SourceFile");
-      new SourceFileRewriter(appView).run();
+      new SourceFileRewriter(appViewWithLiveness).run();
       timing.end();
 
       // Collect the already pruned types before creating a new app info without liveness.
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 5d102bdf..8986801 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -235,8 +235,8 @@
       proguardMapId = proguardMapSupplier.writeProguardMap();
     }
 
-    // If we do have a map then we're called from R8. In that case we have exactly one marker.
-    assert proguardMapId == null || (markers != null && markers.size() == 1);
+    // If we do have a map then we're called from R8. In that case we have at least one marker.
+    assert proguardMapId == null || (markers != null && markers.size() >= 1);
 
     if (markers != null && !markers.isEmpty()) {
       if (proguardMapId != null) {
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 9763f50..245a9ab 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -20,6 +20,7 @@
 import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoFactory;
 import com.android.tools.r8.ir.optimize.library.LibraryMemberOptimizer;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.KeepInfoCollection;
 import com.android.tools.r8.shaking.LibraryModeledPredicate;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
 import com.android.tools.r8.utils.InternalOptions;
@@ -43,12 +44,13 @@
   private T appInfo;
   private AppInfoWithClassHierarchy appInfoForDesugaring;
   private AppServices appServices;
-  private final DexItemFactory dexItemFactory;
   private final WholeProgramOptimizations wholeProgramOptimizations;
   private GraphLense graphLense;
   private InitClassLens initClassLens;
-  private final InternalOptions options;
   private RootSet rootSet;
+  // This should perferably always be obtained via AppInfoWithLiveness.
+  // Currently however the liveness may be downgraded thus loosing the computed keep info.
+  private KeepInfoCollection keepInfo = null;
   private final AbstractValueFactory abstractValueFactory = new AbstractValueFactory();
   private final InstanceFieldInitializationInfoFactory instanceFieldInitializationInfoFactory =
       new InstanceFieldInitializationInfoFactory();
@@ -74,32 +76,19 @@
   private Map<DexClass, DexValueString> sourceDebugExtensions = new IdentityHashMap<>();
 
   private AppView(
-      T appInfo, WholeProgramOptimizations wholeProgramOptimizations, InternalOptions options) {
-    this(
-        appInfo,
-        wholeProgramOptimizations,
-        options,
-        appInfo == null
-            ? PrefixRewritingMapper.empty()
-            : options.desugaredLibraryConfiguration.createPrefixRewritingMapper(options));
-  }
-
-  private AppView(
       T appInfo,
       WholeProgramOptimizations wholeProgramOptimizations,
-      InternalOptions options,
       PrefixRewritingMapper mapper) {
+    assert appInfo != null;
     this.appInfo = appInfo;
-    this.dexItemFactory = appInfo != null ? appInfo.dexItemFactory() : null;
     this.wholeProgramOptimizations = wholeProgramOptimizations;
     this.graphLense = GraphLense.getIdentityLense();
     this.initClassLens = InitClassLens.getDefault();
     this.methodProcessingIdFactory =
-        new MethodProcessingId.Factory(options.testing.methodProcessingIdConsumer);
-    this.options = options;
+        new MethodProcessingId.Factory(options().testing.methodProcessingIdConsumer);
     this.rewritePrefix = mapper;
 
-    if (enableWholeProgramOptimizations() && options.callSiteOptimizationOptions().isEnabled()) {
+    if (enableWholeProgramOptimizations() && options().callSiteOptimizationOptions().isEnabled()) {
       this.callSiteOptimizationInfoPropagator =
           new CallSiteOptimizationInfoPropagator(withLiveness());
     } else {
@@ -108,7 +97,7 @@
 
     this.libraryMemberOptimizer = new LibraryMemberOptimizer(this);
 
-    if (enableWholeProgramOptimizations() && options.protoShrinking().isProtoShrinkingEnabled()) {
+    if (enableWholeProgramOptimizations() && options().protoShrinking().isProtoShrinkingEnabled()) {
       this.protoShrinker = new ProtoShrinker(withLiveness());
     } else {
       this.protoShrinker = null;
@@ -120,27 +109,34 @@
     return libraryMemberOptimizer.isModeled(type);
   }
 
-  public static <T extends AppInfo> AppView<T> createForD8(T appInfo, InternalOptions options) {
-    return new AppView<>(appInfo, WholeProgramOptimizations.OFF, options);
+  private static <T extends AppInfo> PrefixRewritingMapper defaultPrefixRewritingMapper(T appInfo) {
+    InternalOptions options = appInfo.options();
+    return options.desugaredLibraryConfiguration.createPrefixRewritingMapper(options);
+  }
+
+  public static <T extends AppInfo> AppView<T> createForD8(T appInfo) {
+    return new AppView<>(
+        appInfo, WholeProgramOptimizations.OFF, defaultPrefixRewritingMapper(appInfo));
   }
 
   public static <T extends AppInfo> AppView<T> createForD8(
-      T appInfo, InternalOptions options, PrefixRewritingMapper mapper) {
-    return new AppView<>(appInfo, WholeProgramOptimizations.OFF, options, mapper);
+      T appInfo, PrefixRewritingMapper mapper) {
+    return new AppView<>(appInfo, WholeProgramOptimizations.OFF, mapper);
   }
 
-  public static <T extends AppInfo> AppView<T> createForR8(T appInfo, InternalOptions options) {
-    return new AppView<>(appInfo, WholeProgramOptimizations.ON, options);
+  public static <T extends AppInfo> AppView<T> createForR8(T appInfo) {
+    return new AppView<>(
+        appInfo, WholeProgramOptimizations.ON, defaultPrefixRewritingMapper(appInfo));
   }
 
   public static <T extends AppInfo> AppView<T> createForL8(
-      T appInfo, InternalOptions options, PrefixRewritingMapper mapper) {
-    return new AppView<>(appInfo, WholeProgramOptimizations.OFF, options, mapper);
+      T appInfo, PrefixRewritingMapper mapper) {
+    return new AppView<>(appInfo, WholeProgramOptimizations.OFF, mapper);
   }
 
-  public static <T extends AppInfo> AppView<T> createForRelocator(
-      T appInfo, InternalOptions options) {
-    return new AppView<>(appInfo, WholeProgramOptimizations.OFF, options);
+  public static <T extends AppInfo> AppView<T> createForRelocator(T appInfo) {
+    return new AppView<>(
+        appInfo, WholeProgramOptimizations.OFF, defaultPrefixRewritingMapper(appInfo));
   }
 
   public AbstractValueFactory abstractValueFactory() {
@@ -184,6 +180,9 @@
     if (appInfo != previous) {
       previous.markObsolete();
     }
+    if (appInfo.hasLiveness()) {
+      keepInfo = appInfo.withLiveness().getKeepInfo();
+    }
     @SuppressWarnings("unchecked")
     AppView<U> appViewWithSpecializedAppInfo = (AppView<U>) this;
     return appViewWithSpecializedAppInfo;
@@ -252,7 +251,7 @@
 
   @Override
   public DexItemFactory dexItemFactory() {
-    return dexItemFactory;
+    return appInfo.dexItemFactory();
   }
 
   public boolean enableWholeProgramOptimizations() {
@@ -344,7 +343,7 @@
   }
 
   public boolean canUseInitClass() {
-    return options.isShrinking() && !initClassLens.isFinal();
+    return options().isShrinking() && !initClassLens.isFinal();
   }
 
   public InitClassLens initClassLens() {
@@ -373,7 +372,7 @@
   }
 
   public InternalOptions options() {
-    return options;
+    return appInfo.options();
   }
 
   public RootSet rootSet() {
@@ -385,6 +384,10 @@
     this.rootSet = rootSet;
   }
 
+  public KeepInfoCollection getKeepInfo() {
+    return keepInfo;
+  }
+
   public MergedClassesCollection allMergedClasses() {
     MergedClassesCollection collection = new MergedClassesCollection();
     if (horizontallyMergedLambdaClasses != null) {
@@ -455,14 +458,14 @@
   }
 
   public boolean isCfByteCodePassThrough(DexEncodedMethod method) {
-    if (!options.isGeneratingClassFiles()) {
+    if (!options().isGeneratingClassFiles()) {
       return false;
     }
     if (cfByteCodePassThrough.contains(method.method)) {
       return true;
     }
-    return options.testing.cfByteCodePassThrough != null
-        && options.testing.cfByteCodePassThrough.test(method.method);
+    return options().testing.cfByteCodePassThrough != null
+        && options().testing.cfByteCodePassThrough.test(method.method);
   }
 
   public boolean hasCfByteCodePassThroughMethods() {
diff --git a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
index 1315f4a..3cd5fc5 100644
--- a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
+++ b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
@@ -140,7 +140,7 @@
 
   private void writeIR(ProgramMethod method, PrintStream ps) {
     CfgPrinter printer = new CfgPrinter();
-    IRConverter converter = new IRConverter(appInfo, options, timing, printer);
+    IRConverter converter = new IRConverter(appInfo, timing, printer);
     OneTimeMethodProcessor methodProcessor =
         OneTimeMethodProcessor.create(method, methodProcessingIdFactory);
     methodProcessor.forEachWave(
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 5f6f83b..0809d00 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
@@ -372,8 +372,8 @@
     this(appView, timing, printer, MainDexClasses.NONE);
   }
 
-  public IRConverter(AppInfo appInfo, InternalOptions options, Timing timing, CfgPrinter printer) {
-    this(AppView.createForD8(appInfo, options), timing, printer, MainDexClasses.NONE);
+  public IRConverter(AppInfo appInfo, Timing timing, CfgPrinter printer) {
+    this(AppView.createForD8(appInfo), timing, printer, MainDexClasses.NONE);
   }
 
   private boolean enableTwrCloseResourceDesugaring() {
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 c0cdc54..fbf5a15 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
@@ -107,7 +107,7 @@
           new ApplicationReader(androidApp, options, Timing.empty()).read(executor);
       appInfo = new AppInfo(app);
     }
-    AppView<?> appView = AppView.createForD8(appInfo, options, rewritePrefix);
+    AppView<?> appView = AppView.createForD8(appInfo, rewritePrefix);
     BackportedMethodRewriter.RewritableMethods rewritableMethods =
         new BackportedMethodRewriter.RewritableMethods(options, appView);
     rewritableMethods.visit(methods::add);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
index a1af388..d687d19 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.ExceptionDiagnostic;
 import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.SemanticVersion;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
@@ -22,14 +23,7 @@
 public class DesugaredLibraryConfigurationParser {
 
   public static final int MAX_SUPPORTED_VERSION = 4;
-
-  private static final String MIN_DESUGARED_LIBRARY_WITH_COMMON_FLAGS = "1.0.9";
-
-  private static final String UNSUPPORTED_MESSAGE =
-      "Unsupported desugared library version, please upgrade the"
-          + " desugared library to at least version "
-          + MIN_DESUGARED_LIBRARY_WITH_COMMON_FLAGS
-          + ".";
+  public static final SemanticVersion MIN_SUPPORTED_VERSION = new SemanticVersion(1, 0, 9);
 
   static final String CONFIGURATION_FORMAT_VERSION_KEY = "configuration_format_version";
   static final String VERSION_KEY = "version";
@@ -73,15 +67,11 @@
   }
 
   private JsonElement required(JsonObject json, String key) {
-    return required(
-        json,
-        key,
-        "Invalid desugared library configuration. " + "Expected required key '" + key + "'");
-  }
-
-  private JsonElement required(JsonObject json, String key, String message) {
     if (!json.has(key)) {
-      throw reporter.fatalError(new StringDiagnostic(message, origin));
+      throw reporter.fatalError(
+          new StringDiagnostic(
+              "Invalid desugared library configuration. Expected required key '" + key + "'",
+              origin));
     }
     return json.get(key);
   }
@@ -115,6 +105,18 @@
     }
 
     String version = required(jsonConfig, VERSION_KEY).getAsString();
+    SemanticVersion semanticVersion = SemanticVersion.parse(version);
+    if (!semanticVersion.isNewerOrEqual(MIN_SUPPORTED_VERSION)) {
+      throw reporter.fatalError(
+          new StringDiagnostic(
+              "Unsupported desugared library version: "
+                  + version
+                  + ", please upgrade the desugared library to at least version "
+                  + MIN_SUPPORTED_VERSION
+                  + ".",
+              origin));
+    }
+
     String groupID = required(jsonConfig, GROUP_ID_KEY).getAsString();
     String artifactID = required(jsonConfig, ARTIFACT_ID_KEY).getAsString();
     String identifier = String.join(":", groupID, artifactID, version);
@@ -126,7 +128,7 @@
         required(jsonConfig, REQUIRED_COMPILATION_API_LEVEL_KEY).getAsInt();
     configurationBuilder.setRequiredCompilationAPILevel(
         AndroidApiLevel.getAndroidApiLevel(required_compilation_api_level));
-    JsonElement commonFlags = required(jsonConfig, COMMON_FLAGS_KEY, UNSUPPORTED_MESSAGE);
+    JsonElement commonFlags = required(jsonConfig, COMMON_FLAGS_KEY);
     JsonElement libraryFlags = required(jsonConfig, LIBRARY_FLAGS_KEY);
     JsonElement programFlags = required(jsonConfig, PROGRAM_FLAGS_KEY);
     parseFlagsList(commonFlags.getAsJsonArray());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
index 7272948..b1d8880 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
@@ -322,7 +322,8 @@
       return null;
     }
 
-    if (appView.options().isMinifying() && appView.rootSet().mayBeMinified(holder, appView)) {
+    if (appView.enableWholeProgramOptimizations()
+        && appView.withLiveness().appInfo().isMinificationAllowed(holder)) {
       if (invokedMethod == dexItemFactory.classMethods.getName) {
         return new DexItemBasedValueString(holder, ClassNameComputationInfo.getInstance(NAME));
       }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
index d00100a..af2dee2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
@@ -354,7 +354,8 @@
         continue;
       }
       boolean mayBeRenamed =
-          appView.options().isMinifying() && appView.rootSet().mayBeMinified(holder.type, appView);
+          appView.enableWholeProgramOptimizations()
+              && appView.withLiveness().appInfo().isMinificationAllowed(holder.type);
       // b/120138731: Filter out escaping uses. In such case, the result of this optimization will
       // be stored somewhere, which can lead to a regression if the corresponding class is in a deep
       // package hierarchy. For local cases, it is likely a one-time computation, but make sure the
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index 2ee7485..3d27387 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -130,10 +130,10 @@
   static class MinificationClassNamingStrategy extends BaseMinificationNamingStrategy
       implements ClassNamingStrategy {
 
-    final AppView<?> appView;
+    final AppView<AppInfoWithLiveness> appView;
     final DexItemFactory factory;
 
-    MinificationClassNamingStrategy(AppView<?> appView) {
+    MinificationClassNamingStrategy(AppView<AppInfoWithLiveness> appView) {
       super(
           appView.options().getProguardConfiguration().getClassObfuscationDictionary(),
           appView.options().getProguardConfiguration().hasDontUseMixedCaseClassnames());
@@ -169,7 +169,7 @@
 
     @Override
     public DexString reservedDescriptor(DexType type) {
-      if (appView.rootSet().mayNotBeMinified(type, appView)) {
+      if (!appView.appInfo().isMinificationAllowed(type)) {
         return type.descriptor;
       }
       return null;
@@ -207,11 +207,11 @@
   static class MinifierMemberNamingStrategy extends BaseMinificationNamingStrategy
       implements MemberNamingStrategy {
 
-    final AppView<?> appView;
+    final AppView<AppInfoWithLiveness> appView;
     private final DexItemFactory factory;
     private final boolean desugaredLibraryRenaming;
 
-    public MinifierMemberNamingStrategy(AppView<?> appView) {
+    public MinifierMemberNamingStrategy(AppView<AppInfoWithLiveness> appView) {
       super(appView.options().getProguardConfiguration().getObfuscationDictionary(), false);
       this.appView = appView;
       this.factory = appView.dexItemFactory();
@@ -254,7 +254,7 @@
       if (!allowMemberRenaming(holder)
           || holder.accessFlags.isAnnotation()
           || method.accessFlags.isConstructor()
-          || appView.rootSet().mayNotBeMinified(method.method, appView)) {
+          || !appView.appInfo().isMinificationAllowed(method.method)) {
         return method.method.name;
       }
       if (desugaredLibraryRenaming
@@ -268,7 +268,7 @@
 
     @Override
     public DexString getReservedName(DexEncodedField field, DexClass holder) {
-      if (holder.isLibraryClass() || appView.rootSet().mayNotBeMinified(field.field, appView)) {
+      if (holder.isLibraryClass() || !appView.appInfo().isMinificationAllowed(field.field)) {
         return field.field.name;
       }
       return null;
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
index 57f63fa..ad27200 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -203,7 +203,7 @@
       if (clazz == null || !appView.options().isMinifying()) {
         notMappedReferences.add(type);
       } else if (appView.options().isMinifying()
-          && appView.rootSet().mayNotBeMinified(type, appView)) {
+          && !appView.appInfo().isMinificationAllowed(type)) {
         notMappedReferences.add(type);
       }
     }
@@ -392,7 +392,9 @@
     private final Set<String> mappedNames;
 
     ApplyMappingClassNamingStrategy(
-        AppView<?> appView, Map<DexType, DexString> mappings, Set<String> mappedNames) {
+        AppView<AppInfoWithLiveness> appView,
+        Map<DexType, DexString> mappings,
+        Set<String> mappedNames) {
       super(appView);
       this.mappings = mappings;
       this.mappedNames = mappedNames;
@@ -402,7 +404,7 @@
     public DexString next(
         DexType type, char[] packagePrefix, InternalNamingState state, Predicate<String> isUsed) {
       assert !mappings.containsKey(type);
-      assert appView.rootSet().mayBeMinified(type, appView);
+      assert appView.appInfo().isMinificationAllowed(type);
       return super.next(
           type,
           packagePrefix,
@@ -427,15 +429,12 @@
       if (clazz.isNotProgramClass() && mappings.containsKey(type)) {
         return mappings.get(type);
       }
-      if (clazz.isProgramClass() && appView.rootSet().mayBeMinified(type, appView)) {
-        if (mappings.containsKey(type)) {
+      if (clazz.isProgramClass()) {
+        if (appView.appInfo().isMinificationAllowed(type)) {
           return mappings.get(type);
         }
-        return null;
-      } else if (clazz.isProgramClass()
-          && !appView.rootSet().mayBeMinified(type, appView)
-          && mappings.containsKey(type)) {
-        // TODO(b/136694827): Report a warning here since the user may find this not intuitive.
+        // TODO(b/136694827): Report a warning here if in the mapping since the user may find this
+        //  non intuitive.
       }
       return type.descriptor;
     }
@@ -453,7 +452,7 @@
     private final Reporter reporter;
 
     public ApplyMappingMemberNamingStrategy(
-        AppView<?> appView, Map<DexReference, MemberNaming> mappedNames) {
+        AppView<AppInfoWithLiveness> appView, Map<DexReference, MemberNaming> mappedNames) {
       super(appView);
       this.mappedNames = mappedNames;
       this.factory = appView.dexItemFactory();
@@ -477,7 +476,7 @@
         nextName = reservedName;
       } else {
         assert !mappedNames.containsKey(reference);
-        assert appView.rootSet().mayBeMinified(reference, appView);
+        assert appView.appInfo().isMinificationAllowed(reference);
         nextName = super.next(method, internalState, isAvailable);
       }
       assert nextName == reference.name || !method.isInitializer();
@@ -500,7 +499,7 @@
         return reservedName;
       }
       assert !mappedNames.containsKey(reference);
-      assert appView.rootSet().mayBeMinified(reference, appView);
+      assert appView.appInfo().isMinificationAllowed(reference);
       return super.next(field, internalState, isAvailable);
     }
 
diff --git a/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java b/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
index a5ead29..1e57331 100644
--- a/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
+++ b/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.DexDebugEvent.SetFile;
 import com.android.tools.r8.graph.DexDebugInfo;
 import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.ProguardConfiguration;
 import java.util.Arrays;
 
@@ -20,9 +21,9 @@
  */
 public class SourceFileRewriter {
 
-  private final AppView<?> appView;
+  private final AppView<AppInfoWithLiveness> appView;
 
-  public SourceFileRewriter(AppView<?> appView) {
+  public SourceFileRewriter(AppView<AppInfoWithLiveness> appView) {
     this.appView = appView;
   }
 
@@ -45,7 +46,7 @@
       // of ART.
       if (!hasRenameSourceFileAttribute
           && proguardConfiguration.getKeepAttributes().sourceFile
-          && appView.rootSet().mayNotBeMinified(clazz.type, appView)) {
+          && !appView.appInfo().isMinificationAllowed(clazz.type)) {
         continue;
       }
       clazz.sourceFile = defaultRenaming;
diff --git a/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
index 44efffc..db4613f 100644
--- a/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
@@ -165,7 +165,7 @@
       boolean wasSeen = methodPoolCollection.markIfNotSeen(holder, method.method);
       if (wasSeen) {
         // We can't do anything further because even renaming is not allowed due to the keep rule.
-        if (appView.rootSet().mayNotBeMinified(method.method, appView)) {
+        if (!appView.appInfo().isMinificationAllowed(method.method)) {
           return false;
         }
         // TODO(b/111118390): Renaming will enable more private instance methods to be publicized.
diff --git a/src/main/java/com/android/tools/r8/relocator/Relocator.java b/src/main/java/com/android/tools/r8/relocator/Relocator.java
index 1e8420e..176e058 100644
--- a/src/main/java/com/android/tools/r8/relocator/Relocator.java
+++ b/src/main/java/com/android/tools/r8/relocator/Relocator.java
@@ -81,7 +81,7 @@
       DexApplication app = new ApplicationReader(inputApp, options, timing).read(executor);
 
       AppInfo appInfo = new AppInfo(app);
-      AppView<?> appView = AppView.createForRelocator(appInfo, options);
+      AppView<?> appView = AppView.createForRelocator(appInfo);
       appView.setAppServices(AppServices.builder(appView).build());
 
       SimplePackagesRewritingMapper packageRemapper = new SimplePackagesRewritingMapper(appView);
diff --git a/src/main/java/com/android/tools/r8/retrace/Retrace.java b/src/main/java/com/android/tools/r8/retrace/Retrace.java
index af2b520..3d59b8b 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -17,7 +17,11 @@
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.Timing;
+import com.google.common.base.Charsets;
 import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -117,7 +121,7 @@
   private static List<String> getStackTraceFromFile(
       String stackTracePath, DiagnosticsHandler diagnostics) {
     try {
-      return Files.readAllLines(Paths.get(stackTracePath));
+      return Files.readAllLines(Paths.get(stackTracePath), Charsets.UTF_8);
     } catch (IOException e) {
       diagnostics.error(new StringDiagnostic("Could not find stack trace file: " + stackTracePath));
       throw new RetraceAbortException();
@@ -196,7 +200,15 @@
       return;
     }
     builder.setRetracedStackTraceConsumer(
-        retraced -> System.out.print(StringUtils.lines(retraced)));
+        retraced -> {
+          try (PrintStream printStream = new PrintStream(System.out, true, Charsets.UTF_8.name())) {
+            for (String line : retraced) {
+              printStream.println(line);
+            }
+          } catch (UnsupportedEncodingException e) {
+            retraceDiagnosticsHandler.error(new StringDiagnostic(e.getMessage()));
+          }
+        });
     run(builder.build());
   }
 
@@ -210,7 +222,7 @@
   }
 
   private static List<String> getStackTraceFromStandardInput() {
-    Scanner sc = new Scanner(System.in);
+    Scanner sc = new Scanner(new InputStreamReader(System.in, Charsets.UTF_8));
     List<String> readLines = new ArrayList<>();
     while (sc.hasNext()) {
       readLines.add(sc.nextLine());
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java b/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java
index 4973190..f1603f7 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.retrace;
 
 import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.references.Reference;
@@ -33,6 +34,9 @@
 
   private static final int NO_MATCH = -1;
 
+  private final RegularExpressionGroup[] syntheticGroups =
+      new RegularExpressionGroup[] {new SourceFileLineNumberGroup()};
+
   private final RegularExpressionGroup[] groups =
       new RegularExpressionGroup[] {
         new TypeNameGroup(),
@@ -130,19 +134,17 @@
       String regularExpression, List<RegularExpressionGroupHandler> handlers) {
     int currentIndex = 0;
     int captureGroupIndex = 0;
+    regularExpression = registerSyntheticGroups(regularExpression);
     while (currentIndex < regularExpression.length()) {
       RegularExpressionGroup firstGroup = null;
       int firstIndexFromCurrent = regularExpression.length();
       for (RegularExpressionGroup group : groups) {
-        int nextIndexOf = regularExpression.indexOf(group.shortName(), currentIndex);
-        if (nextIndexOf > NO_MATCH && nextIndexOf < firstIndexFromCurrent) {
-          // Check if previous character in the regular expression is not \\ to ensure not
-          // overriding a matching on shortName.
-          if (nextIndexOf > 0 && regularExpression.charAt(nextIndexOf - 1) == '\\') {
-            continue;
-          }
+        int firstIndex =
+            firstIndexOfGroup(
+                currentIndex, firstIndexFromCurrent, regularExpression, group.shortName());
+        if (firstIndex > NO_MATCH) {
           firstGroup = group;
-          firstIndexFromCurrent = nextIndexOf;
+          firstIndexFromCurrent = firstIndex;
         }
       }
       if (firstGroup != null) {
@@ -161,6 +163,49 @@
     return regularExpression;
   }
 
+  private int firstIndexOfGroup(int startIndex, int endIndex, String expression, String shortName) {
+    int nextIndexOf = startIndex;
+    while (nextIndexOf != NO_MATCH) {
+      nextIndexOf = expression.indexOf(shortName, nextIndexOf);
+      if (nextIndexOf > NO_MATCH) {
+        if (nextIndexOf < endIndex && !isEscaped(expression, nextIndexOf)) {
+          return nextIndexOf;
+        }
+        nextIndexOf++;
+      }
+    }
+    return NO_MATCH;
+  }
+
+  private boolean isEscaped(String expression, int index) {
+    boolean escaped = false;
+    while (index > 0 && expression.charAt(--index) == '\\') {
+      escaped = !escaped;
+    }
+    return escaped;
+  }
+
+  private String registerSyntheticGroups(String regularExpression) {
+    boolean modifiedExpression;
+    do {
+      modifiedExpression = false;
+      for (RegularExpressionGroup syntheticGroup : syntheticGroups) {
+        int firstIndex =
+            firstIndexOfGroup(
+                0, regularExpression.length(), regularExpression, syntheticGroup.shortName());
+        if (firstIndex > NO_MATCH) {
+          regularExpression =
+              regularExpression.substring(0, firstIndex)
+                  + syntheticGroup.subExpression()
+                  + regularExpression.substring(firstIndex + syntheticGroup.shortName().length());
+          // Loop as long as we can replace.
+          modifiedExpression = true;
+        }
+      }
+    } while (modifiedExpression);
+    return regularExpression;
+  }
+
   static class RetraceString {
 
     private final Element classContext;
@@ -387,6 +432,10 @@
     abstract String subExpression();
 
     abstract RegularExpressionGroupHandler createHandler(String captureGroup);
+
+    boolean isSynthetic() {
+      return false;
+    }
   }
 
   // TODO(b/145731185): Extend support for identifiers with strings inside back ticks.
@@ -718,6 +767,29 @@
     }
   }
 
+  private static class SourceFileLineNumberGroup extends RegularExpressionGroup {
+
+    @Override
+    String shortName() {
+      return "%S";
+    }
+
+    @Override
+    String subExpression() {
+      return "%s(?::%l)?";
+    }
+
+    @Override
+    RegularExpressionGroupHandler createHandler(String captureGroup) {
+      throw new Unreachable("Should never be called");
+    }
+
+    @Override
+    boolean isSynthetic() {
+      return true;
+    }
+  }
+
   private static final String JAVA_TYPE_REGULAR_EXPRESSION =
       "(" + javaIdentifierSegment + "\\.)*" + javaIdentifierSegment + "[\\[\\]]*";
 
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 f617c31..d3afad2 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -917,11 +917,14 @@
     return this;
   }
 
+  public boolean isMinificationAllowed(DexReference reference) {
+    return options().isMinificationEnabled()
+        && keepInfo.getInfo(reference, this).isMinificationAllowed(options());
+  }
+
   public boolean isAccessModificationAllowed(DexReference reference) {
     assert options().getProguardConfiguration().isAccessModificationAllowed();
-    return keepInfo
-        .getInfo(reference, this)
-        .isAccessModificationAllowed(options().getProguardConfiguration());
+    return keepInfo.getInfo(reference, this).isAccessModificationAllowed(options());
   }
 
   public boolean isPinned(DexReference reference) {
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 e432e69..e033e78 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -92,6 +92,7 @@
 import com.android.tools.r8.shaking.DelayedRootSetActionItem.InterfaceMethodSyntheticBridgeAction;
 import com.android.tools.r8.shaking.EnqueuerWorklist.EnqueuerAction;
 import com.android.tools.r8.shaking.GraphReporter.KeepReasonWitness;
+import com.android.tools.r8.shaking.KeepInfo.Joiner;
 import com.android.tools.r8.shaking.KeepInfoCollection.MutableKeepInfoCollection;
 import com.android.tools.r8.shaking.RootSetBuilder.ConsequentRootSet;
 import com.android.tools.r8.shaking.RootSetBuilder.ItemsWithRules;
@@ -630,8 +631,13 @@
 
   private void enqueueRootClass(
       DexProgramClass clazz, Set<ProguardKeepRuleBase> rules, DexDefinition precondition) {
-    KeepReasonWitness witness = graphReporter.reportKeepClass(precondition, rules, clazz);
     keepClassWithRules(clazz, rules);
+    enqueueKeepRuleInstantiatedType(clazz, rules, precondition);
+  }
+
+  private void enqueueKeepRuleInstantiatedType(
+      DexProgramClass clazz, Set<ProguardKeepRuleBase> rules, DexDefinition precondition) {
+    KeepReasonWitness witness = graphReporter.reportKeepClass(precondition, rules, clazz);
     if (clazz.isAnnotation()) {
       workList.enqueueMarkAnnotationInstantiatedAction(clazz, witness);
     } else if (clazz.isInterface()) {
@@ -743,11 +749,12 @@
   }
 
   private void compatEnqueueHolderIfDependentNonStaticMember(
-      DexClass holder, Set<ProguardKeepRuleBase> compatRules) {
+      DexProgramClass holder, Set<ProguardKeepRuleBase> compatRules) {
     if (!forceProguardCompatibility || compatRules == null) {
       return;
     }
-    enqueueRootItem(holder, compatRules);
+    // TODO(b/120959039): This needs the set of instance member as preconditon.
+    enqueueKeepRuleInstantiatedType(holder, compatRules, null);
   }
 
   //
@@ -1583,7 +1590,7 @@
 
     rootSet.forEachDependentInstanceConstructor(
         holder, appView, this::enqueueHolderWithDependentInstanceConstructor);
-    rootSet.forEachDependentStaticMember(holder, appView, this::enqueueDependentItem);
+    rootSet.forEachDependentStaticMember(holder, appView, this::enqueueDependentMember);
     compatEnqueueHolderIfDependentNonStaticMember(
         holder, rootSet.getDependentKeepClassCompatRule(holder.getType()));
 
@@ -1648,14 +1655,17 @@
     }
   }
 
-  private void enqueueDependentItem(
-      DexDefinition precondition, DexDefinition consequent, Set<ProguardKeepRuleBase> reasons) {
+  private void enqueueDependentMember(
+      DexDefinition precondition,
+      DexEncodedMember<?, ?> consequent,
+      Set<ProguardKeepRuleBase> reasons) {
     internalEnqueueRootItem(consequent, reasons, precondition);
   }
 
   private void enqueueHolderWithDependentInstanceConstructor(
       ProgramMethod instanceInitializer, Set<ProguardKeepRuleBase> reasons) {
-    enqueueRootItem(instanceInitializer.getHolder(), reasons);
+    DexProgramClass holder = instanceInitializer.getHolder();
+    enqueueKeepRuleInstantiatedType(holder, reasons, instanceInitializer.getDefinition());
   }
 
   private void processAnnotations(DexProgramClass holder, DexDefinition annotatedItem) {
@@ -1828,36 +1838,36 @@
   }
 
   private void ensureFromLibraryOrThrow(DexType type, DexClass context) {
-    if (!mode.isInitialTreeShaking()) {
+    if (mode.isTracingMainDex()) {
       // b/72312389: android.jar contains parts of JUnit and most developers include JUnit in
       // their programs. This leads to library classes extending program classes. When tracing
       // main dex lists we allow this.
       return;
     }
-    DexClass holder = appView.definitionFor(type);
-    if (holder != null && !holder.isLibraryClass()) {
-      if (forceProguardCompatibility) {
-        // To ensure that the program works correctly we have to pin all super types and members
-        // in the tree.
-        appInfo.forEachSuperType(
-            holder,
-            (dexType, ignored) -> {
-              if (holder.isProgramClass()) {
-                DexProgramClass holderClass = holder.asProgramClass();
-                keepInfo.keepClass(holderClass);
-                rootSet.shouldNotBeMinified(holder.toReference());
-                for (DexEncodedMember<?, ?> member : holder.members()) {
-                  keepInfo.keepMember(holderClass, member);
-                  DexMember<?, ?> memberReference = member.toReference();
-                  rootSet.shouldNotBeMinified(memberReference);
-                }
-              }
-            });
-      }
-      if (dontWarnPatterns.matches(context.type)) {
-        // Ignore.
-        return;
-      }
+    DexProgramClass holder = getProgramClassOrNull(type);
+    if (holder == null) {
+      return;
+    }
+    if (forceProguardCompatibility) {
+      // To ensure that the program works correctly we have to pin all super types and members
+      // in the tree.
+      KeepReason keepReason = KeepReason.reachableFromLiveType(context.type);
+      keepClassAndAllMembers(holder, keepReason);
+      appInfo.forEachSuperType(
+          holder,
+          (dexType, ignored) -> {
+            DexProgramClass superClass = getProgramClassOrNull(dexType);
+            if (superClass != null) {
+              keepClassAndAllMembers(superClass, keepReason);
+            }
+          });
+    }
+    if (dontWarnPatterns.matches(context.type)) {
+      // Ignore.
+      return;
+    }
+    // Only report an error during the first round of treeshaking.
+    if (mode.isInitialTreeShaking()) {
       Diagnostic message =
           new StringDiagnostic(
               "Library class "
@@ -1873,6 +1883,25 @@
     }
   }
 
+  private void keepClassAndAllMembers(DexProgramClass clazz, KeepReason keepReason) {
+    KeepReasonWitness keepReasonWitness = graphReporter.registerClass(clazz, keepReason);
+    markClassAsInstantiatedWithCompatRule(clazz.asProgramClass(), keepReasonWitness);
+    keepInfo.keepClass(clazz);
+    rootSet.shouldNotBeMinified(clazz.toReference());
+    clazz.forEachProgramField(
+        field -> {
+          keepInfo.keepField(field);
+          rootSet.shouldNotBeMinified(field.getReference());
+          markFieldAsKept(field, keepReasonWitness);
+        });
+    clazz.forEachProgramMethod(
+        method -> {
+          keepInfo.keepMethod(method);
+          rootSet.shouldNotBeMinified(method.getReference());
+          markMethodAsKept(method, keepReasonWitness);
+        });
+  }
+
   private void reportMissingClass(DexType clazz) {
     assert !mode.isFinalTreeShaking()
             || appView.dexItemFactory().isPossiblyCompilerSynthesizedType(clazz)
@@ -2227,7 +2256,7 @@
   private void transitionDependentItemsForInstantiatedItem(DexProgramClass clazz) {
     do {
       // Handle keep rules that are dependent on the class being instantiated.
-      rootSet.forEachDependentNonStaticMember(clazz, appView, this::enqueueDependentItem);
+      rootSet.forEachDependentNonStaticMember(clazz, appView, this::enqueueDependentMember);
 
       // Visit the super type.
       clazz =
@@ -2639,6 +2668,24 @@
           new KotlinMetadataEnqueuerExtension(
               appView, enqueuerDefinitionSupplier, initialPrunedTypes));
     }
+    if (mode.isInitialTreeShaking()) {
+      // This is simulating the effect of the "root set" applied rules.
+      // This is done only in the initial pass, in subsequent passes the "rules" are reapplied
+      // by iterating the instances.
+      for (DexReference reference : rootSet.noObfuscation) {
+        keepInfo.evaluateRule(reference, appInfo, Joiner::disallowMinification);
+      }
+    } else if (appView.getKeepInfo() != null) {
+      appView
+          .getKeepInfo()
+          .getRuleInstances()
+          .forEach(
+              (reference, rules) -> {
+                for (Consumer<Joiner<?, ?, ?>> rule : rules) {
+                  keepInfo.evaluateRule(reference, appInfo, rule);
+                }
+              });
+    }
     if (appView.options().isShrinking() || appView.options().getProguardConfiguration() == null) {
       enqueueRootItems(rootSet.noShrinking);
     } else {
@@ -2669,43 +2716,35 @@
   }
 
   private void keepClassWithRules(DexProgramClass clazz, Set<ProguardKeepRuleBase> rules) {
-    keepInfo.joinClass(
-        clazz,
-        info ->
-            info.pin()
-                .lazyDisallowAccessModification(() -> computeDisallowAccessModification(rules)));
+    keepInfo.joinClass(clazz, info -> applyKeepRules(rules, info));
   }
 
   private void keepMethodWithRules(
       DexProgramClass holder, DexEncodedMethod method, Set<ProguardKeepRuleBase> rules) {
-    keepInfo.joinMethod(
-        holder,
-        method,
-        info ->
-            info.pin()
-                .lazyDisallowAccessModification(() -> computeDisallowAccessModification(rules)));
+    keepInfo.joinMethod(holder, method, info -> applyKeepRules(rules, info));
   }
 
   private void keepFieldWithRules(
       DexProgramClass holder, DexEncodedField field, Set<ProguardKeepRuleBase> rules) {
-    keepInfo.joinField(
-        holder,
-        field,
-        info ->
-            info.pin()
-                .lazyDisallowAccessModification(() -> computeDisallowAccessModification(rules)));
+    keepInfo.joinField(holder, field, info -> applyKeepRules(rules, info));
   }
 
-  private boolean computeDisallowAccessModification(Set<ProguardKeepRuleBase> rules) {
+  private void applyKeepRules(Set<ProguardKeepRuleBase> rules, KeepInfo.Joiner<?, ?, ?> joiner) {
     for (ProguardKeepRuleBase rule : rules) {
       ProguardKeepRuleModifiers modifiers =
           (rule.isProguardIfRule() ? rule.asProguardIfRule().getSubsequentRule() : rule)
               .getModifiers();
+      if (!modifiers.allowsShrinking) {
+        // TODO(b/159589281): Evaluate this interpretation.
+        joiner.pin();
+      }
+      if (!modifiers.allowsObfuscation) {
+        joiner.disallowMinification();
+      }
       if (!modifiers.allowsAccessModification) {
-        return true;
+        joiner.disallowAccessModification();
       }
     }
-    return false;
   }
 
   private static class SyntheticAdditions {
@@ -3283,11 +3322,11 @@
             consequentRootSet.forEachDependentInstanceConstructor(
                 clazz, appView, this::enqueueHolderWithDependentInstanceConstructor);
             consequentRootSet.forEachDependentStaticMember(
-                clazz, appView, this::enqueueDependentItem);
+                clazz, appView, this::enqueueDependentMember);
             if (objectAllocationInfoCollection.isInstantiatedDirectlyOrHasInstantiatedSubtype(
                 clazz)) {
               consequentRootSet.forEachDependentNonStaticMember(
-                  clazz, appView, this::enqueueDependentItem);
+                  clazz, appView, this::enqueueDependentMember);
             }
             compatEnqueueHolderIfDependentNonStaticMember(
                 clazz, consequentRootSet.getDependentKeepClassCompatRule(clazz.type));
@@ -3302,13 +3341,18 @@
         });
     // TODO(b/132600955): This modifies the root set. Should the consequent be persistent?
     rootSet.addConsequentRootSet(consequentRootSet, addNoShrinking);
+    if (mode.isInitialTreeShaking()) {
+      for (DexReference reference : consequentRootSet.noObfuscation) {
+        keepInfo.evaluateRule(reference, appView, Joiner::disallowMinification);
+      }
+    }
     enqueueRootItems(consequentRootSet.noShrinking);
     // Check for compatibility rules indicating that the holder must be implicitly kept.
     if (forceProguardCompatibility) {
       consequentRootSet.dependentKeepClassCompatRule.forEach(
           (precondition, compatRules) -> {
             assert precondition.isDexType();
-            DexClass preconditionHolder = appView.definitionFor(precondition.asDexType());
+            DexProgramClass preconditionHolder = getProgramClassOrNull(precondition.asDexType());
             compatEnqueueHolderIfDependentNonStaticMember(preconditionHolder, compatRules);
           });
     }
diff --git a/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java b/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
new file mode 100644
index 0000000..18ae09e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2020, 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;
+
+/** Globally controlled settings that affect the default values for kept items. */
+public interface GlobalKeepInfoConfiguration {
+
+  boolean isTreeShakingEnabled();
+
+  boolean isMinificationEnabled();
+
+  boolean isAccessModificationEnabled();
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
index 761547a..a1f8424 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
@@ -4,21 +4,23 @@
 package com.android.tools.r8.shaking;
 
 import com.android.tools.r8.shaking.KeepInfo.Builder;
-import java.util.function.Supplier;
 
 /** Keep information that can be associated with any item, i.e., class, method or field. */
 public abstract class KeepInfo<B extends Builder, K extends KeepInfo> {
 
   private final boolean pinned;
+  private final boolean allowMinification;
   private final boolean allowAccessModification;
 
-  private KeepInfo(boolean pinned, boolean allowAccessModification) {
+  private KeepInfo(boolean pinned, boolean allowMinification, boolean allowAccessModification) {
     this.pinned = pinned;
+    this.allowMinification = allowMinification;
     this.allowAccessModification = allowAccessModification;
   }
 
   KeepInfo(B builder) {
-    this(builder.isPinned(), builder.isAccessModificationAllowed());
+    this(
+        builder.isPinned(), builder.isMinificationAllowed(), builder.isAccessModificationAllowed());
   }
 
   /** True if an item must be present in the output. */
@@ -27,6 +29,20 @@
   }
 
   /**
+   * True if an item may have its name minified/changed.
+   *
+   * <p>This method requires knowledge of the global configuration as that can override the concrete
+   * value on a given item.
+   */
+  public boolean isMinificationAllowed(GlobalKeepInfoConfiguration configuration) {
+    return configuration.isMinificationEnabled() && internalIsMinificationAllowed();
+  }
+
+  boolean internalIsMinificationAllowed() {
+    return allowMinification;
+  }
+
+  /**
    * True if an item may have its access flags modified.
    *
    * <p>This method requires knowledge of the global access modification as that will override the
@@ -34,8 +50,8 @@
    *
    * @param configuration Global configuration object to determine access modification.
    */
-  public boolean isAccessModificationAllowed(ProguardConfiguration configuration) {
-    return configuration.isAccessModificationAllowed() && internalIsAccessModificationAllowed();
+  public boolean isAccessModificationAllowed(GlobalKeepInfoConfiguration configuration) {
+    return configuration.isAccessModificationEnabled() && internalIsAccessModificationAllowed();
   }
 
   // Internal accessor for the items access-modification bit.
@@ -69,6 +85,7 @@
 
     private K original;
     private boolean pinned;
+    private boolean allowMinification;
     private boolean allowAccessModification;
 
     Builder() {
@@ -78,17 +95,20 @@
     Builder(K original) {
       this.original = original;
       pinned = original.isPinned();
+      allowMinification = original.internalIsMinificationAllowed();
       allowAccessModification = original.internalIsAccessModificationAllowed();
     }
 
     B makeTop() {
       pin();
+      disallowMinification();
       disallowAccessModification();
       return self();
     }
 
     B makeBottom() {
       unpin();
+      allowMinification();
       allowAccessModification();
       return self();
     }
@@ -110,6 +130,7 @@
 
     private boolean internalIsEqualTo(K other) {
       return isPinned() == other.isPinned()
+          && isMinificationAllowed() == other.internalIsMinificationAllowed()
           && isAccessModificationAllowed() == other.internalIsAccessModificationAllowed()
           && isEqualTo(other);
     }
@@ -118,6 +139,10 @@
       return pinned;
     }
 
+    public boolean isMinificationAllowed() {
+      return allowMinification;
+    }
+
     public boolean isAccessModificationAllowed() {
       return allowAccessModification;
     }
@@ -135,6 +160,19 @@
       return setPinned(false);
     }
 
+    public B setAllowMinification(boolean allowMinification) {
+      this.allowMinification = allowMinification;
+      return self();
+    }
+
+    public B allowMinification() {
+      return setAllowMinification(true);
+    }
+
+    public B disallowMinification() {
+      return setAllowMinification(false);
+    }
+
     public B setAllowAccessModification(boolean allowAccessModification) {
       this.allowAccessModification = allowAccessModification;
       return self();
@@ -175,12 +213,13 @@
       return self();
     }
 
-    // Lazy modification of access modification.
-    // Only forced if access modification is still allowed.
-    public J lazyDisallowAccessModification(Supplier<Boolean> lazyShouldDisallow) {
-      if (builder.isAccessModificationAllowed() && lazyShouldDisallow.get()) {
-        builder.disallowAccessModification();
-      }
+    public J disallowMinification() {
+      builder.disallowMinification();
+      return self();
+    }
+
+    public J disallowAccessModification() {
+      builder.disallowAccessModification();
       return self();
     }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
index 37c8771..53af669 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
@@ -20,8 +20,10 @@
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.shaking.KeepFieldInfo.Joiner;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.IdentityHashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.function.Consumer;
 
@@ -43,6 +45,8 @@
     return KeepFieldInfo.bottom();
   }
 
+  abstract Map<DexReference, List<Consumer<KeepInfo.Joiner<?, ?, ?>>>> getRuleInstances();
+
   /**
    * Base accessor for keep info on a class.
    *
@@ -127,6 +131,14 @@
     return getFieldInfo(field, definitions).isPinned();
   }
 
+  public final boolean isMinificationAllowed(
+      DexReference reference,
+      DexDefinitionSupplier definitions,
+      GlobalKeepInfoConfiguration configuration) {
+    return configuration.isMinificationEnabled()
+        && getInfo(reference, definitions).isMinificationAllowed(configuration);
+  }
+
   public final boolean verifyNoneArePinned(Collection<DexType> types, AppInfo appInfo) {
     for (DexType type : types) {
       DexProgramClass clazz =
@@ -161,17 +173,26 @@
     private final Map<DexMethod, KeepMethodInfo> keepMethodInfo;
     private final Map<DexField, KeepFieldInfo> keepFieldInfo;
 
+    // Map of applied rules for which keys may need to be mutated.
+    private final Map<DexReference, List<Consumer<KeepInfo.Joiner<?, ?, ?>>>> ruleInstances;
+
     MutableKeepInfoCollection() {
-      this(new IdentityHashMap<>(), new IdentityHashMap<>(), new IdentityHashMap<>());
+      this(
+          new IdentityHashMap<>(),
+          new IdentityHashMap<>(),
+          new IdentityHashMap<>(),
+          new IdentityHashMap<>());
     }
 
     private MutableKeepInfoCollection(
         Map<DexType, KeepClassInfo> keepClassInfo,
         Map<DexMethod, KeepMethodInfo> keepMethodInfo,
-        Map<DexField, KeepFieldInfo> keepFieldInfo) {
+        Map<DexField, KeepFieldInfo> keepFieldInfo,
+        Map<DexReference, List<Consumer<KeepInfo.Joiner<?, ?, ?>>>> ruleInstances) {
       this.keepClassInfo = keepClassInfo;
       this.keepMethodInfo = keepMethodInfo;
       this.keepFieldInfo = keepFieldInfo;
+      this.ruleInstances = ruleInstances;
     }
 
     @Override
@@ -197,7 +218,43 @@
             assert !info.isPinned() || field == newField;
             newFieldInfo.put(newField, info);
           });
-      return new MutableKeepInfoCollection(newClassInfo, newMethodInfo, newFieldInfo);
+      Map<DexReference, List<Consumer<KeepInfo.Joiner<?, ?, ?>>>> newRuleInstances =
+          new IdentityHashMap<>(ruleInstances.size());
+      ruleInstances.forEach(
+          (reference, consumers) -> {
+            DexReference newReference;
+            if (reference.isDexType()) {
+              DexType newType = lens.lookupType(reference.asDexType());
+              if (!newType.isClassType()) {
+                assert newType.isIntType() : "Expected only enum unboxing type changes.";
+                return;
+              }
+              newReference = newType;
+            } else if (reference.isDexMethod()) {
+              newReference = lens.getRenamedMethodSignature(reference.asDexMethod());
+            } else {
+              assert reference.isDexField();
+              newReference = lens.getRenamedFieldSignature(reference.asDexField());
+            }
+            newRuleInstances.put(newReference, consumers);
+          });
+      return new MutableKeepInfoCollection(
+          newClassInfo, newMethodInfo, newFieldInfo, newRuleInstances);
+    }
+
+    @Override
+    Map<DexReference, List<Consumer<KeepInfo.Joiner<?, ?, ?>>>> getRuleInstances() {
+      return ruleInstances;
+    }
+
+    void evaluateRule(
+        DexReference reference,
+        DexDefinitionSupplier definitions,
+        Consumer<KeepInfo.Joiner<?, ?, ?>> fn) {
+      joinInfo(reference, definitions, fn);
+      if (!getInfo(reference, definitions).isBottom()) {
+        ruleInstances.computeIfAbsent(reference, k -> new ArrayList<>()).add(fn);
+      }
     }
 
     @Override
@@ -230,6 +287,34 @@
       }
     }
 
+    public void joinInfo(
+        DexReference reference,
+        DexDefinitionSupplier definitions,
+        Consumer<KeepInfo.Joiner<?, ?, ?>> fn) {
+      if (reference.isDexType()) {
+        DexType type = reference.asDexType();
+        DexProgramClass clazz = asProgramClassOrNull(definitions.definitionFor(type));
+        if (clazz != null) {
+          joinClass(clazz, fn::accept);
+        }
+      } else if (reference.isDexMethod()) {
+        DexMethod method = reference.asDexMethod();
+        DexProgramClass clazz = asProgramClassOrNull(definitions.definitionFor(method.holder));
+        DexEncodedMethod definition = method.lookupOnClass(clazz);
+        if (definition != null) {
+          joinMethod(clazz, definition, fn::accept);
+        }
+      } else {
+        assert reference.isDexField();
+        DexField field = reference.asDexField();
+        DexProgramClass clazz = asProgramClassOrNull(definitions.definitionFor(field.holder));
+        DexEncodedField definition = field.lookupOnClass(clazz);
+        if (definition != null) {
+          joinField(clazz, definition, fn::accept);
+        }
+      }
+    }
+
     public void keepClass(DexProgramClass clazz) {
       joinClass(clazz, KeepInfo.Joiner::top);
     }
@@ -303,6 +388,10 @@
       }
     }
 
+    public void keepField(ProgramField programField) {
+      keepField(programField.getHolder(), programField.getDefinition());
+    }
+
     public void keepField(DexProgramClass holder, DexEncodedField field) {
       joinField(holder, field, KeepInfo.Joiner::top);
     }
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index b38d077..ec9f101 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -1368,7 +1368,7 @@
     public void forEachDependentMember(
         DexDefinition item,
         AppView<?> appView,
-        Consumer3<DexDefinition, DexDefinition, Set<ProguardKeepRuleBase>> fn) {
+        Consumer3<DexDefinition, DexEncodedMember<?, ?>, Set<ProguardKeepRuleBase>> fn) {
       getDependentItems(item)
           .forEachMember(
               (reference, reasons) -> {
@@ -1386,7 +1386,7 @@
     public void forEachDependentNonStaticMember(
         DexDefinition item,
         AppView<?> appView,
-        Consumer3<DexDefinition, DexDefinition, Set<ProguardKeepRuleBase>> fn) {
+        Consumer3<DexDefinition, DexEncodedMember<?, ?>, Set<ProguardKeepRuleBase>> fn) {
       forEachDependentMember(
           item,
           appView,
@@ -1400,7 +1400,7 @@
     public void forEachDependentStaticMember(
         DexDefinition item,
         AppView<?> appView,
-        Consumer3<DexDefinition, DexDefinition, Set<ProguardKeepRuleBase>> fn) {
+        Consumer3<DexDefinition, DexEncodedMember<?, ?>, Set<ProguardKeepRuleBase>> fn) {
       forEachDependentMember(
           item,
           appView,
@@ -1903,24 +1903,6 @@
       noObfuscation.add(reference);
     }
 
-    public boolean mayBeMinified(DexReference reference, AppView<?> appView) {
-      return !mayNotBeMinified(reference, appView);
-    }
-
-    public boolean mayNotBeMinified(DexReference reference, AppView<?> appView) {
-      if (reference.isDexType()) {
-        return noObfuscation.contains(
-            appView.graphLense().getOriginalType(reference.asDexType()));
-      } else if (reference.isDexMethod()) {
-        return noObfuscation.contains(
-            appView.graphLense().getOriginalMethodSignature(reference.asDexMethod()));
-      } else {
-        assert reference.isDexField();
-        return noObfuscation.contains(
-            appView.graphLense().getOriginalFieldSignature(reference.asDexField()));
-      }
-    }
-
     public boolean verifyKeptFieldsAreAccessedAndLive(AppInfoWithLiveness appInfo) {
       noShrinking.forEachField(
           reference -> {
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 35e4614..f7353d9 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -45,6 +45,7 @@
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.Enqueuer;
+import com.android.tools.r8.shaking.GlobalKeepInfoConfiguration;
 import com.android.tools.r8.shaking.ProguardConfiguration;
 import com.android.tools.r8.shaking.ProguardConfigurationRule;
 import com.android.tools.r8.utils.IROrdering.IdentityIROrdering;
@@ -77,7 +78,7 @@
 import java.util.function.Predicate;
 import org.objectweb.asm.Opcodes;
 
-public class InternalOptions {
+public class InternalOptions implements GlobalKeepInfoConfiguration {
 
   // Set to true to run compilation in a single thread and without randomly shuffling the input.
   // This makes life easier when running R8 in a debugger.
@@ -481,13 +482,33 @@
   private final boolean enableMinification;
 
   public boolean isShrinking() {
+    assert proguardConfiguration == null
+        || enableTreeShaking == proguardConfiguration.isShrinking();
     return enableTreeShaking;
   }
 
   public boolean isMinifying() {
+    assert proguardConfiguration == null
+        || enableMinification == proguardConfiguration.isObfuscating();
     return enableMinification;
   }
 
+  @Override
+  public boolean isTreeShakingEnabled() {
+    return isShrinking();
+  }
+
+  @Override
+  public boolean isMinificationEnabled() {
+    return isMinifying();
+  }
+
+  @Override
+  public boolean isAccessModificationEnabled() {
+    return getProguardConfiguration() != null
+        && getProguardConfiguration().isAccessModificationAllowed();
+  }
+
   public boolean keepInnerClassStructure() {
     return getProguardConfiguration().getKeepAttributes().signature
         || getProguardConfiguration().getKeepAttributes().innerClasses;
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index 367092b..83f8ec0 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -47,7 +47,7 @@
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.naming.Range;
-import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
+import com.android.tools.r8.shaking.KeepInfoCollection;
 import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
 import com.google.common.base.Suppliers;
 import java.util.ArrayList;
@@ -448,7 +448,7 @@
     if (appView.options().isGeneratingClassFiles()) {
       return true;
     }
-    RootSet rootSet = appView.rootSet();
+    KeepInfoCollection keepInfo = appView.getKeepInfo();
     boolean allSeenAreInstanceInitializers = true;
     DexString originalName = null;
     for (DexEncodedMethod method : methods) {
@@ -459,7 +459,7 @@
       }
       allSeenAreInstanceInitializers = false;
       // If the method is pinned, we cannot minify it.
-      if (rootSet.mayNotBeMinified(method.method, appView)) {
+      if (!keepInfo.isMinificationAllowed(method.method, appView, appView.options())) {
         continue;
       }
       // With desugared library, call-backs names are reserved here.
diff --git a/src/main/java/com/android/tools/r8/utils/SemanticVersion.java b/src/main/java/com/android/tools/r8/utils/SemanticVersion.java
new file mode 100644
index 0000000..977073d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/SemanticVersion.java
@@ -0,0 +1,84 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import java.util.Objects;
+
+public class SemanticVersion {
+
+  public static SemanticVersion parse(String version) {
+    int majorEnd = version.indexOf('.');
+    if (majorEnd <= 0) {
+      throw new IllegalArgumentException("Invalid semantic version: " + version);
+    }
+    int minorEnd = version.indexOf('.', majorEnd + 1);
+    if (minorEnd <= majorEnd) {
+      throw new IllegalArgumentException("Invalid semantic version: " + version);
+    }
+    // No current support for extensions.
+    int patchEnd = version.length();
+    int major;
+    int minor;
+    int patch;
+    try {
+      major = Integer.parseInt(version.substring(0, majorEnd));
+      minor = Integer.parseInt(version.substring(majorEnd + 1, minorEnd));
+      patch = Integer.parseInt(version.substring(minorEnd + 1, patchEnd));
+    } catch (NumberFormatException e) {
+      throw new IllegalArgumentException("Invalid semantic version: " + version, e);
+    }
+    return new SemanticVersion(major, minor, patch);
+  }
+
+  private final int major;
+  private final int minor;
+  private final int patch;
+
+  public SemanticVersion(int major, int minor, int patch) {
+    this.major = major;
+    this.minor = minor;
+    this.patch = patch;
+  }
+
+  public int getMajor() {
+    return major;
+  }
+
+  public int getMinor() {
+    return minor;
+  }
+
+  public int getPatch() {
+    return patch;
+  }
+
+  public boolean isNewerOrEqual(SemanticVersion other) {
+    if (major != other.major) {
+      return major > other.major;
+    }
+    if (minor != other.minor) {
+      return minor > other.minor;
+    }
+    return patch >= other.patch;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (!(obj instanceof SemanticVersion)) {
+      return false;
+    }
+    SemanticVersion other = (SemanticVersion) obj;
+    return major == other.major && minor == other.minor && patch == other.patch;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(major, minor, patch);
+  }
+
+  @Override
+  public String toString() {
+    return "" + major + "." + minor + "." + patch;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/D8ApiBinaryCompatibilityTests.java b/src/test/java/com/android/tools/r8/D8ApiBinaryCompatibilityTests.java
index 846bc6b..2c53667 100644
--- a/src/test/java/com/android/tools/r8/D8ApiBinaryCompatibilityTests.java
+++ b/src/test/java/com/android/tools/r8/D8ApiBinaryCompatibilityTests.java
@@ -6,7 +6,6 @@
 import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
 import static org.junit.Assert.assertEquals;
 
-import com.android.tools.r8.TestRuntime.NoneRuntime;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.FileUtils;
@@ -32,7 +31,7 @@
   }
 
   public D8ApiBinaryCompatibilityTests(TestParameters parameters) {
-    assertEquals(NoneRuntime.getInstance(), parameters.getRuntime());
+    parameters.assertNoneRuntime();
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/MarkerMatcher.java b/src/test/java/com/android/tools/r8/MarkerMatcher.java
new file mode 100644
index 0000000..90aec54
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/MarkerMatcher.java
@@ -0,0 +1,181 @@
+// Copyright (c) 2020, 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.dex.Marker;
+import com.android.tools.r8.dex.Marker.Tool;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+
+public abstract class MarkerMatcher extends TypeSafeMatcher<Marker> {
+
+  public static void assertMarkersMatch(
+      Iterable<Marker> markers, Collection<Matcher<Marker>> matchers) {
+    // Match is unordered, but we make no attempts to find the maximum match.
+    int markerCount = 0;
+    Set<Marker> matchedMarkers = new HashSet<>();
+    Set<Matcher<Marker>> matchedMatchers = new HashSet<>();
+    for (Marker marker : markers) {
+      markerCount++;
+      for (Matcher<Marker> matcher : matchers) {
+        if (matchedMatchers.contains(matcher)) {
+          continue;
+        }
+        if (matcher.matches(marker)) {
+          matchedMarkers.add(marker);
+          matchedMatchers.add(matcher);
+          break;
+        }
+      }
+    }
+    StringBuilder builder = new StringBuilder();
+    boolean failedMatching = false;
+    if (matchedMarkers.size() < markerCount) {
+      failedMatching = true;
+      builder.append("\nUnmatched markers:");
+      for (Marker marker : markers) {
+        if (!matchedMarkers.contains(marker)) {
+          builder.append("\n  - ").append(marker);
+        }
+      }
+    }
+    if (matchedMatchers.size() < matchers.size()) {
+      failedMatching = true;
+      builder.append("\nUnmatched matchers:");
+      for (Matcher<Marker> matcher : matchers) {
+        if (!matchedMatchers.contains(matcher)) {
+          builder.append("\n  - ").append(matcher);
+        }
+      }
+    }
+    if (failedMatching) {
+      builder.append("\nAll markers:");
+      for (Marker marker : markers) {
+        builder.append("\n  - ").append(marker);
+      }
+      builder.append("\nAll matchers:");
+      for (Matcher<Marker> matcher : matchers) {
+        builder.append("\n  - ").append(matcher);
+      }
+      fail(builder.toString());
+    }
+    // Double check consistency.
+    assertEquals(matchers.size(), markerCount);
+    assertEquals(markerCount, matchedMarkers.size());
+    assertEquals(markerCount, matchedMatchers.size());
+  }
+
+  public static Matcher<Marker> markerTool(Tool tool) {
+    return new MarkerMatcher() {
+      @Override
+      protected boolean eval(Marker marker) {
+        return marker.getTool() == tool;
+      }
+
+      @Override
+      protected void explain(Description description) {
+        description.appendText("tool ").appendText(tool.name());
+      }
+    };
+  }
+
+  public static Matcher<Marker> markerCompilationMode(CompilationMode compilationMode) {
+    return new MarkerMatcher() {
+      @Override
+      protected boolean eval(Marker marker) {
+        return marker.getCompilationMode().equals(compilationMode.name().toLowerCase());
+      }
+
+      @Override
+      protected void explain(Description description) {
+        description.appendText(Marker.COMPILATION_MODE + " ").appendText(compilationMode.name());
+      }
+    };
+  }
+
+  public static Matcher<Marker> markerMinApi(AndroidApiLevel level) {
+    return new MarkerMatcher() {
+      @Override
+      protected boolean eval(Marker marker) {
+        return marker.getMinApi() == level.getLevel();
+      }
+
+      @Override
+      protected void explain(Description description) {
+        description.appendText(Marker.MIN_API + " ").appendText(level.toString());
+      }
+    };
+  }
+
+  public static Matcher<Marker> markerHasChecksums(boolean value) {
+    return new MarkerMatcher() {
+      @Override
+      protected boolean eval(Marker marker) {
+        return marker.getHasChecksums() == value;
+      }
+
+      @Override
+      protected void explain(Description description) {
+        description.appendText(Marker.HAS_CHECKSUMS + " ").appendText(Boolean.toString(value));
+      }
+    };
+  }
+
+  public static Matcher<Marker> markerR8Mode(String r8Mode) {
+    return new MarkerMatcher() {
+      @Override
+      protected boolean eval(Marker marker) {
+        return marker.getR8Mode().equals(r8Mode);
+      }
+
+      @Override
+      protected void explain(Description description) {
+        description.appendText(Marker.R8_MODE + " ").appendText(r8Mode);
+      }
+    };
+  }
+
+  public static Matcher<Marker> markerDesugaredLibraryIdentifier(
+      String desugaredLibraryIdentifier) {
+    return new MarkerMatcher() {
+      @Override
+      protected boolean eval(Marker marker) {
+        if (marker.getDesugaredLibraryIdentifiers().length != 1) {
+          return false;
+        }
+        return marker.getDesugaredLibraryIdentifiers()[0].equals(desugaredLibraryIdentifier);
+      }
+
+      @Override
+      protected void explain(Description description) {
+        description
+            .appendText(Marker.DESUGARED_LIBRARY_IDENTIFIERS + " ")
+            .appendText(desugaredLibraryIdentifier);
+      }
+    };
+  }
+
+  @Override
+  protected boolean matchesSafely(Marker marker) {
+    return eval(marker);
+  }
+
+  @Override
+  public void describeTo(Description description) {
+    explain(description.appendText("a marker "));
+  }
+
+  protected abstract boolean eval(Marker diagnostic);
+
+  protected abstract void explain(Description description);
+}
diff --git a/src/test/java/com/android/tools/r8/MarkersTest.java b/src/test/java/com/android/tools/r8/MarkersTest.java
new file mode 100644
index 0000000..cc7b81e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/MarkersTest.java
@@ -0,0 +1,88 @@
+// Copyright (c) 2020, 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 static com.android.tools.r8.MarkerMatcher.assertMarkersMatch;
+import static com.android.tools.r8.MarkerMatcher.markerCompilationMode;
+import static com.android.tools.r8.MarkerMatcher.markerDesugaredLibraryIdentifier;
+import static com.android.tools.r8.MarkerMatcher.markerHasChecksums;
+import static com.android.tools.r8.MarkerMatcher.markerMinApi;
+import static com.android.tools.r8.MarkerMatcher.markerR8Mode;
+import static com.android.tools.r8.MarkerMatcher.markerTool;
+import static org.hamcrest.CoreMatchers.allOf;
+
+import com.android.tools.r8.dex.Marker;
+import com.android.tools.r8.dex.Marker.Tool;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.util.Collection;
+import org.hamcrest.Matcher;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MarkersTest extends TestBase {
+
+  @Parameterized.Parameters(name = "{0}, compilationMode {1}, shrinkDesugaredLibrary {2}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withNoneRuntime().build(),
+        CompilationMode.values(),
+        BooleanUtils.values());
+  }
+
+  private final TestParameters parameters;
+  private final CompilationMode compilationMode;
+  private final boolean shrinkDesugaredLibrary;
+
+  public MarkersTest(
+      TestParameters parameters, CompilationMode compilationMode, boolean shrinkDesugaredLibrary) {
+    this.parameters = parameters;
+    this.compilationMode = compilationMode;
+    this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+  }
+
+  @Test
+  public void testL8Marker() throws Throwable {
+    AndroidApiLevel apiLevel = AndroidApiLevel.L;
+    Path output = temp.newFolder().toPath().resolve("desugar_jdk_libs.zip");
+    L8Command.Builder builder =
+        L8Command.builder()
+            .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+            .addProgramFiles(ToolHelper.getDesugarJDKLibs())
+            .setMinApiLevel(apiLevel.getLevel())
+            .setMode(compilationMode)
+            .addDesugaredLibraryConfiguration(
+                StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
+            .setOutput(output, OutputMode.DexIndexed);
+    if (shrinkDesugaredLibrary) {
+      builder.addProguardConfiguration(ImmutableList.of("-keep class * { *; }"), Origin.unknown());
+    }
+    L8.run(builder.build());
+    Collection<Marker> markers = ExtractMarker.extractMarkerFromDexFile(output);
+    Matcher<Marker> l8Matcher =
+        allOf(
+            markerTool(Tool.L8),
+            markerCompilationMode(compilationMode),
+            markerDesugaredLibraryIdentifier("com.tools.android:desugar_jdk_libs:1.0.10"),
+            markerHasChecksums(false));
+    Matcher<Marker> d8Matcher =
+        allOf(
+            markerTool(Tool.R8),
+            markerCompilationMode(compilationMode),
+            markerMinApi(apiLevel),
+            markerR8Mode("compatibility"));
+    Matcher<Marker> r8Matcher =
+        allOf(markerTool(Tool.D8), markerCompilationMode(compilationMode), markerMinApi(apiLevel));
+    if (shrinkDesugaredLibrary) {
+      assertMarkersMatch(markers, ImmutableList.of(l8Matcher, d8Matcher));
+    } else {
+      assertMarkersMatch(markers, ImmutableList.of(l8Matcher, r8Matcher));
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/R8ApiBinaryCompatibilityTests.java b/src/test/java/com/android/tools/r8/R8ApiBinaryCompatibilityTests.java
index 954d773..ac3d89f 100644
--- a/src/test/java/com/android/tools/r8/R8ApiBinaryCompatibilityTests.java
+++ b/src/test/java/com/android/tools/r8/R8ApiBinaryCompatibilityTests.java
@@ -6,7 +6,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.TestRuntime.NoneRuntime;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.FileUtils;
@@ -35,7 +34,7 @@
   }
 
   public R8ApiBinaryCompatibilityTests(TestParameters parameters) {
-    assertEquals(NoneRuntime.getInstance(), parameters.getRuntime());
+    parameters.assertNoneRuntime();
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java b/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java
index 2daa0a9..e30b788 100644
--- a/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java
+++ b/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java
@@ -44,8 +44,7 @@
     DirectMappedDexApplication application =
         new ApplicationReader(input, options, timing).read(executorService).toDirect();
     IRConverter converter =
-        new IRConverter(
-            AppView.createForR8(new AppInfoWithClassHierarchy(application), options), null);
+        new IRConverter(AppView.createForR8(new AppInfoWithClassHierarchy(application)), null);
     converter.optimize();
     DexProgramClass clazz = application.classes().iterator().next();
     assertEquals(4, clazz.getMethodCollection().numberOfDirectMethods());
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index aeaf585..78eaabf 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -47,6 +47,7 @@
 import com.android.tools.r8.shaking.EnqueuerFactory;
 import com.android.tools.r8.shaking.ProguardClassFilter;
 import com.android.tools.r8.shaking.ProguardClassNameList;
+import com.android.tools.r8.shaking.ProguardConfiguration;
 import com.android.tools.r8.shaking.ProguardConfigurationRule;
 import com.android.tools.r8.shaking.ProguardKeepRule;
 import com.android.tools.r8.shaking.ProguardKeepRule.Builder;
@@ -66,6 +67,7 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.PreloadedClassFileProvider;
+import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.TestDescriptionWatcher;
 import com.android.tools.r8.utils.Timing;
@@ -574,35 +576,40 @@
     return newJar;
   }
 
-  protected static AppInfo computeAppInfo(AndroidApp application) {
-    try {
-      DexApplication dexApplication =
-          new ApplicationReader(application, new InternalOptions(), Timing.empty()).read();
-      return new AppInfo(dexApplication);
-    } catch (IOException e) {
-      throw new RuntimeException(e);
-    }
+  private static DexApplication readApplicationForDexOutput(AndroidApp app, InternalOptions options)
+      throws Exception {
+    assert options.programConsumer == null;
+    options.programConsumer = DexIndexedConsumer.emptyConsumer();
+    return new ApplicationReader(app, options, Timing.empty()).read();
   }
 
-  protected static AppInfoWithClassHierarchy computeAppInfoWithClassHierarchy(
-      AndroidApp application) {
-    try {
-      DexApplication dexApplication =
-          new ApplicationReader(application, new InternalOptions(), Timing.empty()).read();
-      return new AppInfoWithClassHierarchy(dexApplication);
-    } catch (IOException e) {
-      throw new RuntimeException(e);
-    }
+  protected static AppView<AppInfo> computeAppView(AndroidApp app) throws Exception {
+    AppInfo appInfo = new AppInfo(readApplicationForDexOutput(app, new InternalOptions()));
+    return AppView.createForD8(appInfo);
+  }
+
+  protected static AppInfoWithClassHierarchy computeAppInfoWithClassHierarchy(AndroidApp app)
+      throws Exception {
+    return new AppInfoWithClassHierarchy(readApplicationForDexOutput(app, new InternalOptions()));
   }
 
   protected static AppView<AppInfoWithClassHierarchy> computeAppViewWithSubtyping(AndroidApp app)
       throws Exception {
-    Timing timing = Timing.empty();
-    InternalOptions options = new InternalOptions();
-    DirectMappedDexApplication application =
-        new ApplicationReader(app, options, timing).read().toDirect();
+    return computeAppViewWithSubtyping(
+        app,
+        factory ->
+            buildConfigForRules(
+                factory,
+                Collections.singletonList(ProguardKeepRule.defaultKeepAllRule(unused -> {}))));
+  }
+
+  private static AppView<AppInfoWithClassHierarchy> computeAppViewWithSubtyping(
+      AndroidApp app, Function<DexItemFactory, ProguardConfiguration> keepConfig) throws Exception {
+    DexItemFactory dexItemFactory = new DexItemFactory();
+    InternalOptions options = new InternalOptions(keepConfig.apply(dexItemFactory), new Reporter());
+    DexApplication dexApplication = readApplicationForDexOutput(app, options);
     AppView<AppInfoWithClassHierarchy> appView =
-        AppView.createForR8(new AppInfoWithClassHierarchy(application), options);
+        AppView.createForR8(new AppInfoWithClassHierarchy(dexApplication.toDirect()));
     appView.setAppServices(AppServices.builder(appView).build());
     return appView;
   }
@@ -610,31 +617,32 @@
   protected static AppView<AppInfoWithLiveness> computeAppViewWithLiveness(AndroidApp app)
       throws Exception {
     return computeAppViewWithLiveness(
-        app, factory -> ImmutableList.of(ProguardKeepRule.defaultKeepAllRule(unused -> {})));
+        app,
+        factory ->
+            buildConfigForRules(
+                factory, ImmutableList.of(ProguardKeepRule.defaultKeepAllRule(unused -> {}))));
   }
 
   protected static AppView<AppInfoWithLiveness> computeAppViewWithLiveness(
       AndroidApp app, Class<?> mainClass) throws Exception {
     return computeAppViewWithLiveness(
-        app, factory -> buildKeepRuleForClassAndMethods(mainClass, factory));
+        app,
+        factory ->
+            buildConfigForRules(factory, buildKeepRuleForClassAndMethods(mainClass, factory)));
   }
 
   protected static AppView<AppInfoWithLiveness> computeAppViewWithLiveness(
-      AndroidApp app,
-      Function<DexItemFactory, Collection<ProguardConfigurationRule>>
-          proguardConfigurationRulesGenerator)
-      throws Exception {
-    AppView<AppInfoWithClassHierarchy> appView = computeAppViewWithSubtyping(app);
+      AndroidApp app, Function<DexItemFactory, ProguardConfiguration> keepConfig) throws Exception {
+    AppView<AppInfoWithClassHierarchy> appView = computeAppViewWithSubtyping(app, keepConfig);
     // Run the tree shaker to compute an instance of AppInfoWithLiveness.
     ExecutorService executor = Executors.newSingleThreadExecutor();
     DirectMappedDexApplication application = appView.appInfo().app().asDirect();
     SubtypingInfo subtypingInfo = new SubtypingInfo(application.allClasses(), application);
     RootSet rootSet =
         new RootSetBuilder(
-                appView,
-                subtypingInfo,
-                proguardConfigurationRulesGenerator.apply(appView.appInfo().dexItemFactory()))
+                appView, subtypingInfo, application.options.getProguardConfiguration().getRules())
             .run(executor);
+    appView.setRootSet(rootSet);
     AppInfoWithLiveness appInfoWithLiveness =
         EnqueuerFactory.createForInitialTreeShaking(appView, subtypingInfo)
             .traceApplication(rootSet, ProguardClassFilter.empty(), executor, application.timing);
@@ -714,6 +722,18 @@
     return Collections.singletonList(keepRuleBuilder.build());
   }
 
+  protected static ProguardConfiguration buildConfigForRules(
+      DexItemFactory factory, Collection<ProguardConfigurationRule> rules) {
+    return buildConfigForRules(factory, new Reporter(), rules);
+  }
+
+  protected static ProguardConfiguration buildConfigForRules(
+      DexItemFactory factory, Reporter reporter, Collection<ProguardConfigurationRule> rules) {
+    ProguardConfiguration.Builder builder = ProguardConfiguration.builder(factory, reporter);
+    rules.forEach(builder::addRule);
+    return builder.build();
+  }
+
   /** Returns a list containing all the data resources in the given app. */
   public static List<DataEntryResource> getDataResources(AndroidApp app) throws ResourceException {
     List<DataEntryResource> dataResources = new ArrayList<>();
@@ -1500,4 +1520,28 @@
   public static AndroidApiLevel apiLevelWithInvokeCustomSupport() {
     return AndroidApiLevel.O;
   }
+
+  public Path compileToZip(
+      TestParameters parameters, Collection<Class<?>> classPath, Class<?>... compilationUnit)
+      throws Exception {
+    return compileToZip(parameters, classPath, Arrays.asList(compilationUnit));
+  }
+
+  public Path compileToZip(
+      TestParameters parameters,
+      Collection<Class<?>> classpath,
+      Collection<Class<?>> compilationUnit)
+      throws Exception {
+    if (parameters.isCfRuntime()) {
+      Path out = temp.newFolder().toPath().resolve("out.jar");
+      writeClassesToJar(out, compilationUnit);
+      return out;
+    }
+    return testForD8()
+        .setMinApi(parameters.getApiLevel())
+        .addProgramClasses(compilationUnit)
+        .addClasspathClasses(classpath)
+        .compile()
+        .writeToZip();
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
index ed0e8f1..72a22e6 100644
--- a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
+++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
@@ -34,6 +34,18 @@
 
   TestDiagnosticMessages assertErrorsCount(int count);
 
+  default TestDiagnosticMessages assertNoInfos() {
+    return assertInfosCount(0);
+  }
+
+  default TestDiagnosticMessages assertNoWarnings() {
+    return assertWarningsCount(0);
+  }
+
+  default TestDiagnosticMessages assertNoErrors() {
+    return assertErrorsCount(0);
+  }
+
   // Match exact.
 
   default TestDiagnosticMessages assertDiagnosticsMatch(Matcher<Diagnostic> matcher) {
diff --git a/src/test/java/com/android/tools/r8/TestParameters.java b/src/test/java/com/android/tools/r8/TestParameters.java
index d886efb..75b7336 100644
--- a/src/test/java/com/android/tools/r8/TestParameters.java
+++ b/src/test/java/com/android/tools/r8/TestParameters.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
+import static org.junit.Assert.assertEquals;
+
 import com.android.tools.r8.TestBase.Backend;
 import com.android.tools.r8.TestRuntime.NoneRuntime;
 import com.android.tools.r8.utils.AndroidApiLevel;
@@ -70,4 +72,8 @@
     }
     return runtime.toString();
   }
+
+  public void assertNoneRuntime() {
+    assertEquals(NoneRuntime.getInstance(), runtime);
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/bisect/BisectTest.java b/src/test/java/com/android/tools/r8/bisect/BisectTest.java
index 28ecca3..bd1d3c4 100644
--- a/src/test/java/com/android/tools/r8/bisect/BisectTest.java
+++ b/src/test/java/com/android/tools/r8/bisect/BisectTest.java
@@ -37,10 +37,8 @@
     return TestParametersBuilder.builder().withNoneRuntime().build();
   }
 
-  private final TestParameters parameters;
-
   public BisectTest(TestParameters parameters) {
-    this.parameters = parameters;
+    parameters.assertNoneRuntime();
   }
 
   private final String[] CLASSES = {"A", "B", "C", "D", "E", "F", "G", "H"};
diff --git a/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java b/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java
index b0c7ecf..df38cb2 100644
--- a/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java
+++ b/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java
@@ -3,25 +3,27 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.checkdiscarded;
 
-import static org.hamcrest.MatcherAssert.assertThat;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static org.hamcrest.CoreMatchers.allOf;
 import static org.hamcrest.core.StringContains.containsString;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 
 import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.Diagnostic;
 import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestDiagnosticMessages;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.NoneRuntime;
 import com.android.tools.r8.checkdiscarded.testclasses.Main;
 import com.android.tools.r8.checkdiscarded.testclasses.UnusedClass;
 import com.android.tools.r8.checkdiscarded.testclasses.UsedClass;
 import com.android.tools.r8.checkdiscarded.testclasses.WillBeGone;
 import com.android.tools.r8.checkdiscarded.testclasses.WillStay;
+import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableList;
 import java.util.List;
 import java.util.function.Consumer;
 import org.junit.Test;
@@ -32,19 +34,19 @@
 @RunWith(Parameterized.class)
 public class CheckDiscardedTest extends TestBase {
 
-  @Parameters(name = "{0}")
-  public static TestParametersCollection data() {
-    return getTestParameters().withNoneRuntime().build();
+  @Parameters(name = "{0}, minify:{1}")
+  public static List<Object[]> data() {
+    return buildParameters(getTestParameters().withNoneRuntime().build(), BooleanUtils.values());
   }
 
-  private final TestParameters parameters;
+  public final boolean minify;
 
-  public CheckDiscardedTest(TestParameters parameters) {
-    this.parameters = parameters;
+  public CheckDiscardedTest(TestParameters parameters, boolean minify) {
+    assertEquals(NoneRuntime.getInstance(), parameters.getRuntime());
+    this.minify = minify;
   }
 
   private void compile(
-      boolean obfuscate,
       Class annotation,
       boolean checkMembers,
       Consumer<TestDiagnosticMessages> onCompilationFailure) {
@@ -56,7 +58,7 @@
               .addProgramClasses(UnusedClass.class, UsedClass.class, Main.class)
               .addKeepMainRule(Main.class)
               .addKeepRules(checkDiscardRule(checkMembers, annotation))
-              .minification(obfuscate)
+              .minification(minify)
               .addOptionsModification(this::noInlining)
               .compile();
       assertNull(onCompilationFailure);
@@ -80,45 +82,44 @@
 
   @Test
   public void classesAreGone() {
-    compile(false, WillBeGone.class, false, null);
-    compile(true, WillBeGone.class, false, null);
+    compile(WillBeGone.class, false, null);
   }
 
   @Test
   public void classesAreNotGone() {
     Consumer<TestDiagnosticMessages> check =
-        diagnostics -> {
-          List<Diagnostic> infos = diagnostics.getInfos();
-          assertEquals(2, infos.size());
-          String messageUsedClass = infos.get(1).getDiagnosticMessage();
-          assertThat(messageUsedClass, containsString("UsedClass was not discarded"));
-          assertThat(messageUsedClass, containsString("is instantiated in"));
-          String messageMain = infos.get(0).getDiagnosticMessage();
-          assertThat(messageMain, containsString("Main was not discarded"));
-          assertThat(messageMain, containsString("is referenced in keep rule"));
-        };
-    compile(false, WillStay.class, false, check);
-    compile(true, WillStay.class, false, check);
+        diagnostics ->
+            diagnostics
+                .assertNoWarnings()
+                .assertErrorsMatch(diagnosticMessage(containsString("Discard checks failed")))
+                .assertInfosMatch(
+                    ImmutableList.of(
+                        allOf(
+                            diagnosticMessage(containsString("UsedClass was not discarded")),
+                            diagnosticMessage(containsString("is instantiated in"))),
+                        allOf(
+                            diagnosticMessage(containsString("Main was not discarded")),
+                            diagnosticMessage(containsString("is referenced in keep rule")))));
+    compile(WillStay.class, false, check);
   }
 
   @Test
   public void membersAreGone() {
-    compile(false, WillBeGone.class, true, null);
-    compile(true, WillBeGone.class, true, null);
+    compile(WillBeGone.class, true, null);
   }
 
   @Test
   public void membersAreNotGone() {
     Consumer<TestDiagnosticMessages> check =
-        diagnostics -> {
-          List<Diagnostic> infos = diagnostics.getInfos();
-          assertEquals(1, infos.size());
-          String message = infos.get(0).getDiagnosticMessage();
-          assertThat(message, containsString("was not discarded"));
-          assertThat(message, containsString("is invoked from"));
-        };
-    compile(false, WillStay.class, true, check);
-    compile(true, WillStay.class, true, check);
+        diagnostics ->
+            diagnostics
+                .assertNoWarnings()
+                .assertErrorsMatch(diagnosticMessage(containsString("Discard checks failed")))
+                .assertInfosMatch(
+                    allOf(
+                        diagnosticMessage(containsString("was not discarded")),
+                        diagnosticMessage(containsString("is invoked from"))));
+    compile(WillStay.class, true, check);
   }
 
 }
diff --git a/src/test/java/com/android/tools/r8/classlookup/ClasspathClassExtendsProgramClassTest.java b/src/test/java/com/android/tools/r8/classlookup/ClasspathClassExtendsProgramClassTest.java
index b409e5a..f9d6e4d 100644
--- a/src/test/java/com/android/tools/r8/classlookup/ClasspathClassExtendsProgramClassTest.java
+++ b/src/test/java/com/android/tools/r8/classlookup/ClasspathClassExtendsProgramClassTest.java
@@ -4,14 +4,11 @@
 
 package com.android.tools.r8.classlookup;
 
-import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.references.Reference;
 import com.google.common.collect.ImmutableList;
-import java.nio.file.Path;
-import java.util.Collection;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -63,7 +60,12 @@
   public void testReference() throws Exception {
     testForRuntime(parameters)
         .addProgramClasses(Main.class, ProgramClass.class)
-        .addRunClasspathFiles(compileClasspath())
+        .addRunClasspathFiles(
+            compileToZip(
+                parameters,
+                ImmutableList.of(ProgramClass.class),
+                ClasspathClass.class,
+                ClasspathIndirection.class))
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines("ClasspathClass::foo");
   }
@@ -77,29 +79,13 @@
         .addKeepMainRule(Main.class)
         // Keep the method that is overridden by the classpath class.
         .addKeepMethodRules(Reference.methodFromMethod(ProgramClass.class.getDeclaredMethod("foo")))
-        .addRunClasspathFiles(compileClasspath())
+        .addRunClasspathFiles(
+            compileToZip(
+                parameters,
+                ImmutableList.of(ProgramClass.class),
+                ClasspathClass.class,
+                ClasspathIndirection.class))
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines("ClasspathClass::foo");
   }
-
-  private Path compileClasspath() throws java.io.IOException, CompilationFailedException {
-    return compile(
-        ImmutableList.of(ClasspathClass.class, ClasspathIndirection.class),
-        ImmutableList.of(ProgramClass.class));
-  }
-
-  private Path compile(Collection<Class<?>> compilationUnit, Collection<Class<?>> classpath)
-      throws java.io.IOException, CompilationFailedException {
-    if (parameters.isCfRuntime()) {
-      Path out = temp.newFolder().toPath().resolve("out.jar");
-      writeClassesToJar(out, compilationUnit);
-      return out;
-    }
-    return testForD8()
-        .setMinApi(parameters.getApiLevel())
-        .addProgramClasses(compilationUnit)
-        .addClasspathClasses(classpath)
-        .compile()
-        .writeToZip();
-  }
 }
diff --git a/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java b/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java
index c27b55b..8866342 100644
--- a/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java
@@ -19,10 +19,9 @@
 import com.android.tools.r8.graph.ResolutionResult;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.ProguardConfigurationRule;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
-import java.util.ArrayList;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Set;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -45,13 +44,14 @@
       Class<?> methodToBeKept, Class<?> classToBeKept) throws Exception {
     return computeAppViewWithLiveness(
         buildClasses(I.class, J.class, K.class, L.class, A.class, Main.class).build(),
-        factory -> {
-          List<ProguardConfigurationRule> rules = new ArrayList<>();
-          rules.addAll(buildKeepRuleForClassAndMethods(methodToBeKept, factory));
-          rules.addAll(buildKeepRuleForClass(classToBeKept, factory));
-          rules.addAll(buildKeepRuleForClassAndMethods(Main.class, factory));
-          return rules;
-        });
+        factory ->
+            buildConfigForRules(
+                factory,
+                ImmutableList.<ProguardConfigurationRule>builder()
+                    .addAll(buildKeepRuleForClassAndMethods(methodToBeKept, factory))
+                    .addAll(buildKeepRuleForClass(classToBeKept, factory))
+                    .addAll(buildKeepRuleForClassAndMethods(Main.class, factory))
+                    .build()));
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BackwardsCompatibleSpecificationTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BackwardsCompatibleSpecificationTest.java
index 54f3acb..6b46ee4 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BackwardsCompatibleSpecificationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BackwardsCompatibleSpecificationTest.java
@@ -7,7 +7,6 @@
 
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestRuntime.NoneRuntime;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.google.common.collect.ImmutableList;
@@ -33,7 +32,7 @@
   private final String release;
 
   public BackwardsCompatibleSpecificationTest(TestParameters parameters, String release) {
-    assertEquals(NoneRuntime.getInstance(), parameters.getRuntime());
+    parameters.assertNoneRuntime();
     this.release = release;
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibaryChecksumsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibaryChecksumsTest.java
index 3e819e2..8280f92 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibaryChecksumsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibaryChecksumsTest.java
@@ -5,7 +5,6 @@
 
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.CompilationMode;
@@ -16,7 +15,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.NoneRuntime;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.utils.AndroidApiLevel;
@@ -36,7 +34,7 @@
   }
 
   public DesugaredLibaryChecksumsTest(TestParameters parameters) {
-    assertEquals(NoneRuntime.getInstance(), parameters.getRuntime());
+    parameters.assertNoneRuntime();
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryConfigurationParsingTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryConfigurationParsingTest.java
index 92fc7be..1141cb3 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryConfigurationParsingTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryConfigurationParsingTest.java
@@ -17,7 +17,6 @@
 import com.android.tools.r8.TestDiagnosticMessagesImpl;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.TestRuntime.NoneRuntime;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
@@ -28,6 +27,7 @@
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.SemanticVersion;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.StringUtils.BraceType;
 import com.google.common.collect.ImmutableList;
@@ -51,7 +51,7 @@
   }
 
   public DesugaredLibraryConfigurationParsingTest(TestParameters parameters) {
-    assertEquals(NoneRuntime.getInstance(), parameters.getRuntime());
+    parameters.assertNoneRuntime();
   }
 
   final AndroidApiLevel minApi = AndroidApiLevel.B;
@@ -73,7 +73,7 @@
               DesugaredLibraryConfigurationParser.MAX_SUPPORTED_VERSION)
           .put("group_id", "com.tools.android")
           .put("artifact_id", "desugar_jdk_libs")
-          .put("version", "0.0.0")
+          .put("version", DesugaredLibraryConfigurationParser.MIN_SUPPORTED_VERSION.toString())
           .put("required_compilation_api_level", 1)
           .put("synthesized_library_classes_package_prefix", "j$.")
           .put("common_flags", Collections.emptyList())
@@ -141,6 +141,7 @@
             "version",
             "required_compilation_api_level",
             "synthesized_library_classes_package_prefix",
+            "common_flags",
             "program_flags",
             "library_flags");
     for (String key : requiredKeys) {
@@ -158,9 +159,13 @@
   }
 
   @Test
-  public void testUnsupportedFormatMissingFlags() {
+  public void testUnsupportedVersion() {
     LinkedHashMap<String, Object> data = template();
-    data.remove("common_flags");
+    SemanticVersion minVersion = DesugaredLibraryConfigurationParser.MIN_SUPPORTED_VERSION;
+    data.put(
+        "version",
+        new SemanticVersion(minVersion.getMajor(), minVersion.getMinor(), minVersion.getPatch() - 1)
+            .toString());
     runFailing(
         toJson(data),
         diagnostics ->
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DontKeepBootstrapClassesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DontKeepBootstrapClassesTest.java
index 1d66c7f..5f323bc 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DontKeepBootstrapClassesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DontKeepBootstrapClassesTest.java
@@ -5,11 +5,9 @@
 
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.TestRuntime.NoneRuntime;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import java.util.Arrays;
 import java.util.function.Consumer;
@@ -28,7 +26,7 @@
   final AndroidApiLevel minApiLevel = AndroidApiLevel.B;
 
   public DontKeepBootstrapClassesTest(TestParameters parameters) {
-    assertEquals(NoneRuntime.getInstance(), parameters.getRuntime());
+    parameters.assertNoneRuntime();
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
index 846b65d..2225ceb 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
@@ -13,7 +13,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.NoneRuntime;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
@@ -99,7 +98,7 @@
   private final AndroidApiLevel targetApi = AndroidApiLevel.Q;
 
   public ExtractWrapperTypesTest(TestParameters parameters) {
-    assertEquals(NoneRuntime.getInstance(), parameters.getRuntime());
+    parameters.assertNoneRuntime();
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
index 23d3658..6414d4b 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
@@ -37,7 +37,13 @@
   private final TestParameters parameters;
   private final boolean shrinkDesugaredLibrary;
   private static final String expectedOutput =
-      StringUtils.lines("Caught java.time.format.DateTimeParseException", "true", "Hello, world");
+      StringUtils.lines(
+          "Caught java.time.format.DateTimeParseException",
+          "true",
+          "1970-01-02T10:17:36.789Z",
+          "GMT",
+          "GMT",
+          "Hello, world");
 
   @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
   public static List<Object[]> data() {
@@ -62,13 +68,15 @@
     String expectedInstanceOfTypes;
     if (parameters.getApiLevel().getLevel() >= 26) {
       expectedInvokeHolders =
-          ImmutableSet.of("java.time.Clock", "java.time.LocalDate", "java.time.ZoneOffset");
+          ImmutableSet.of(
+              "java.time.Clock", "java.time.LocalDate", "java.time.ZoneOffset", "java.time.ZoneId");
       expectedCatchGuards = ImmutableSet.of("java.time.format.DateTimeParseException");
       expectedCheckCastType = ImmutableSet.of("java.time.ZoneId");
       expectedInstanceOfTypes = "java.time.ZoneOffset";
     } else {
       expectedInvokeHolders =
-          ImmutableSet.of("j$.time.Clock", "j$.time.LocalDate", "j$.time.ZoneOffset");
+          ImmutableSet.of(
+              "j$.time.Clock", "j$.time.LocalDate", "j$.time.ZoneOffset", "j$.time.ZoneId");
       expectedCatchGuards = ImmutableSet.of("j$.time.format.DateTimeParseException");
       expectedCheckCastType = ImmutableSet.of("j$.time.ZoneId");
       expectedInstanceOfTypes = "j$.time.ZoneOffset";
@@ -166,6 +174,14 @@
         System.out.println("NOT!");
       }
       System.out.println(java.time.ZoneOffset.getAvailableZoneIds().size() > 0);
+
+      System.out.println(
+          java.util.Date.from(new java.util.Date(123456789).toInstant()).toInstant());
+
+      java.util.TimeZone timeZone = java.util.TimeZone.getTimeZone(java.time.ZoneId.of("GMT"));
+      System.out.println(timeZone.getID());
+      System.out.println(timeZone.toZoneId().getId());
+
       System.out.println("Hello, world");
     }
   }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
index 04a7258..32bbe65 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
@@ -122,6 +122,26 @@
       System.out.println("43");
       System.out.println(myAtomicInteger.updateAndGet(x -> x + 100));
       System.out.println("145");
+
+      try {
+        MyDateNoOverride.from(myCal.toInstant());
+        System.out.println("b/159441805 fixed");
+      } catch (NoSuchMethodError e) {
+        // TODO(b/159441805): Should not throw.
+      }
+
+      try {
+        MyDateOverride.from(myCal.toInstant());
+        System.out.println("b/159441805 fixed");
+      } catch (NoSuchMethodError e) {
+        // TODO(b/159441805): Should not throw.
+      }
+
+      System.out.println(MyDateDoubleOverride.from(myCal.toInstant()).toInstant());
+      System.out.println("1970-01-02T10:17:36.788Z");
+
+      System.out.println(MyDateTrippleOverride.from(myCal.toInstant()).toInstant());
+      System.out.println("1970-01-02T10:17:36.788Z");
     }
 
     public static void polyTypes() {
@@ -212,6 +232,22 @@
     public Instant toInstant() {
       return super.toInstant().plusSeconds(3);
     }
+
+    public static Date from(Instant instant) {
+      return new Date(123456788);
+    }
+  }
+
+  static class MyDateTrippleOverride extends MyDateDoubleOverride {
+
+    public MyDateTrippleOverride(long date) {
+      super(date);
+    }
+
+    @Override
+    public Instant toInstant() {
+      return super.toInstant().plusSeconds(6);
+    }
   }
 
   static class MyDateNoOverride extends Date {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicTimeConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicTimeConversionTest.java
deleted file mode 100644
index bb728fb..0000000
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicTimeConversionTest.java
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.desugar.desugaredlibrary.conversiontests;
-
-import static junit.framework.TestCase.assertTrue;
-
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import java.time.ZoneId;
-import java.util.List;
-import java.util.TimeZone;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-@RunWith(Parameterized.class)
-public class BasicTimeConversionTest extends DesugaredLibraryTestBase {
-
-  private final TestParameters parameters;
-  private final boolean shrinkDesugaredLibrary;
-  private static final String GMT = StringUtils.lines("GMT");
-
-  @Parameters(name = "{0}, shrinkDesugaredLibrary: {1}")
-  public static List<Object[]> data() {
-    return buildParameters(
-        getConversionParametersUpToExcluding(AndroidApiLevel.O), BooleanUtils.values());
-  }
-
-  public BasicTimeConversionTest(TestParameters parameters, boolean shrinkDesugaredLibrary) {
-    this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
-    this.parameters = parameters;
-  }
-
-  @Test
-  public void testRewrittenAPICallsD8() throws Exception {
-    KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
-    testForD8()
-        .setMinApi(parameters.getApiLevel())
-        .addInnerClasses(BasicTimeConversionTest.class)
-        .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
-        .compile()
-        .addDesugaredCoreLibraryRunClassPath(
-            this::buildDesugaredLibrary,
-            parameters.getApiLevel(),
-            keepRuleConsumer.get(),
-            shrinkDesugaredLibrary)
-        .inspect(this::checkAPIRewritten)
-        .run(parameters.getRuntime(), Executor.class)
-        .assertSuccessWithOutput(GMT);
-    if (shrinkDesugaredLibrary) {
-      checkKeepRules(keepRuleConsumer.get());
-    }
-  }
-
-  private void checkKeepRules(String keepRules) {
-    assertTrue(keepRules.contains("TimeConversion"));
-  }
-
-  @Test
-  public void testRewrittenAPICallsR8() throws Exception {
-    KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
-    testForR8(parameters.getBackend())
-        .setMinApi(parameters.getApiLevel())
-        .addKeepMainRule(Executor.class)
-        .addInnerClasses(BasicTimeConversionTest.class)
-        .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
-        .compile()
-        .addDesugaredCoreLibraryRunClassPath(
-            this::buildDesugaredLibrary,
-            parameters.getApiLevel(),
-            keepRuleConsumer.get(),
-            shrinkDesugaredLibrary)
-        .run(parameters.getRuntime(), Executor.class)
-        .assertSuccessWithOutput(GMT);
-    if (shrinkDesugaredLibrary) {
-      checkKeepRules(keepRuleConsumer.get());
-    }
-  }
-
-  private void checkAPIRewritten(CodeInspector inspector) {
-    MethodSubject mainMethod = inspector.clazz(Executor.class).uniqueMethodWithName("main");
-    // Check the API calls are not using j$ types.
-    assertTrue(
-        mainMethod
-            .streamInstructions()
-            .anyMatch(
-                instr ->
-                    instr.isInvokeStatic()
-                        && instr.getMethod().name.toString().equals("getTimeZone")
-                        && instr
-                            .getMethod()
-                            .proto
-                            .parameters
-                            .values[0]
-                            .toString()
-                            .equals("java.time.ZoneId")));
-    assertTrue(
-        mainMethod
-            .streamInstructions()
-            .anyMatch(
-                instr ->
-                    instr.isInvokeVirtual()
-                        && instr.getMethod().name.toString().equals("toZoneId")
-                        && instr
-                            .getMethod()
-                            .proto
-                            .returnType
-                            .toString()
-                            .equals("java.time.ZoneId")));
-    // Check the conversion instructions are present.
-    assertTrue(
-        mainMethod
-            .streamInstructions()
-            .anyMatch(
-                instr ->
-                    instr.isInvokeStatic()
-                        && instr.getMethod().name.toString().equals("convert")
-                        && instr
-                            .getMethod()
-                            .proto
-                            .parameters
-                            .values[0]
-                            .toString()
-                            .equals("j$.time.ZoneId")));
-    assertTrue(
-        mainMethod
-            .streamInstructions()
-            .anyMatch(
-                instr ->
-                    instr.isInvokeStatic()
-                        && instr.getMethod().name.toString().equals("convert")
-                        && instr
-                            .getMethod()
-                            .proto
-                            .parameters
-                            .values[0]
-                            .toString()
-                            .equals("java.time.ZoneId")));
-  }
-
-  static class Executor {
-    public static void main(String[] args) {
-      ZoneId zoneId = ZoneId.systemDefault();
-      // Following is a call where java.time.ZoneId is a parameter type (getTimeZone()).
-      TimeZone timeZone = TimeZone.getTimeZone(zoneId);
-      // Following is a call where java.time.ZoneId is a return type (toZoneId()).
-      System.out.println(timeZone.toZoneId().getId());
-    }
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
index 9c86160..7d22782 100644
--- a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
@@ -9,7 +9,8 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 
-import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.ToolHelper.ProcessResult;
@@ -30,11 +31,24 @@
 import java.nio.file.Path;
 import java.util.Collections;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
+@RunWith(Parameterized.class)
 public class TargetLookupTest extends SmaliTestBase {
 
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public TargetLookupTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
+
   @Test
-  public void lookupDirect() {
+  public void lookupDirect() throws Exception {
     SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
 
     builder.addDefaultConstructor();
@@ -100,7 +114,7 @@
   }
 
   @Test
-  public void lookupDirectSuper() {
+  public void lookupDirectSuper() throws Exception {
     SmaliBuilder builder = new SmaliBuilder("TestSuper");
 
     builder.addDefaultConstructor();
@@ -185,7 +199,7 @@
   }
 
   @Test
-  public void lookupFieldWithDefaultInInterface() throws CompilationFailedException {
+  public void lookupFieldWithDefaultInInterface() throws Exception {
     SmaliBuilder builder = new SmaliBuilder();
 
     builder.addInterface("Interface");
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
index 5048226..ca35303 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
@@ -60,7 +60,7 @@
             .read(proguardMap, executorService)
             .toDirect();
     InternalOptions options = new InternalOptions();
-    appView = AppView.createForR8(new AppInfoWithClassHierarchy(program), options);
+    appView = AppView.createForR8(new AppInfoWithClassHierarchy(program));
     appView.setAppServices(AppServices.builder(appView).build());
     subtypingInfo = new SubtypingInfo(program.allClasses(), program);
   }
diff --git a/src/test/java/com/android/tools/r8/internal/proto/ProtoShrinkingTestBase.java b/src/test/java/com/android/tools/r8/internal/proto/ProtoShrinkingTestBase.java
index 79ce685..23d12f6 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/ProtoShrinkingTestBase.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/ProtoShrinkingTestBase.java
@@ -115,7 +115,7 @@
           continue;
         }
 
-        IRCode code = methodSubject.buildIR(dexItemFactory);
+        IRCode code = methodSubject.buildIR();
         InvokeMethod invoke =
             GeneratedMessageLiteShrinker.getNewMessageInfoInvoke(code, references);
         assert invoke != null;
diff --git a/src/test/java/com/android/tools/r8/internal/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/internal/retrace/RetraceTests.java
index 6fea76f..c8b98d7 100644
--- a/src/test/java/com/android/tools/r8/internal/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/internal/retrace/RetraceTests.java
@@ -16,6 +16,7 @@
 import com.android.tools.r8.retrace.Retrace;
 import com.android.tools.r8.retrace.RetraceCommand;
 import com.android.tools.r8.retrace.stacktraces.StackTraceForTest;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.function.BiConsumer;
 import junit.framework.TestCase;
@@ -32,6 +33,13 @@
     return getTestParameters().withNoneRuntime().build();
   }
 
+  private static String REMAPPER_REGEX =
+      "(?:.*?\\bat\\s+%c\\.%m\\s*\\(%S\\)\\s*)"
+          + "|(?:(?:.*?[:\"]\\s+)?%c(?::.*)?)"
+          + "|(?:.*?%t\\s+%c\\.%m\\s*\\(%a\\)\\s*)";
+  private static String FINSKY_REGEX = "(?:.*Finsky\\s+:\\s+\\[\\d+\\]\\s+%c\\.%m\\(%l\\):.*)";
+  private static String SMILEY_EMOJI = "\uD83D\uDE00";
+
   public RetraceTests(TestParameters parameters) {}
 
   @Test
@@ -41,7 +49,82 @@
 
   @Test
   public void testFinskyStackTrace() {
-    runRetraceTest(new FinskyStackTrace(), "(?:.*Finsky\\s+:\\s+\\[\\d+\\]\\s+%c\\.%m\\(%l\\):.*)");
+    runRetraceTest(new FinskyStackTrace(), FINSKY_REGEX);
+  }
+
+  @Test
+  public void testCronetRemapperRegexpTest() {
+    runRetraceTest(new CronetStackTrace(), REMAPPER_REGEX);
+  }
+
+  @Test
+  public void testCronetAndFinskyStackTrace() {
+    CronetStackTrace cronetStackTrace = new CronetStackTrace();
+    FinskyStackTrace finskyStackTrace = new FinskyStackTrace();
+    runRetraceTest(
+        new StackTraceForTest() {
+          @Override
+          public List<String> obfuscatedStackTrace() {
+            ArrayList<String> obfuscated = new ArrayList<>();
+            obfuscated.addAll(cronetStackTrace.obfuscatedStackTrace());
+            obfuscated.addAll(finskyStackTrace.obfuscatedStackTrace());
+            return obfuscated;
+          }
+
+          @Override
+          public String mapping() {
+            return cronetStackTrace.mapping();
+          }
+
+          @Override
+          public List<String> retracedStackTrace() {
+            ArrayList<String> retraced = new ArrayList<>();
+            retraced.addAll(cronetStackTrace.retracedStackTrace());
+            retraced.addAll(finskyStackTrace.retracedStackTrace());
+            return retraced;
+          }
+
+          @Override
+          public int expectedWarnings() {
+            return 0;
+          }
+        },
+        FINSKY_REGEX + "|" + DEFAULT_REGULAR_EXPRESSION);
+  }
+
+  @Test
+  public void testCronetAndFinskyStackTraceRemapperRegExp() {
+    CronetStackTrace cronetStackTrace = new CronetStackTrace();
+    FinskyStackTrace finskyStackTrace = new FinskyStackTrace();
+    runRetraceTest(
+        new StackTraceForTest() {
+          @Override
+          public List<String> obfuscatedStackTrace() {
+            ArrayList<String> obfuscated = new ArrayList<>();
+            obfuscated.addAll(cronetStackTrace.obfuscatedStackTrace());
+            obfuscated.addAll(finskyStackTrace.obfuscatedStackTrace());
+            return obfuscated;
+          }
+
+          @Override
+          public String mapping() {
+            return cronetStackTrace.mapping();
+          }
+
+          @Override
+          public List<String> retracedStackTrace() {
+            ArrayList<String> retraced = new ArrayList<>();
+            retraced.addAll(cronetStackTrace.retracedStackTrace());
+            retraced.addAll(finskyStackTrace.retracedStackTrace());
+            return retraced;
+          }
+
+          @Override
+          public int expectedWarnings() {
+            return 0;
+          }
+        },
+        FINSKY_REGEX + "|" + REMAPPER_REGEX);
   }
 
   @Test
@@ -49,6 +132,39 @@
     runRetraceTest(new VelvetStackTrace());
   }
 
+  @Test
+  public void testNonAscii() {
+    CronetStackTrace cronetStackTrace = new CronetStackTrace();
+    runRetraceTest(
+        new StackTraceForTest() {
+          @Override
+          public List<String> obfuscatedStackTrace() {
+            ArrayList<String> smileyObf = new ArrayList<>();
+            smileyObf.add(SMILEY_EMOJI);
+            smileyObf.addAll(cronetStackTrace.obfuscatedStackTrace());
+            return smileyObf;
+          }
+
+          @Override
+          public String mapping() {
+            return cronetStackTrace.mapping();
+          }
+
+          @Override
+          public List<String> retracedStackTrace() {
+            ArrayList<String> smileyObf = new ArrayList<>();
+            smileyObf.add(SMILEY_EMOJI);
+            smileyObf.addAll(cronetStackTrace.retracedStackTrace());
+            return smileyObf;
+          }
+
+          @Override
+          public int expectedWarnings() {
+            return 0;
+          }
+        });
+  }
+
   private TestDiagnosticMessagesImpl runRetraceTest(StackTraceForTest stackTraceForTest) {
     return runRetraceTest(stackTraceForTest, DEFAULT_REGULAR_EXPRESSION);
   }
diff --git a/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java b/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java
index 586129b..04c8788 100644
--- a/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java
+++ b/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.ir;
 
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppView;
@@ -24,11 +26,25 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
+@RunWith(Parameterized.class)
 public class BasicBlockIteratorTest extends SmaliTestBase {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
   @Rule
   public ExpectedException thrown = ExpectedException.none();
 
+  public BasicBlockIteratorTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
+
   /**
    * Simple test IR, which has three blocks:
    *
@@ -55,7 +71,7 @@
     InternalOptions options = new InternalOptions();
     DexApplication dexApplication =
         new ApplicationReader(application, options, Timing.empty()).read();
-    AppView<?> appView = AppView.createForD8(new AppInfo(dexApplication), options);
+    AppView<?> appView = AppView.createForD8(new AppInfo(dexApplication));
 
     // Build the code, and split the code into three blocks.
     MethodSubject methodSubject = getMethodSubject(application, signature);
diff --git a/src/test/java/com/android/tools/r8/ir/InlineTest.java b/src/test/java/com/android/tools/r8/ir/InlineTest.java
index a2246c0..2bd2803 100644
--- a/src/test/java/com/android/tools/r8/ir/InlineTest.java
+++ b/src/test/java/com/android/tools/r8/ir/InlineTest.java
@@ -6,6 +6,9 @@
 
 import static org.junit.Assert.assertEquals;
 
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppServices;
 import com.android.tools.r8.graph.AppView;
@@ -40,9 +43,22 @@
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
+@RunWith(Parameterized.class)
 public class InlineTest extends IrInjectionTestBase {
 
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public InlineTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
+
   private TestApplication buildTestApplication(
       DexApplication application,
       InternalOptions options,
@@ -51,7 +67,7 @@
       throws ExecutionException {
     DirectMappedDexApplication directApp = application.asDirect();
     AppView<AppInfoWithClassHierarchy> appView =
-        AppView.createForR8(new AppInfoWithClassHierarchy(directApp), options);
+        AppView.createForR8(new AppInfoWithClassHierarchy(directApp));
     appView.setAppServices(AppServices.builder(appView).build());
     ExecutorService executorService = ThreadUtils.getExecutorService(options);
     SubtypingInfo subtypingInfo = new SubtypingInfo(directApp.allClasses(), directApp);
@@ -73,7 +89,9 @@
     Reporter reporter = new Reporter();
     ProguardConfiguration proguardConfiguration =
         ProguardConfiguration.builder(new DexItemFactory(), reporter).build();
-    return new InternalOptions(proguardConfiguration, reporter);
+    InternalOptions options = new InternalOptions(proguardConfiguration, reporter);
+    options.programConsumer = DexIndexedConsumer.emptyConsumer();
+    return options;
   }
 
   private TestApplication codeForMethodReplaceTest(int a, int b) throws ExecutionException {
@@ -129,10 +147,10 @@
     MethodSubject methodSubject = getMethodSubject(application, signature);
 
     MethodSubject methodASubject = getMethodSubject(application, signatureA);
-    IRCode codeA = methodASubject.buildIR(options.itemFactory);
+    IRCode codeA = methodASubject.buildIR();
 
     MethodSubject methodBSubject = getMethodSubject(application, signatureB);
-    IRCode codeB = methodBSubject.buildIR(options.itemFactory);
+    IRCode codeB = methodBSubject.buildIR();
 
     return buildTestApplication(
         application, options, methodSubject, ImmutableList.of(codeA, codeB));
@@ -211,7 +229,7 @@
     MethodSubject methodSubject = getMethodSubject(application, signature);
 
     MethodSubject methodASubject = getMethodSubject(application, signatureA);
-    IRCode codeA = methodASubject.buildIR(options.itemFactory);
+    IRCode codeA = methodASubject.buildIR();
 
     return buildTestApplication(application, options, methodSubject, ImmutableList.of(codeA));
   }
@@ -291,13 +309,13 @@
     List<IRCode> additionalCode = new ArrayList<>();
     for (int i = 0; i < 3; i++) {
       MethodSubject methodASubject = getMethodSubject(application, signatureA);
-      IRCode codeA = methodASubject.buildIR(options.itemFactory);
+      IRCode codeA = methodASubject.buildIR();
       additionalCode.add(codeA);
     }
 
     for (int i = 0; i < 3; i++) {
       MethodSubject methodBSubject = getMethodSubject(application, signatureB);
-      IRCode codeB = methodBSubject.buildIR(options.itemFactory);
+      IRCode codeB = methodBSubject.buildIR();
       additionalCode.add(codeB);
     }
 
@@ -421,10 +439,10 @@
     MethodSubject methodSubject = getMethodSubject(application, signature);
 
     MethodSubject methodASubject = getMethodSubject(application, signatureA);
-    IRCode codeA = methodASubject.buildIR(options.itemFactory);
+    IRCode codeA = methodASubject.buildIR();
 
     MethodSubject methodBSubject = getMethodSubject(application, signatureB);
-    IRCode codeB = methodBSubject.buildIR(options.itemFactory);
+    IRCode codeB = methodBSubject.buildIR();
 
     return buildTestApplication(
         application, options, methodSubject, ImmutableList.of(codeA, codeB));
@@ -534,10 +552,10 @@
     MethodSubject methodSubject = getMethodSubject(application, signature);
 
     MethodSubject methodASubject = getMethodSubject(application, signatureA);
-    IRCode codeA = methodASubject.buildIR(options.itemFactory);
+    IRCode codeA = methodASubject.buildIR();
 
     MethodSubject methodBSubject = getMethodSubject(application, signatureB);
-    IRCode codeB = methodBSubject.buildIR(options.itemFactory);
+    IRCode codeB = methodBSubject.buildIR();
 
     return buildTestApplication(
         application, options, methodSubject, ImmutableList.of(codeA, codeB));
@@ -645,10 +663,10 @@
     MethodSubject methodSubject = getMethodSubject(application, signature);
 
     MethodSubject methodASubject = getMethodSubject(application, signatureA);
-    IRCode codeA = methodASubject.buildIR(options.itemFactory);
+    IRCode codeA = methodASubject.buildIR();
 
     MethodSubject methodBSubject = getMethodSubject(application, signatureB);
-    IRCode codeB = methodBSubject.buildIR(options.itemFactory);
+    IRCode codeB = methodBSubject.buildIR();
 
     return buildTestApplication(
         application, options, methodSubject, ImmutableList.of(codeA, codeB));
@@ -761,13 +779,13 @@
     List<IRCode> additionalCode = new ArrayList<>();
     for (int i = 0; i < 3; i++) {
       MethodSubject methodASubject = getMethodSubject(application, signatureA);
-      IRCode codeA = methodASubject.buildIR(options.itemFactory);
+      IRCode codeA = methodASubject.buildIR();
       additionalCode.add(codeA);
     }
 
     for (int i = 0; i < 3; i++) {
       MethodSubject methodBSubject = getMethodSubject(application, signatureB);
-      IRCode codeB = methodBSubject.buildIR(options.itemFactory);
+      IRCode codeB = methodBSubject.buildIR();
       additionalCode.add(codeB);
     }
 
@@ -917,13 +935,13 @@
     List<IRCode> additionalCode = new ArrayList<>();
     for (int i = 0; i < 3; i++) {
       MethodSubject methodASubject = getMethodSubject(application, signatureA);
-      IRCode codeA = methodASubject.buildIR(options.itemFactory);
+      IRCode codeA = methodASubject.buildIR();
       additionalCode.add(codeA);
     }
 
     for (int i = 0; i < 3; i++) {
       MethodSubject methodBSubject = getMethodSubject(application, signatureB);
-      IRCode codeB = methodBSubject.buildIR(options.itemFactory);
+      IRCode codeB = methodBSubject.buildIR();
       additionalCode.add(codeB);
     }
 
@@ -1160,10 +1178,10 @@
     MethodSubject methodSubject = getMethodSubject(application, signature);
 
     MethodSubject methodASubject = getMethodSubject(application, signatureA);
-    IRCode codeA = methodASubject.buildIR(options.itemFactory);
+    IRCode codeA = methodASubject.buildIR();
 
     MethodSubject methodBSubject = getMethodSubject(application, signatureB);
-    IRCode codeB = methodBSubject.buildIR(options.itemFactory);
+    IRCode codeB = methodBSubject.buildIR();
 
     return buildTestApplication(
         application, options, methodSubject, ImmutableList.of(codeA, codeB));
diff --git a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
index e9d9abd..83f64ee 100644
--- a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
@@ -14,7 +14,6 @@
 import com.android.tools.r8.ir.code.InstructionListIterator;
 import com.android.tools.r8.ir.code.ValueNumberGenerator;
 import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.MainDexClasses;
 import com.android.tools.r8.smali.SmaliBuilder;
 import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
@@ -34,22 +33,12 @@
 
   protected DexApplication buildApplication(SmaliBuilder builder, InternalOptions options) {
     try {
-      return buildApplication(
-          AndroidApp.builder().addDexProgramData(builder.compile(), Origin.unknown()).build(),
-          options);
+      return new ApplicationReader(builder.build(), options, Timing.empty()).read();
     } catch (IOException | RecognitionException | ExecutionException e) {
       throw new RuntimeException(e);
     }
   }
 
-  protected DexApplication buildApplication(AndroidApp input, InternalOptions options) {
-    try {
-      return new ApplicationReader(input, options, Timing.empty()).read();
-    } catch (IOException e) {
-      throw new RuntimeException(e);
-    }
-  }
-
   protected MethodSubject getMethodSubject(DexApplication application, MethodSignature signature) {
     return getMethodSubject(
         application,
@@ -89,7 +78,7 @@
       this.application = appView.appInfo().app();
       this.appView = appView;
       this.method = method.getMethod();
-      this.code = method.buildIR(appView.dexItemFactory());
+      this.code = method.buildIR();
       this.additionalCode = additionalCode;
       this.consumers = new AndroidAppConsumers(appView.options());
     }
diff --git a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
index 7104c9a..1662269 100644
--- a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
+++ b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
@@ -8,9 +8,10 @@
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.Add;
 import com.android.tools.r8.ir.code.BasicBlock;
@@ -23,16 +24,28 @@
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.smali.SmaliBuilder;
 import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
-import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import com.google.common.collect.ImmutableList;
 import java.util.ArrayList;
 import java.util.List;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
+@RunWith(Parameterized.class)
 public class SplitBlockTest extends IrInjectionTestBase {
 
-  private TestApplication codeWithoutCatchHandlers() {
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public SplitBlockTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
+
+  private TestApplication codeWithoutCatchHandlers() throws Exception {
     SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
 
     String returnType = "int";
@@ -59,12 +72,10 @@
         "    return-void"
     );
 
-    InternalOptions options = new InternalOptions();
-    DexApplication application = buildApplication(builder, options);
-    AppView<?> appView = AppView.createForD8(new AppInfo(application), options);
+    AppView<AppInfo> appView = computeAppView(builder.build());
 
     // Return the processed method for inspection.
-    MethodSubject methodSubject = getMethodSubject(application, signature);
+    MethodSubject methodSubject = getMethodSubject(appView.appInfo().app(), signature);
     return new TestApplication(appView, methodSubject);
   }
 
@@ -137,7 +148,8 @@
     }
   }
 
-  private TestApplication codeWithCatchHandlers(boolean shouldThrow, boolean twoGuards) {
+  private TestApplication codeWithCatchHandlers(boolean shouldThrow, boolean twoGuards)
+      throws Exception {
     SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
 
     String secondGuard = twoGuards ?
@@ -175,12 +187,10 @@
         "    return-void"
     );
 
-    InternalOptions options = new InternalOptions();
-    DexApplication application = buildApplication(builder, options);
-    AppView<?> appView = AppView.createForD8(new AppInfo(application), options);
+    AppView<?> appView = computeAppView(builder.build());
 
     // Return the processed method for inspection.
-    MethodSubject methodSubject = getMethodSubject(application, signature);
+    MethodSubject methodSubject = getMethodSubject(appView.appInfo().app(), signature);
     return new TestApplication(appView, methodSubject);
   }
 
@@ -270,7 +280,7 @@
     runCatchHandlerSplitThreeTest(true, true);
   }
 
-  private TestApplication codeWithIf(boolean hitTrueBranch) {
+  private TestApplication codeWithIf(boolean hitTrueBranch) throws Exception {
     SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
 
     String returnType = "int";
@@ -299,12 +309,10 @@
         "    return-void"
     );
 
-    InternalOptions options = new InternalOptions();
-    DexApplication application = buildApplication(builder, options);
-    AppView<?> appView = AppView.createForD8(new AppInfo(application), options);
+    AppView<?> appView = computeAppView(builder.build());
 
     // Return the processed method for inspection.
-    MethodSubject methodSubject = getMethodSubject(application, signature);
+    MethodSubject methodSubject = getMethodSubject(appView.appInfo().app(), signature);
     return new TestApplication(appView, methodSubject);
   }
 
@@ -385,7 +393,7 @@
     splitBeforeReturn(true);
   }
 
-  private TestApplication codeWithSwitch(boolean hitCase) {
+  private TestApplication codeWithSwitch(boolean hitCase) throws Exception {
     SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
 
     String returnType = "int";
@@ -422,12 +430,10 @@
         "    return-void"
     );
 
-    InternalOptions options = new InternalOptions();
-    DexApplication application = buildApplication(builder, options);
-    AppView<?> appView = AppView.createForD8(new AppInfo(application), options);
+    AppView<?> appView = computeAppView(builder.build());
 
     // Return the processed method for inspection.
-    MethodSubject methodSubject = getMethodSubject(application, signature);
+    MethodSubject methodSubject = getMethodSubject(appView.appInfo().app(), signature);
     return new TestApplication(appView, methodSubject);
   }
 
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/AnalysisTestBase.java b/src/test/java/com/android/tools/r8/ir/analysis/AnalysisTestBase.java
index 80fef97..ce5cbce 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/AnalysisTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/AnalysisTestBase.java
@@ -64,7 +64,7 @@
   public void buildAndCheckIR(String methodName, Consumer<IRCode> irInspector) {
     CodeInspector inspector = new CodeInspector(appView.appInfo().app());
     MethodSubject methodSubject = inspector.clazz(className).uniqueMethodWithName(methodName);
-    irInspector.accept(methodSubject.buildIR(appView.dexItemFactory()));
+    irInspector.accept(methodSubject.buildIR());
   }
 
   @SuppressWarnings("unchecked")
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
index 5a53fb5..86f4e3d 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
@@ -134,7 +134,7 @@
                 timing)
             .read()
             .toDirect();
-    return AppView.createForR8(new AppInfoWithClassHierarchy(application), options);
+    return AppView.createForR8(new AppInfoWithClassHierarchy(application));
   }
 
   private DexEncodedField uniqueFieldByName(DexProgramClass clazz, String name) {
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
index 06f2813..a4c9785 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
@@ -11,6 +11,8 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
@@ -39,8 +41,22 @@
 import java.util.Map;
 import java.util.function.BiConsumer;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
+@RunWith(Parameterized.class)
 public class NullabilityTest extends NonNullTrackerTestBase {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public NullabilityTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
+
   private void buildAndTest(
       Class<?> mainClass,
       MethodSignature signature,
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeAnalysisTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeAnalysisTest.java
index d0cf699..40c87b6 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeAnalysisTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeAnalysisTest.java
@@ -11,10 +11,8 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.code.ArrayLength;
 import com.android.tools.r8.ir.code.CheckCast;
@@ -32,9 +30,7 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.smali.SmaliTestBase;
 import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Smali;
-import com.android.tools.r8.utils.Timing;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import com.google.common.collect.ImmutableList;
@@ -57,7 +53,6 @@
 
 @RunWith(Parameterized.class)
 public class TypeAnalysisTest extends SmaliTestBase {
-  private static final InternalOptions TEST_OPTIONS = new InternalOptions();
   private static final TypeElement NULL = TypeElement.getNull();
   private static final TypeElement SINGLE = TypeElement.getSingle();
   private static final TypeElement INT = TypeElement.getInt();
@@ -112,11 +107,8 @@
         .forEach(s -> smaliStringBuilder.append(s).append(System.lineSeparator()));
     byte[] content = Smali.compile(smaliStringBuilder.toString());
     AndroidApp app = AndroidApp.builder().addDexProgramData(content, Origin.unknown()).build();
-    DexApplication dexApplication =
-        new ApplicationReader(app, TEST_OPTIONS, Timing.empty()).read().toDirect();
-    inspection.accept(
-        AppView.createForD8(new AppInfo(dexApplication), TEST_OPTIONS),
-        new CodeInspector(dexApplication));
+    AppView<AppInfo> appView = computeAppView(app);
+    inspection.accept(appView, new CodeInspector(appView.appInfo().app()));
   }
 
   private static void forEachOutValue(IRCode irCode, BiConsumer<Value, TypeElement> consumer) {
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java
index cfe27c9..06de00a 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java
@@ -60,7 +60,7 @@
             .read()
             .toDirect();
     factory = options.itemFactory;
-    appView = AppView.createForR8(new AppInfoWithClassHierarchy(application), options);
+    appView = AppView.createForR8(new AppInfoWithClassHierarchy(application));
   }
 
   private TopTypeElement top() {
diff --git a/src/test/java/com/android/tools/r8/ir/conversion/PartialCallGraphTest.java b/src/test/java/com/android/tools/r8/ir/conversion/PartialCallGraphTest.java
index c838a0d..41c5ef5 100644
--- a/src/test/java/com/android/tools/r8/ir/conversion/PartialCallGraphTest.java
+++ b/src/test/java/com/android/tools/r8/ir/conversion/PartialCallGraphTest.java
@@ -45,7 +45,7 @@
               parser.parse(
                   createConfigurationForTesting(
                       ImmutableList.of("-keep class ** { void m1(); void m5(); }")));
-              return parser.getConfig().getRules();
+              return parser.getConfig();
             });
     this.options = appView.options();
     this.executorService = ThreadUtils.getExecutorService(options);
diff --git a/src/test/java/com/android/tools/r8/ir/conversion/StringSwitchConversionFromIfTest.java b/src/test/java/com/android/tools/r8/ir/conversion/StringSwitchConversionFromIfTest.java
index 2a700c3..294efee 100644
--- a/src/test/java/com/android/tools/r8/ir/conversion/StringSwitchConversionFromIfTest.java
+++ b/src/test/java/com/android/tools/r8/ir/conversion/StringSwitchConversionFromIfTest.java
@@ -17,8 +17,6 @@
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import com.google.common.collect.ImmutableList;
@@ -75,15 +73,12 @@
               for (String methodName : methodNames) {
                 MethodSubject methodSubject = classSubject.uniqueMethodWithName(methodName);
                 assertThat(methodSubject, isPresent());
-
-                DexItemFactory dexItemFactory = new DexItemFactory();
-                InternalOptions options = new InternalOptions(dexItemFactory, new Reporter());
-                options.enableStringSwitchConversion = enableStringSwitchConversion;
-
-                IRCode code = methodSubject.buildIR(options);
+                IRCode code = methodSubject.buildIR();
                 List<Value> hashCodeValues =
                     Streams.stream(code.instructions())
-                        .filter(instruction -> isInvokeStringHashCode(instruction, dexItemFactory))
+                        .filter(
+                            instruction ->
+                                isInvokeStringHashCode(instruction, inspector.getFactory()))
                         .map(Instruction::asInvokeVirtual)
                         .map(Instruction::outValue)
                         .collect(Collectors.toList());
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java b/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java
index fa6597f..dc6bc19 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java
@@ -134,7 +134,7 @@
     InternalOptions options = new InternalOptions();
     options.debug = true;
     AppInfo appInfo = new AppInfo(DexApplication.builder(options, null).build());
-    AppView<?> appView = AppView.createForD8(appInfo, options);
+    AppView<?> appView = AppView.createForD8(appInfo);
     IRCode code =
         new IRCode(
             options,
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
index a9f77ce..daa21f4 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
@@ -7,6 +7,8 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.ir.code.IRCode;
@@ -25,9 +27,22 @@
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import java.util.function.Consumer;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
+@RunWith(Parameterized.class)
 public class NonNullTrackerTest extends NonNullTrackerTestBase {
 
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public NonNullTrackerTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
+
   private void buildAndTest(
       Class<?> testClass,
       MethodSignature signature,
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTestBase.java b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTestBase.java
index 4820221..6b5f0cb 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTestBase.java
@@ -5,27 +5,13 @@
 
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.AppServices;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DirectMappedDexApplication;
-import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.Timing;
 
 public abstract class NonNullTrackerTestBase extends TestBase {
 
   protected AppView<? extends AppInfoWithClassHierarchy> build(Class<?> mainClass)
       throws Exception {
-    Timing timing = Timing.empty();
-    AndroidApp app = buildAndroidApp(ToolHelper.getClassAsBytes(mainClass));
-    InternalOptions options = new InternalOptions();
-    DirectMappedDexApplication dexApplication =
-        new ApplicationReader(app, options, timing).read().toDirect();
-    AppView<? extends AppInfoWithClassHierarchy> appView =
-        AppView.createForR8(new AppInfoWithClassHierarchy(dexApplication), options);
-    appView.setAppServices(AppServices.builder(appView).build());
-    return appView;
+    return computeAppViewWithSubtyping(buildAndroidApp(ToolHelper.getClassAsBytes(mainClass)));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
index 95557b6..7d7785f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
@@ -6,9 +6,11 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.Argument;
@@ -26,13 +28,26 @@
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.code.ValueNumberGenerator;
 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.Timing;
 import com.google.common.collect.ImmutableList;
 import java.util.LinkedList;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
-public class TrivialGotoEliminationTest {
+@RunWith(Parameterized.class)
+public class TrivialGotoEliminationTest extends TestBase {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public TrivialGotoEliminationTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
 
   private final IRMetadata metadata = IRMetadata.unknown();
 
@@ -92,10 +107,9 @@
   }
 
   @Test
-  public void trivialGotoLoopAsFallthrough() {
-    InternalOptions options = new InternalOptions();
-    DexApplication app = DexApplication.builder(new InternalOptions(), Timing.empty()).build();
-    AppView<AppInfo> appView = AppView.createForD8(new AppInfo(app), options);
+  public void trivialGotoLoopAsFallthrough() throws Exception {
+    AppView<AppInfo> appView = computeAppView(AndroidApp.builder().build());
+    InternalOptions options = appView.options();
     // Setup block structure:
     // block0:
     //   v0 <- argument
@@ -135,7 +149,7 @@
         new Value(
             0,
             TypeElement.fromDexType(
-                app.dexItemFactory.throwableType, Nullability.definitelyNotNull(), appView),
+                options.itemFactory.throwableType, Nullability.definitelyNotNull(), appView),
             null);
     instruction = new Argument(value, 0, false);
     instruction.setPosition(position);
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
new file mode 100644
index 0000000..5e7a17d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsCollisionMappingTest.java
@@ -0,0 +1,101 @@
+// Copyright (c) 2020, 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.unusedarguments;
+
+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.assertTrue;
+
+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;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class UnusedArgumentsCollisionMappingTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public UnusedArgumentsCollisionMappingTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    R8TestRunResult runResult =
+        testForR8(parameters.getBackend())
+            .addProgramClasses(Main.class)
+            .setMinApi(parameters.getApiLevel())
+            .addKeepMainRule(Main.class)
+            .enableInliningAnnotations()
+            .addKeepAttributeLineNumberTable()
+            .run(parameters.getRuntime(), Main.class)
+            .assertSuccessWithOutputLines("test with unused", "test with used: foo")
+            .inspect(this::verifyArgumentsRemoved);
+    // TODO(b/140851070): Duplicate entries for different methods.
+    assertEquals(
+        2,
+        StringUtils.splitLines(runResult.proguardMap()).stream()
+            .filter(line -> line.contains("void test(java.lang.String,java.lang.String)"))
+            .count());
+    assertEquals(
+        0,
+        StringUtils.splitLines(runResult.proguardMap()).stream()
+            .filter(line -> line.contains("void test(java.lang.String)"))
+            .count());
+  }
+
+  private void verifyArgumentsRemoved(CodeInspector inspector) {
+    ClassSubject main = inspector.clazz(Main.class);
+    assertThat(main, isPresent());
+    List<FoundMethodSubject> foundMethodSubjects = main.allMethods();
+    assertEquals(3, foundMethodSubjects.size());
+    boolean foundZeroArgumentMethod = false;
+    boolean foundOneArgumentMethod = false;
+    for (FoundMethodSubject method : foundMethodSubjects) {
+      if (method.getFinalName().equals("main")) {
+        continue;
+      }
+      foundZeroArgumentMethod |= method.getMethod().parameters().size() == 0;
+      foundOneArgumentMethod |= method.getMethod().parameters().size() == 1;
+    }
+    assertTrue(foundZeroArgumentMethod);
+    assertTrue(foundOneArgumentMethod);
+  }
+
+  public static class Main {
+
+    @NeverInline
+    public static void test(String unused) {
+      System.out.println("test with unused");
+    }
+
+    @NeverInline
+    public static void test(String used, String unused) {
+      System.out.println("test with used: " + used);
+    }
+
+    public static void main(String[] args) {
+      test("foo");
+      test("foo", "notused");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
index b4d2c3a..3c202dd 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
@@ -367,7 +367,7 @@
   public void multipleLiveTempRegisters() {
     InternalOptions options = new InternalOptions();
     AppView<AppInfo> appInfo =
-        AppView.createForD8(new AppInfo(DexApplication.builder(options, null).build()), options);
+        AppView.createForD8(new AppInfo(DexApplication.builder(options, null).build()));
     TypeElement objectType =
         TypeElement.fromDexType(options.itemFactory.objectType, Nullability.maybeNull(), appInfo);
     CollectMovesIterator moves = new CollectMovesIterator();
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/Regress68656641.java b/src/test/java/com/android/tools/r8/ir/regalloc/Regress68656641.java
index 2c1d228..e246335 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/Regress68656641.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/Regress68656641.java
@@ -57,7 +57,7 @@
   public void splitOverlappingInactiveIntervalWithNoNextUse() {
     InternalOptions options = new InternalOptions();
     AppInfo appInfo = new AppInfo(DexApplication.builder(options, null).build());
-    AppView<?> appView = AppView.createForD8(appInfo, options);
+    AppView<?> appView = AppView.createForD8(appInfo);
     IRCode code = simpleCode();
     MyRegisterAllocator allocator = new MyRegisterAllocator(appView, code);
     // Setup live an inactive live interval with ranges [0, 10[ and [20, 30[ with only
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index 26186aa..b3cbe0e 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -809,7 +809,7 @@
     options.intermediate = intermediate;
     DexItemFactory factory = options.itemFactory;
     AppInfo appInfo = new AppInfo(DexApplication.builder(options, timing).build());
-    AppView<?> appView = AppView.createForR8(appInfo, options);
+    AppView<?> appView = AppView.createForR8(appInfo);
     DexApplication.Builder<?> builder = DexApplication.builder(options, timing);
     for (String clazz : classes) {
       DexString desc = factory.createString(DescriptorUtils.javaTypeToDescriptor(clazz));
diff --git a/src/test/java/com/android/tools/r8/naming/MinifierTest.java b/src/test/java/com/android/tools/r8/naming/MinifierTest.java
index 1baffde..ebc3ad7 100644
--- a/src/test/java/com/android/tools/r8/naming/MinifierTest.java
+++ b/src/test/java/com/android/tools/r8/naming/MinifierTest.java
@@ -36,7 +36,7 @@
   @Test
   public void minifierTest() throws Exception {
     NamingLens naming = runMinifier(ListUtils.map(keepRulesFiles, Paths::get));
-    inspection.accept(dexItemFactory, naming);
+    inspection.accept(naming);
   }
 
   @Parameters(name = "test: {0} keep: {1}")
diff --git a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
index 385911c..e461666 100644
--- a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
+++ b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
@@ -3,83 +3,55 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.naming;
 
-import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.AppServices;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DirectMappedDexApplication;
-import com.android.tools.r8.graph.SubtypingInfo;
-import com.android.tools.r8.shaking.Enqueuer;
-import com.android.tools.r8.shaking.EnqueuerFactory;
-import com.android.tools.r8.shaking.ProguardConfiguration;
-import com.android.tools.r8.shaking.RootSetBuilder;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableList;
 import java.io.File;
-import java.io.IOException;
 import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 import java.util.function.BiConsumer;
-import org.junit.Before;
+import java.util.function.Consumer;
 
-public abstract class NamingTestBase {
+public abstract class NamingTestBase extends TestBase {
 
   private final String appFileName;
   protected final List<String> keepRulesFiles;
-  protected final BiConsumer<DexItemFactory, NamingLens> inspection;
+  protected final Consumer<NamingLens> inspection;
 
-  private final Timing timing;
-
-  private DirectMappedDexApplication program;
-  protected DexItemFactory dexItemFactory;
+  private DexItemFactory dexItemFactory = null;
 
   protected NamingTestBase(
       String test, List<String> keepRulesFiles, BiConsumer<DexItemFactory, NamingLens> inspection) {
     appFileName = ToolHelper.EXAMPLES_BUILD_DIR + test + "/classes.dex";
     this.keepRulesFiles = keepRulesFiles;
-    this.inspection = inspection;
-    this.timing = Timing.empty();
+    this.inspection = lens -> inspection.accept(dexItemFactory, lens);
   }
 
-  @Before
-  public void readApp() throws IOException, ExecutionException {
-    program = ToolHelper.buildApplication(ImmutableList.of(appFileName));
-    dexItemFactory = program.dexItemFactory;
-  }
-
-  protected NamingLens runMinifier(List<Path> configPaths) throws ExecutionException {
-    ProguardConfiguration configuration =
-        ToolHelper.loadProguardConfiguration(dexItemFactory, configPaths);
-
-    InternalOptions options = new InternalOptions(configuration, new Reporter());
-    options.programConsumer = DexIndexedConsumer.emptyConsumer();
-
-    ExecutorService executor = ThreadUtils.getExecutorService(1);
-
-    AppView<AppInfoWithClassHierarchy> appView =
-        AppView.createForR8(new AppInfoWithClassHierarchy(program), options);
-    SubtypingInfo subtypingInfo = new SubtypingInfo(program.allClasses(), program);
-    appView.setRootSet(
-        new RootSetBuilder(appView, subtypingInfo, configuration.getRules()).run(executor));
-    appView.setAppServices(AppServices.builder(appView).build());
-
-    Enqueuer enqueuer = EnqueuerFactory.createForInitialTreeShaking(appView, subtypingInfo);
-    appView.setAppInfo(
-        enqueuer.traceApplication(
-            appView.rootSet(), configuration.getDontWarnPatterns(), executor, timing));
-    return new Minifier(appView.withLiveness()).run(executor, timing);
+  protected NamingLens runMinifier(List<Path> configPaths) throws Exception {
+    AppView<AppInfoWithLiveness> appView =
+        computeAppViewWithLiveness(
+            AndroidApp.builder().addProgramFile(Paths.get(appFileName)).build(),
+            factory -> ToolHelper.loadProguardConfiguration(factory, configPaths));
+    dexItemFactory = appView.dexItemFactory();
+    ExecutorService executor = Executors.newSingleThreadExecutor();
+    try {
+      return new Minifier(appView).run(executor, Timing.empty());
+    } finally {
+      executor.shutdown();
+    }
   }
 
   protected static <T> Collection<Object[]> createTests(
diff --git a/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java b/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java
index 549b0e1..caa1ae9 100644
--- a/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java
@@ -44,7 +44,7 @@
   @Test
   public void packageNamingTest() throws Exception {
     NamingLens naming = runMinifier(ListUtils.map(keepRulesFiles, Paths::get));
-    inspection.accept(dexItemFactory, naming);
+    inspection.accept(naming);
   }
 
   private static final List<String> CLASSES_IN_NAMING044 = ImmutableList.of(
diff --git a/src/test/java/com/android/tools/r8/resolution/singletarget/InstantiatedLowerBoundTest.java b/src/test/java/com/android/tools/r8/resolution/singletarget/InstantiatedLowerBoundTest.java
index abd4956..4e19a05 100644
--- a/src/test/java/com/android/tools/r8/resolution/singletarget/InstantiatedLowerBoundTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/singletarget/InstantiatedLowerBoundTest.java
@@ -24,7 +24,6 @@
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.google.common.collect.Sets;
-import java.util.ArrayList;
 import java.util.Set;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -48,7 +47,8 @@
     AppView<AppInfoWithLiveness> appView =
         computeAppViewWithLiveness(
             buildClasses(A.class, B.class, Main.class).build(),
-            factory -> new ArrayList<>(buildKeepRuleForClassAndMethods(Main.class, factory)));
+            factory ->
+                buildConfigForRules(factory, buildKeepRuleForClassAndMethods(Main.class, factory)));
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexType typeA = buildType(A.class, appInfo.dexItemFactory());
     DexType typeB = buildType(B.class, appInfo.dexItemFactory());
@@ -72,7 +72,8 @@
     AppView<AppInfoWithLiveness> appView =
         computeAppViewWithLiveness(
             buildClasses(A.class, B.class, C.class, Main.class).build(),
-            factory -> new ArrayList<>(buildKeepRuleForClassAndMethods(Main.class, factory)));
+            factory ->
+                buildConfigForRules(factory, buildKeepRuleForClassAndMethods(Main.class, factory)));
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexType typeA = buildType(A.class, appInfo.dexItemFactory());
     DexType typeB = buildType(B.class, appInfo.dexItemFactory());
@@ -97,8 +98,8 @@
         computeAppViewWithLiveness(
             buildClasses(A.class, B.class, C.class, MainAllInstantiated.class).build(),
             factory ->
-                new ArrayList<>(
-                    buildKeepRuleForClassAndMethods(MainAllInstantiated.class, factory)));
+                buildConfigForRules(
+                    factory, buildKeepRuleForClassAndMethods(MainAllInstantiated.class, factory)));
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexType typeA = buildType(A.class, appInfo.dexItemFactory());
     DexType typeC = buildType(C.class, appInfo.dexItemFactory());
diff --git a/src/test/java/com/android/tools/r8/resolution/singletarget/SuccessAndInvalidLookupTest.java b/src/test/java/com/android/tools/r8/resolution/singletarget/SuccessAndInvalidLookupTest.java
index 9489060..593670d 100644
--- a/src/test/java/com/android/tools/r8/resolution/singletarget/SuccessAndInvalidLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/singletarget/SuccessAndInvalidLookupTest.java
@@ -17,7 +17,6 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import java.util.ArrayList;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -40,7 +39,8 @@
     AppView<AppInfoWithLiveness> appView =
         computeAppViewWithLiveness(
             buildClasses(I.class, A.class, Main.class).build(),
-            factory -> new ArrayList<>(buildKeepRuleForClassAndMethods(Main.class, factory)));
+            factory ->
+                buildConfigForRules(factory, buildKeepRuleForClassAndMethods(Main.class, factory)));
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexType typeMain = buildType(Main.class, appInfo.dexItemFactory());
     DexMethod mainMethodReference =
@@ -63,7 +63,8 @@
     AppView<AppInfoWithLiveness> appView =
         computeAppViewWithLiveness(
             buildClasses(I.class, A.class, Main.class).build(),
-            factory -> new ArrayList<>(buildKeepRuleForClassAndMethods(Main.class, factory)));
+            factory ->
+                buildConfigForRules(factory, buildKeepRuleForClassAndMethods(Main.class, factory)));
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexType typeMain = buildType(Main.class, appInfo.dexItemFactory());
     DexMethod mainMethodReference =
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java
index 745ff4b..4332d86 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java
@@ -82,7 +82,7 @@
               rules.addAll(buildKeepRuleForClassAndMethods(methodToBeKept, factory));
               rules.addAll(buildKeepRuleForClass(classToBeKept, factory));
               rules.addAll(buildKeepRuleForClassAndMethods(main, factory));
-              return rules;
+              return buildConfigForRules(factory, rules);
             });
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexMethod method = buildNullaryVoidMethod(initial, "foo", appInfo.dexItemFactory());
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
index e8494d6..ec3fcc1 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
@@ -15,12 +15,14 @@
 import com.android.tools.r8.retrace.stacktraces.ActualRetraceBotStackTraceWithInfo;
 import com.android.tools.r8.retrace.stacktraces.FoundMethodVerboseStackTrace;
 import com.android.tools.r8.utils.StringUtils;
+import com.google.common.base.Charsets;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
@@ -38,6 +40,8 @@
 
   @Rule public TemporaryFolder folder = new TemporaryFolder();
 
+  private static String SMILEY_EMOJI = "\uD83D\uDE00";
+
   @Test
   public void testPrintIdentityStackTraceFile() throws IOException {
     runTest("", nonMappableStackTrace, false, nonMappableStackTrace);
@@ -125,6 +129,16 @@
     assertEquals(Retrace.USAGE_MESSAGE, processResult.stdout);
   }
 
+  @Test
+  public void testNonAscii() throws IOException {
+    runTest("", SMILEY_EMOJI, false, SMILEY_EMOJI + StringUtils.LINE_SEPARATOR);
+  }
+
+  @Test
+  public void testNonAsciiStdIn() throws IOException {
+    runTest("", SMILEY_EMOJI, true, SMILEY_EMOJI + StringUtils.LINE_SEPARATOR);
+  }
+
   private final String nonMappableStackTrace =
       StringUtils.lines(
           "com.android.r8.R8Exception: Problem when compiling program",
@@ -164,7 +178,7 @@
     Path mappingFile = folder.newFile("mapping.txt").toPath();
     Files.write(mappingFile, mapping.getBytes());
     File stackTraceFile = folder.newFile("stacktrace.txt");
-    Files.write(stackTraceFile.toPath(), stackTrace.getBytes());
+    Files.write(stackTraceFile.toPath(), stackTrace.getBytes(StandardCharsets.UTF_8));
 
     Collection<String> args = new ArrayList<>();
     args.add(mappingFile.toString());
@@ -216,8 +230,8 @@
       System.setErr(originalErr);
       return new ProcessResult(
           exitCode,
-          outputByteStream.toString(),
-          errorByteStream.toString(),
+          outputByteStream.toString(Charsets.UTF_8.name()),
+          errorByteStream.toString(Charsets.UTF_8.name()),
           StringUtils.joinLines(args));
     }
   }
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java
index bb69fc3..d8768a2 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.retrace;
 
+import static com.android.tools.r8.retrace.Retrace.DEFAULT_REGULAR_EXPRESSION;
 import static junit.framework.TestCase.assertEquals;
 
 import com.android.tools.r8.TestBase;
@@ -11,6 +12,7 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.retrace.stacktraces.InlineFileNameStackTrace;
+import com.android.tools.r8.retrace.stacktraces.RetraceAssertionErrorStackTrace;
 import com.android.tools.r8.retrace.stacktraces.StackTraceForTest;
 import com.android.tools.r8.utils.StringUtils;
 import com.google.common.collect.ImmutableList;
@@ -24,9 +26,6 @@
 @RunWith(Parameterized.class)
 public class RetraceRegularExpressionTests extends TestBase {
 
-  private static final String DEFAULT_REGULAR_EXPRESSION =
-      "(?:.*?\\bat\\s+%c\\.%m\\s*\\(%s(?::%l)?\\)\\s*(?:~\\[.*\\])?)|(?:(?:.*?[:\"]\\s+)?%c(?::.*)?)";
-
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
     return getTestParameters().withNoneRuntime().build();
@@ -731,6 +730,41 @@
         });
   }
 
+  @Test
+  public void testSourceFileLineNumber() {
+    runRetraceTest(
+        DEFAULT_REGULAR_EXPRESSION.replace("%s(?::%l)?", "%S"),
+        new RetraceAssertionErrorStackTrace());
+  }
+
+  @Test
+  public void testEscaping() {
+    runRetraceTest(
+        "\\%c\\\\%c\\\\\\%c.%m\\(\\\\%S\\)\\\\\\%S",
+        new StackTraceForTest() {
+          @Override
+          public List<String> obfuscatedStackTrace() {
+            return ImmutableList.of("%c\\com.android.tools.r8.Foo\\%c.a(\\SourceFile:1)\\%S");
+          }
+
+          @Override
+          public String mapping() {
+            return "com.android.tools.r8.Bar -> com.android.tools.r8.Foo:\n"
+                + "  1:1:void m():13:13 -> a";
+          }
+
+          @Override
+          public List<String> retracedStackTrace() {
+            return ImmutableList.of("%c\\com.android.tools.r8.Bar\\%c.m(\\Bar.java:13)\\%S");
+          }
+
+          @Override
+          public int expectedWarnings() {
+            return 0;
+          }
+        });
+  }
+
   private TestDiagnosticMessagesImpl runRetraceTest(
       String regularExpression, StackTraceForTest stackTraceForTest) {
     TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
diff --git a/src/test/java/com/android/tools/r8/shaking/LibraryClassExtendingProgramClassSuperTest.java b/src/test/java/com/android/tools/r8/shaking/LibraryClassExtendingProgramClassSuperTest.java
new file mode 100644
index 0000000..584d3ae
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/LibraryClassExtendingProgramClassSuperTest.java
@@ -0,0 +1,139 @@
+// Copyright (c) 2020, 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;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThrows;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.R8TestBuilder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class LibraryClassExtendingProgramClassSuperTest extends TestBase {
+
+  private final TestParameters parameters;
+  private final boolean proguardCompatibility;
+  private final boolean dontWarn;
+  private final String EXPECTED = ProgramIndirectSuper.class.getTypeName();
+
+  @Parameters(name = "{0}, proguardcompatiblity: {1}, dontwarn: {2}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimesAndApiLevels().build(),
+        BooleanUtils.values(),
+        BooleanUtils.values());
+  }
+
+  public LibraryClassExtendingProgramClassSuperTest(
+      TestParameters parameters, boolean proguardCompatibility, boolean dontWarn) {
+    this.parameters = parameters;
+    this.proguardCompatibility = proguardCompatibility;
+    this.dontWarn = dontWarn;
+  }
+
+  @Test
+  public void testRuntime() throws Exception {
+    testForRuntime(parameters)
+        .addProgramClasses(Main.class, ProgramDirectSuper.class, ProgramIndirectSuper.class)
+        .addRunClasspathFiles(
+            compileToZip(
+                parameters,
+                ImmutableList.of(ProgramDirectSuper.class, ProgramIndirectSuper.class),
+                LibraryClass.class))
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    R8TestBuilder<? extends R8TestBuilder<?>> r8TestBuilder =
+        (proguardCompatibility
+                ? testForR8Compat(parameters.getBackend(), true)
+                : testForR8(parameters.getBackend()))
+            .addLibraryFiles(
+                parameters.isCfRuntime()
+                    ? ToolHelper.getJava8RuntimeJar()
+                    : ToolHelper.getAndroidJar(parameters.getApiLevel()))
+            .addLibraryClasses(LibraryClass.class)
+            .addProgramClasses(ProgramDirectSuper.class, ProgramIndirectSuper.class, Main.class)
+            .addKeepMainRule(Main.class)
+            .applyIf(dontWarn, b -> b.addKeepRules("-dontwarn " + LibraryClass.class.getTypeName()))
+            .setMinApi(parameters.getApiLevel())
+            .addRunClasspathFiles(
+                compileToZip(
+                    parameters,
+                    ImmutableList.of(ProgramIndirectSuper.class, ProgramDirectSuper.class),
+                    LibraryClass.class));
+    if (proguardCompatibility && dontWarn) {
+      r8TestBuilder.run(parameters.getRuntime(), Main.class).assertSuccessWithOutputLines(EXPECTED);
+    } else if (proguardCompatibility) {
+      r8TestBuilder
+          .allowDiagnosticWarningMessages()
+          .compile()
+          .assertAllWarningMessagesMatch(
+              containsString(
+                  LibraryClass.class.getTypeName()
+                      + " extends program class "
+                      + ProgramDirectSuper.class.getTypeName()))
+          .run(parameters.getRuntime(), Main.class)
+          .assertSuccessWithOutputLines(ProgramIndirectSuper.class.getTypeName());
+    } else if (dontWarn) {
+      // TODO(b/159609181): This fails with an assertion error, should we not handle it the same as
+      //   proguardcompat?
+      assertThrows(
+          CompilationFailedException.class,
+          () ->
+              r8TestBuilder.compileWithExpectedDiagnostics(
+                  diagnostics ->
+                      diagnostics.assertErrorsMatch(
+                          diagnosticMessage(
+                              containsString(
+                                  "Failed lookup of non-missing type: "
+                                      + ProgramDirectSuper.class.getTypeName())))));
+    } else {
+      assertThrows(
+          CompilationFailedException.class,
+          () ->
+              r8TestBuilder.compileWithExpectedDiagnostics(
+                  diagnostics -> {
+                    diagnostics.assertErrorsMatch(
+                        diagnosticMessage(
+                            containsString(
+                                LibraryClass.class.getTypeName()
+                                    + " extends program class "
+                                    + ProgramDirectSuper.class.getTypeName())));
+                  }));
+    }
+  }
+
+  public static class ProgramIndirectSuper {}
+
+  public static class ProgramDirectSuper extends ProgramIndirectSuper {}
+
+  public static class LibraryClass extends ProgramDirectSuper {
+
+    void foo() {
+      System.out.println(this.getClass().getSuperclass().getSuperclass().getName());
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      new LibraryClass().foo();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/keepclassmembers/KeepInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/shaking/keepclassmembers/KeepInterfaceMethodTest.java
index 550957e..e6baeae 100644
--- a/src/test/java/com/android/tools/r8/shaking/keepclassmembers/KeepInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/keepclassmembers/KeepInterfaceMethodTest.java
@@ -40,6 +40,7 @@
   @Test
   public void testIProguard() throws CompilationFailedException, IOException, ExecutionException {
     testForProguard()
+        // TODO(b/159694276): Run the resulting code on runtime.
         .addProgramClasses(I.class)
         .addKeepRules(
             "-keepclassmembers class " + I.class.getTypeName() + " { void foo(); }",
@@ -51,6 +52,7 @@
 
   @Test
   public void testIR8() throws CompilationFailedException, IOException, ExecutionException {
+    // TODO(b/159694276): Add compat variant of this.
     testForR8(parameters.getBackend())
         .addProgramClasses(I.class)
         .addKeepRules("-keepclassmembers class " + I.class.getTypeName() + " { void foo(); }")
@@ -75,6 +77,7 @@
 
   @Test
   public void testAR8() throws CompilationFailedException, IOException, ExecutionException {
+    // TODO(b/159694276): Add non-compat variant of this.
     testForR8Compat(parameters.getBackend())
         .addProgramClasses(I.class, A.class, B.class, Main.class)
         .addKeepRules("-keepclassmembers class " + I.class.getTypeName() + " { void foo(); }")
diff --git a/src/test/java/com/android/tools/r8/smali/CatchSuccessorFallthroughTest.java b/src/test/java/com/android/tools/r8/smali/CatchSuccessorFallthroughTest.java
index 17cce81..f4884c3 100644
--- a/src/test/java/com/android/tools/r8/smali/CatchSuccessorFallthroughTest.java
+++ b/src/test/java/com/android/tools/r8/smali/CatchSuccessorFallthroughTest.java
@@ -79,7 +79,7 @@
 
     ProgramMethod method = getProgramMethod(originalApplication, methodSig);
     // Get the IR pre-optimization.
-    IRCode code = method.buildIR(AppView.createForD8(new AppInfo(application), options));
+    IRCode code = method.buildIR(AppView.createForD8(new AppInfo(application)));
 
     // Find the exit block and assert that the value is a phi merging the exceptional edge
     // with the normal edge.
diff --git a/src/test/java/com/android/tools/r8/utils/SemanticVersionTests.java b/src/test/java/com/android/tools/r8/utils/SemanticVersionTests.java
new file mode 100644
index 0000000..8f0dd61
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/SemanticVersionTests.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import static com.android.tools.r8.utils.SemanticVersion.parse;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.NoneRuntime;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class SemanticVersionTests extends TestBase {
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public SemanticVersionTests(TestParameters parameters) {
+    assertEquals(NoneRuntime.getInstance(), parameters.getRuntime());
+  }
+
+  @Test
+  public void test() throws Exception {
+    assertTrue(parse("1.0.1").isNewerOrEqual(parse("1.0.0")));
+    assertFalse(parse("1.0.1").isNewerOrEqual(parse("1.1.0")));
+    assertTrue(parse("1.1.0").isNewerOrEqual(parse("1.0.1")));
+    assertFalse(parse("1.1.1").isNewerOrEqual(parse("2.0.0")));
+
+    assertTrue(parse("2.0.0").isNewerOrEqual(parse("1.1.1")));
+    assertTrue(parse("42.42.42").isNewerOrEqual(parse("9.9.9")));
+    assertTrue(parse("9.42.42").isNewerOrEqual(parse("9.9.9")));
+    assertTrue(parse("9.9.42").isNewerOrEqual(parse("9.9.9")));
+
+    assertFalse(parse("1.1.1").isNewerOrEqual(parse("2.0.0")));
+    assertFalse(parse("9.9.9").isNewerOrEqual(parse("42.42.42")));
+    assertFalse(parse("9.9.9").isNewerOrEqual(parse("9.42.42")));
+    assertFalse(parse("9.9.9").isNewerOrEqual(parse("9.9.42")));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
index ed7ac4d..38dc236 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
@@ -10,12 +10,11 @@
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.naming.MemberNaming.Signature;
-import com.android.tools.r8.utils.InternalOptions;
 
 public class AbsentMethodSubject extends MethodSubject {
 
   @Override
-  public IRCode buildIR(InternalOptions options) {
+  public IRCode buildIR() {
     throw new Unreachable("Cannot build IR for an absent method");
   }
 
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 f43c01c..76ed0e2 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
@@ -5,6 +5,7 @@
 
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.DexIndexedConsumer;
 import com.android.tools.r8.StringResource;
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.cf.code.CfTryCatch;
@@ -126,6 +127,10 @@
     if (optionsConsumer != null) {
       optionsConsumer.accept(internalOptions);
     }
+    if (internalOptions.programConsumer == null) {
+      // The inspector allows building IR for a method. An output type must be defined for that.
+      internalOptions.programConsumer = DexIndexedConsumer.emptyConsumer();
+    }
     return internalOptions;
   }
 
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
index 0e504ca..1f1fe84 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.utils.codeinspector;
 
-import com.android.tools.r8.DexIndexedConsumer;
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.cf.code.CfPosition;
 import com.android.tools.r8.code.Instruction;
@@ -32,7 +31,6 @@
 import com.android.tools.r8.naming.signature.GenericSignatureParser;
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.LocalVariableTable.LocalVariableTableEntry;
 import com.google.common.base.Predicates;
@@ -58,10 +56,9 @@
   }
 
   @Override
-  public IRCode buildIR(InternalOptions options) {
-    options.programConsumer = DexIndexedConsumer.emptyConsumer();
-    return getProgramMethod()
-        .buildIR(AppView.createForD8(new AppInfo(codeInspector.application), options));
+  public IRCode buildIR() {
+    assert codeInspector.application.options.programConsumer != null;
+    return getProgramMethod().buildIR(AppView.createForD8(new AppInfo(codeInspector.application)));
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
index 72b71b0..8fc439f 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
@@ -5,12 +5,9 @@
 package com.android.tools.r8.utils.codeinspector;
 
 import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.Reporter;
 import com.google.common.collect.Streams;
 import java.util.Iterator;
 import java.util.function.Predicate;
@@ -18,15 +15,7 @@
 
 public abstract class MethodSubject extends MemberSubject {
 
-  public final IRCode buildIR() {
-    return buildIR(new DexItemFactory());
-  }
-
-  public IRCode buildIR(DexItemFactory dexItemFactory) {
-    return buildIR(new InternalOptions(dexItemFactory, new Reporter()));
-  }
-
-  public abstract IRCode buildIR(InternalOptions options);
+  public abstract IRCode buildIR();
 
   public final boolean isAbsent() {
     return !isPresent();