Merge commit 'fa84c4d76daf331833bad01f14133cb4a6484425' into dev-release
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 52735a5..abf8844 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -311,6 +311,7 @@
 
       appView.setArtProfileCollection(
           appView.getArtProfileCollection().withoutMissingItems(appView));
+      assert appView.getStartupProfile().isEmpty();
 
       finalizeApplication(appView, executor, timing);
 
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
index 3c17174..14677cc 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -15,7 +15,7 @@
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.SubtypingInfo;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ProfileCollectionAdditions;
 import com.android.tools.r8.shaking.Enqueuer;
 import com.android.tools.r8.shaking.EnqueuerFactory;
 import com.android.tools.r8.shaking.MainDexInfo;
@@ -66,11 +66,10 @@
 
     SubtypingInfo subtypingInfo = SubtypingInfo.create(appView);
 
-    ArtProfileCollectionAdditions artProfileCollectionAdditions =
-        ArtProfileCollectionAdditions.nop();
+    ProfileCollectionAdditions profileCollectionAdditions = ProfileCollectionAdditions.nop();
     MainDexRootSet mainDexRootSet =
         MainDexRootSet.builder(
-                appView, artProfileCollectionAdditions, subtypingInfo, options.mainDexKeepRules)
+                appView, profileCollectionAdditions, subtypingInfo, options.mainDexKeepRules)
             .build(executor);
     appView.setMainDexRootSet(mainDexRootSet);
 
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 9e8e260..41ea658 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -73,7 +73,7 @@
 import com.android.tools.r8.optimize.proto.ProtoNormalizer;
 import com.android.tools.r8.origin.CommandLineOrigin;
 import com.android.tools.r8.profile.art.ArtProfileCompletenessChecker;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ProfileCollectionAdditions;
 import com.android.tools.r8.repackaging.Repackaging;
 import com.android.tools.r8.repackaging.RepackagingLens;
 import com.android.tools.r8.shaking.AbstractMethodRemover;
@@ -331,14 +331,14 @@
                     options.itemFactory, options.getMinApiLevel()));
           }
         }
-        ArtProfileCollectionAdditions artProfileCollectionAdditions =
-            ArtProfileCollectionAdditions.create(appView);
+        ProfileCollectionAdditions profileCollectionAdditions =
+            ProfileCollectionAdditions.create(appView);
         AssumeInfoCollection.Builder assumeInfoCollectionBuilder = AssumeInfoCollection.builder();
         SubtypingInfo subtypingInfo = SubtypingInfo.create(appView);
         appView.setRootSet(
             RootSet.builder(
                     appView,
-                    artProfileCollectionAdditions,
+                    profileCollectionAdditions,
                     subtypingInfo,
                     Iterables.concat(
                         options.getProguardConfiguration().getRules(), synthesizedProguardRules))
@@ -353,10 +353,7 @@
           // Find classes which may have code executed before secondary dex files installation.
           MainDexRootSet mainDexRootSet =
               MainDexRootSet.builder(
-                      appView,
-                      artProfileCollectionAdditions,
-                      subtypingInfo,
-                      options.mainDexKeepRules)
+                      appView, profileCollectionAdditions, subtypingInfo, options.mainDexKeepRules)
                   .build(executorService);
           appView.setMainDexRootSet(mainDexRootSet);
           appView.appInfo().unsetObsolete();
@@ -371,7 +368,7 @@
                 annotationRemoverBuilder,
                 executorService,
                 appView,
-                artProfileCollectionAdditions,
+                profileCollectionAdditions,
                 subtypingInfo,
                 classMergingEnqueuerExtensionBuilder);
         timing.end();
@@ -702,6 +699,7 @@
 
       appView.setArtProfileCollection(
           appView.getArtProfileCollection().withoutMissingItems(appView));
+      appView.setStartupProfile(appView.getStartupProfile().withoutMissingItems(appView));
 
       if (appView.appInfo().hasLiveness()) {
         SyntheticFinalization.finalizeWithLiveness(appView.withLiveness(), executorService, timing);
@@ -970,14 +968,14 @@
       AnnotationRemover.Builder annotationRemoverBuilder,
       ExecutorService executorService,
       AppView<AppInfoWithClassHierarchy> appView,
-      ArtProfileCollectionAdditions artProfileCollectionAdditions,
+      ProfileCollectionAdditions profileCollectionAdditions,
       SubtypingInfo subtypingInfo,
       RuntimeTypeCheckInfo.Builder classMergingEnqueuerExtensionBuilder)
       throws ExecutionException {
     timing.begin("Set up enqueuer");
     Enqueuer enqueuer =
         EnqueuerFactory.createForInitialTreeShaking(
-            appView, artProfileCollectionAdditions, executorService, subtypingInfo);
+            appView, profileCollectionAdditions, executorService, subtypingInfo);
     enqueuer.setAnnotationRemoverBuilder(annotationRemoverBuilder);
     if (appView.options().enableInitializedClassesInInstanceMethodsAnalysis) {
       enqueuer.registerAnalysis(new InitializedClassesInInstanceMethodsAnalysis(appView));
@@ -997,7 +995,7 @@
     timing.begin("Trace application");
     EnqueuerResult enqueuerResult =
         enqueuer.traceApplication(appView.rootSet(), executorService, timing);
-    assert artProfileCollectionAdditions.verifyIsCommitted();
+    assert profileCollectionAdditions.verifyIsCommitted();
     timing.end();
     timing.begin("Finalize enqueuer result");
     AppView<AppInfoWithLiveness> appViewWithLiveness =
diff --git a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
index c1af115..261497d 100644
--- a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
+++ b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
@@ -23,6 +23,7 @@
 import com.android.tools.r8.graph.ThrowExceptionCode;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.synthesis.CommittedItems;
+import com.android.tools.r8.synthesis.SyntheticItems;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.WorkList;
 import com.google.common.collect.Sets;
@@ -89,10 +90,15 @@
     eventConsumer.finished(appView);
   }
 
+  private boolean isAlreadyOutlined(DexProgramClass clazz) {
+    SyntheticItems syntheticItems = appView.getSyntheticItems();
+    return syntheticItems.isSyntheticOfKind(clazz.getType(), kinds -> kinds.API_MODEL_OUTLINE)
+        || syntheticItems.isSyntheticOfKind(
+            clazz.getType(), kinds -> kinds.API_MODEL_OUTLINE_WITHOUT_GLOBAL_MERGING);
+  }
+
   public void processClass(DexProgramClass clazz) {
-    if (appView
-        .getSyntheticItems()
-        .isSyntheticOfKind(clazz.getType(), kinds -> kinds.API_MODEL_OUTLINE)) {
+    if (isAlreadyOutlined(clazz)) {
       return;
     }
     // We cannot reliably create a stub that will have the same throwing behavior for all VMs.
diff --git a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubberEventConsumer.java b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubberEventConsumer.java
index 7c42536..47b5efb 100644
--- a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubberEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubberEventConsumer.java
@@ -7,7 +7,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexLibraryClass;
 import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingApiReferenceStubberEventConsumer;
+import com.android.tools.r8.profile.art.rewriting.ProfileRewritingApiReferenceStubberEventConsumer;
 
 public interface ApiReferenceStubberEventConsumer {
 
@@ -21,7 +21,7 @@
   boolean isEmpty();
 
   static ApiReferenceStubberEventConsumer create(AppView<?> appView) {
-    return ArtProfileRewritingApiReferenceStubberEventConsumer.attach(appView, empty());
+    return ProfileRewritingApiReferenceStubberEventConsumer.attach(appView, empty());
   }
 
   static EmptyApiReferenceStubberEventConsumer empty() {
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 99cb304..08b14b8 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -25,7 +25,7 @@
 import com.android.tools.r8.dex.VirtualFile.ItemUseInfo;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.experimental.startup.StartupCompleteness;
-import com.android.tools.r8.experimental.startup.StartupOrder;
+import com.android.tools.r8.experimental.startup.StartupProfile;
 import com.android.tools.r8.features.FeatureSplitConfiguration.DataResourceProvidersAndConsumer;
 import com.android.tools.r8.graph.AppServices;
 import com.android.tools.r8.graph.AppView;
@@ -241,13 +241,13 @@
       // Retrieve the startup order for writing the app. In R8, the startup order is created
       // up-front to guide optimizations through-out the compilation. In D8, the startup
       // order is only used for writing the app, so we create it here for the first time.
-      StartupOrder startupOrder =
+      StartupProfile startupProfile =
           appView.appInfo().hasClassHierarchy()
-              ? appView.appInfoWithClassHierarchy().getStartupOrder()
-              : StartupOrder.createInitialStartupOrderForD8(appView);
+              ? appView.getStartupProfile()
+              : StartupProfile.createInitialStartupProfileForD8(appView);
       distributor =
           new VirtualFile.FillFilesDistributor(
-              this, classes, options, executorService, startupOrder);
+              this, classes, options, executorService, startupProfile);
     }
 
     List<VirtualFile> virtualFiles = distributor.run();
diff --git a/src/main/java/com/android/tools/r8/dex/MixedSectionLayoutStrategy.java b/src/main/java/com/android/tools/r8/dex/MixedSectionLayoutStrategy.java
index 13a4a2c..60055c9 100644
--- a/src/main/java/com/android/tools/r8/dex/MixedSectionLayoutStrategy.java
+++ b/src/main/java/com/android/tools/r8/dex/MixedSectionLayoutStrategy.java
@@ -5,7 +5,7 @@
 package com.android.tools.r8.dex;
 
 import com.android.tools.r8.dex.FileWriter.MixedSectionOffsets;
-import com.android.tools.r8.experimental.startup.StartupOrder;
+import com.android.tools.r8.experimental.startup.StartupProfile;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationDirectory;
@@ -22,21 +22,21 @@
 
   public static MixedSectionLayoutStrategy create(
       AppView<?> appView, MixedSectionOffsets mixedSectionOffsets, VirtualFile virtualFile) {
-    StartupOrder startupOrderForWriting;
-    if (virtualFile.getStartupOrder().isEmpty()) {
-      startupOrderForWriting = StartupOrder.empty();
+    StartupProfile startupProfileForWriting;
+    if (virtualFile.getStartupProfile().isEmpty()) {
+      startupProfileForWriting = StartupProfile.empty();
     } else {
       assert virtualFile.getId() == 0;
-      startupOrderForWriting =
+      startupProfileForWriting =
           appView.options().getStartupOptions().isStartupLayoutOptimizationsEnabled()
-              ? virtualFile.getStartupOrder().toStartupOrderForWriting(appView)
-              : StartupOrder.empty();
+              ? virtualFile.getStartupProfile().toStartupProfileForWriting(appView)
+              : StartupProfile.empty();
     }
     MixedSectionLayoutStrategy mixedSectionLayoutStrategy =
-        startupOrderForWriting.isEmpty()
+        startupProfileForWriting.isEmpty()
             ? new DefaultMixedSectionLayoutStrategy(appView, mixedSectionOffsets)
             : new StartupMixedSectionLayoutStrategy(
-                appView, mixedSectionOffsets, startupOrderForWriting, virtualFile);
+                appView, mixedSectionOffsets, startupProfileForWriting, virtualFile);
     return wrapForTesting(appView, mixedSectionLayoutStrategy, virtualFile);
   }
 
diff --git a/src/main/java/com/android/tools/r8/dex/StartupMixedSectionLayoutStrategy.java b/src/main/java/com/android/tools/r8/dex/StartupMixedSectionLayoutStrategy.java
index 0833358..e848e3b 100644
--- a/src/main/java/com/android/tools/r8/dex/StartupMixedSectionLayoutStrategy.java
+++ b/src/main/java/com/android/tools/r8/dex/StartupMixedSectionLayoutStrategy.java
@@ -5,10 +5,10 @@
 package com.android.tools.r8.dex;
 
 import com.android.tools.r8.dex.FileWriter.MixedSectionOffsets;
-import com.android.tools.r8.experimental.startup.StartupOrder;
-import com.android.tools.r8.experimental.startup.profile.StartupClass;
-import com.android.tools.r8.experimental.startup.profile.StartupItem;
-import com.android.tools.r8.experimental.startup.profile.StartupMethod;
+import com.android.tools.r8.experimental.startup.StartupProfile;
+import com.android.tools.r8.experimental.startup.profile.StartupProfileClassRule;
+import com.android.tools.r8.experimental.startup.profile.StartupProfileMethodRule;
+import com.android.tools.r8.experimental.startup.profile.StartupProfileRule;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationDirectory;
@@ -36,7 +36,7 @@
 
 public class StartupMixedSectionLayoutStrategy extends DefaultMixedSectionLayoutStrategy {
 
-  private final StartupOrder startupOrderForWriting;
+  private final StartupProfile startupProfileForWriting;
 
   private final LinkedHashSet<DexAnnotation> annotationLayout;
   private final LinkedHashSet<DexAnnotationDirectory> annotationDirectoryLayout;
@@ -51,10 +51,10 @@
   public StartupMixedSectionLayoutStrategy(
       AppView<?> appView,
       MixedSectionOffsets mixedSectionOffsets,
-      StartupOrder startupOrderForWriting,
+      StartupProfile startupProfileForWriting,
       VirtualFile virtualFile) {
     super(appView, mixedSectionOffsets);
-    this.startupOrderForWriting = startupOrderForWriting;
+    this.startupProfileForWriting = startupProfileForWriting;
 
     // Initialize startup layouts.
     this.annotationLayout = new LinkedHashSet<>(mixedSectionOffsets.getAnnotations().size());
@@ -82,23 +82,18 @@
             virtualFile.classes().size());
     LensCodeRewriterUtils rewriter = new LensCodeRewriterUtils(appView, true);
     StartupIndexedItemCollection indexedItemCollection = new StartupIndexedItemCollection();
-    for (StartupItem startupItem : startupOrderForWriting.getItems()) {
+    for (StartupProfileRule startupItem : startupProfileForWriting.getRules()) {
       startupItem.accept(
           startupClass ->
               collectStartupItems(startupClass, indexedItemCollection, virtualFileDefinitions),
           startupMethod ->
               collectStartupItems(
-                  startupMethod, indexedItemCollection, virtualFileDefinitions, rewriter),
-          syntheticStartupMethod -> {
-            // All synthetic startup items should be removed after calling
-            // StartupOrder#toStartupOrderForWriting.
-            assert false;
-          });
+                  startupMethod, indexedItemCollection, virtualFileDefinitions, rewriter));
     }
   }
 
   private void collectStartupItems(
-      StartupClass startupClass,
+      StartupProfileClassRule startupClass,
       StartupIndexedItemCollection indexedItemCollection,
       Map<DexType, DexProgramClass> virtualFileDefinitions) {
     DexProgramClass definition = virtualFileDefinitions.get(startupClass.getReference());
@@ -121,7 +116,7 @@
   }
 
   private void collectStartupItems(
-      StartupMethod startupMethod,
+      StartupProfileMethodRule startupMethod,
       StartupIndexedItemCollection indexedItemCollection,
       Map<DexType, DexProgramClass> virtualFileDefinitions,
       LensCodeRewriterUtils rewriter) {
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index 692192d..270fcf2 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -9,7 +9,7 @@
 import com.android.tools.r8.debuginfo.DebugRepresentation;
 import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
 import com.android.tools.r8.errors.InternalCompilerError;
-import com.android.tools.r8.experimental.startup.StartupOrder;
+import com.android.tools.r8.experimental.startup.StartupProfile;
 import com.android.tools.r8.features.ClassToFeatureSplitMap;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
@@ -30,7 +30,6 @@
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.shaking.MainDexInfo;
-import com.android.tools.r8.synthesis.SyntheticItems;
 import com.android.tools.r8.synthesis.SyntheticNaming;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.FileUtils;
@@ -69,28 +68,28 @@
   public final VirtualFileIndexedItemCollection indexedItems;
   private final IndexedItemTransaction transaction;
   private final FeatureSplit featureSplit;
-  private final StartupOrder startupOrder;
+  private final StartupProfile startupProfile;
 
   private final DexString primaryClassDescriptor;
   private final DexString primaryClassSynthesizingContextDescriptor;
   private DebugRepresentation debugRepresentation;
 
   VirtualFile(int id, AppView<?> appView) {
-    this(id, appView, null, null, StartupOrder.empty());
+    this(id, appView, null, null, StartupProfile.empty());
   }
 
   VirtualFile(
       int id,
       AppView<?> appView,
       FeatureSplit featureSplit) {
-    this(id, appView, null, featureSplit, StartupOrder.empty());
+    this(id, appView, null, featureSplit, StartupProfile.empty());
   }
 
   private VirtualFile(
       int id,
       AppView<?> appView,
       DexProgramClass primaryClass) {
-    this(id, appView, primaryClass, null, StartupOrder.empty());
+    this(id, appView, primaryClass, null, StartupProfile.empty());
   }
 
   private VirtualFile(
@@ -98,12 +97,12 @@
       AppView<?> appView,
       DexProgramClass primaryClass,
       FeatureSplit featureSplit,
-      StartupOrder startupOrder) {
+      StartupProfile startupProfile) {
     this.id = id;
     this.indexedItems = new VirtualFileIndexedItemCollection(appView);
     this.transaction = new IndexedItemTransaction(indexedItems, appView);
     this.featureSplit = featureSplit;
-    this.startupOrder = startupOrder;
+    this.startupProfile = startupProfile;
     if (primaryClass == null) {
       primaryClassDescriptor = null;
       primaryClassSynthesizingContextDescriptor = null;
@@ -138,8 +137,8 @@
     return featureSplit;
   }
 
-  public StartupOrder getStartupOrder() {
-    return startupOrder;
+  public StartupProfile getStartupProfile() {
+    return startupProfile;
   }
 
   public String getPrimaryClassDescriptor() {
@@ -380,14 +379,14 @@
         ApplicationWriter writer,
         Collection<DexProgramClass> classes,
         InternalOptions options,
-        StartupOrder startupOrder) {
+        StartupProfile startupProfile) {
       super(writer);
       this.options = options;
       this.classes = SetUtils.newIdentityHashSet(classes);
 
       // Create the primary dex file. The distribution will add more if needed. We use the startup
       // order (if any) to guide the layout of the primary dex file.
-      mainDexFile = new VirtualFile(0, appView, null, null, startupOrder);
+      mainDexFile = new VirtualFile(0, appView, null, null, startupProfile);
       assert virtualFiles.isEmpty();
       virtualFiles.add(mainDexFile);
       addMarkers(mainDexFile);
@@ -450,7 +449,8 @@
     }
 
     protected void addFeatureSplitFiles(
-        Map<FeatureSplit, Set<DexProgramClass>> featureSplitClasses, StartupOrder startupOrder) {
+        Map<FeatureSplit, Set<DexProgramClass>> featureSplitClasses,
+        StartupProfile startupProfile) {
       if (featureSplitClasses.isEmpty()) {
         return;
       }
@@ -473,7 +473,7 @@
                 appView,
                 featureSplitSetEntry.getValue(),
                 originalNames,
-                startupOrder,
+                startupProfile,
                 nextFileId)
             .run();
       }
@@ -483,17 +483,17 @@
   public static class FillFilesDistributor extends DistributorBase {
 
     private final ExecutorService executorService;
-    private final StartupOrder startupOrder;
+    private final StartupProfile startupProfile;
 
     FillFilesDistributor(
         ApplicationWriter writer,
         Collection<DexProgramClass> classes,
         InternalOptions options,
         ExecutorService executorService,
-        StartupOrder startupOrder) {
-      super(writer, classes, options, startupOrder);
+        StartupProfile startupProfile) {
+      super(writer, classes, options, startupProfile);
       this.executorService = executorService;
-      this.startupOrder = startupOrder;
+      this.startupProfile = startupProfile;
     }
 
     @Override
@@ -539,11 +539,11 @@
                 appView,
                 classes,
                 originalNames,
-                startupOrder,
+                startupProfile,
                 nextFileId)
             .run();
       }
-      addFeatureSplitFiles(featureSplitClasses, startupOrder);
+      addFeatureSplitFiles(featureSplitClasses, startupProfile);
 
       assert totalClassNumber == virtualFiles.stream().mapToInt(dex -> dex.classes().size()).sum();
       return virtualFiles;
@@ -553,7 +553,7 @@
   public static class MonoDexDistributor extends DistributorBase {
     MonoDexDistributor(
         ApplicationWriter writer, Collection<DexProgramClass> classes, InternalOptions options) {
-      super(writer, classes, options, StartupOrder.empty());
+      super(writer, classes, options, StartupProfile.empty());
     }
 
     @Override
@@ -569,7 +569,7 @@
       if (options.featureSplitConfiguration != null) {
         if (!featureSplitClasses.isEmpty()) {
           // TODO(141334414): Figure out if we allow multidex in features even when mono-dexing
-          addFeatureSplitFiles(featureSplitClasses, StartupOrder.empty());
+          addFeatureSplitFiles(featureSplitClasses, StartupProfile.empty());
         }
       }
       return virtualFiles;
@@ -1279,12 +1279,11 @@
       public static PackageSplitClassPartioning create(
           Collection<DexProgramClass> classes,
           Map<DexProgramClass, String> originalNames,
-          StartupOrder startupOrder,
-          SyntheticItems syntheticItems) {
+          StartupProfile startupProfile) {
         return create(
             classes,
             getClassesByPackageComparator(originalNames),
-            getStartupClassPredicate(startupOrder, syntheticItems));
+            getStartupClassPredicate(startupProfile));
       }
 
       private static PackageSplitClassPartioning create(
@@ -1335,8 +1334,8 @@
       }
 
       private static Predicate<DexProgramClass> getStartupClassPredicate(
-          StartupOrder startupOrder, SyntheticItems syntheticItems) {
-        return clazz -> startupOrder.contains(clazz.getType(), syntheticItems);
+          StartupProfile startupProfile) {
+        return clazz -> startupProfile.containsClassRule(clazz.getType());
       }
 
       public List<DexProgramClass> getStartupClasses() {
@@ -1372,11 +1371,10 @@
         AppView<?> appView,
         Collection<DexProgramClass> classes,
         Map<DexProgramClass, String> originalNames,
-        StartupOrder startupOrder,
+        StartupProfile startupProfile,
         IntBox nextFileId) {
       this.classPartioning =
-          PackageSplitClassPartioning.create(
-              classes, originalNames, startupOrder, appView.getSyntheticItems());
+          PackageSplitClassPartioning.create(classes, originalNames, startupProfile);
       this.originalNames = originalNames;
       this.dexItemFactory = appView.dexItemFactory();
       this.options = appView.options();
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupOrder.java b/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupOrder.java
deleted file mode 100644
index 032e0b8..0000000
--- a/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupOrder.java
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (c) 2022, 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.experimental.startup;
-
-import com.android.tools.r8.experimental.startup.profile.StartupItem;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.synthesis.SyntheticItems;
-import java.util.Collection;
-import java.util.Collections;
-
-public class EmptyStartupOrder extends StartupOrder {
-
-  EmptyStartupOrder() {}
-
-  @Override
-  public boolean contains(DexMethod method, SyntheticItems syntheticItems) {
-    return false;
-  }
-
-  @Override
-  public boolean contains(DexType type, SyntheticItems syntheticItems) {
-    return false;
-  }
-
-  @Override
-  public Collection<StartupItem> getItems() {
-    return Collections.emptyList();
-  }
-
-  @Override
-  public boolean isEmpty() {
-    return true;
-  }
-
-  @Override
-  public EmptyStartupOrder rewrittenWithLens(GraphLens graphLens) {
-    return this;
-  }
-
-  @Override
-  public StartupOrder toStartupOrderForWriting(AppView<?> appView) {
-    return this;
-  }
-
-  @Override
-  public EmptyStartupOrder withoutPrunedItems(
-      PrunedItems prunedItems, SyntheticItems syntheticItems) {
-    return this;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupProfile.java b/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupProfile.java
new file mode 100644
index 0000000..7f2a248
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupProfile.java
@@ -0,0 +1,81 @@
+// Copyright (c) 2022, 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.experimental.startup;
+
+import com.android.tools.r8.experimental.startup.profile.StartupProfileClassRule;
+import com.android.tools.r8.experimental.startup.profile.StartupProfileMethodRule;
+import com.android.tools.r8.experimental.startup.profile.StartupProfileRule;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
+import com.android.tools.r8.synthesis.SyntheticItems;
+import com.android.tools.r8.utils.ThrowingConsumer;
+import java.util.Collection;
+import java.util.Collections;
+
+public class EmptyStartupProfile extends StartupProfile {
+
+  EmptyStartupProfile() {}
+
+  @Override
+  public boolean containsClassRule(DexType type) {
+    return false;
+  }
+
+  @Override
+  public boolean containsMethodRule(DexMethod method) {
+    return false;
+  }
+
+  @Override
+  public <E1 extends Exception, E2 extends Exception> void forEachRule(
+      ThrowingConsumer<StartupProfileClassRule, E1> classRuleConsumer,
+      ThrowingConsumer<StartupProfileMethodRule, E2> methodRuleConsumer) {
+    // Intentionally empty.
+  }
+
+  @Override
+  public StartupProfileClassRule getClassRule(DexType type) {
+    return null;
+  }
+
+  @Override
+  public StartupProfileMethodRule getMethodRule(DexMethod method) {
+    return null;
+  }
+
+  @Override
+  public Collection<StartupProfileRule> getRules() {
+    return Collections.emptyList();
+  }
+
+  @Override
+  public boolean isEmpty() {
+    return true;
+  }
+
+  @Override
+  public EmptyStartupProfile rewrittenWithLens(GraphLens graphLens) {
+    return this;
+  }
+
+  @Override
+  public EmptyStartupProfile toStartupProfileForWriting(AppView<?> appView) {
+    return this;
+  }
+
+  @Override
+  public StartupProfile withoutMissingItems(AppView<?> appView) {
+    return this;
+  }
+
+  @Override
+  public EmptyStartupProfile withoutPrunedItems(
+      PrunedItems prunedItems, SyntheticItems syntheticItems) {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/NonEmptyStartupOrder.java b/src/main/java/com/android/tools/r8/experimental/startup/NonEmptyStartupOrder.java
deleted file mode 100644
index 25188c0..0000000
--- a/src/main/java/com/android/tools/r8/experimental/startup/NonEmptyStartupOrder.java
+++ /dev/null
@@ -1,266 +0,0 @@
-// Copyright (c) 2022, 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.experimental.startup;
-
-import com.android.tools.r8.experimental.startup.profile.StartupClass;
-import com.android.tools.r8.experimental.startup.profile.StartupItem;
-import com.android.tools.r8.experimental.startup.profile.StartupMethod;
-import com.android.tools.r8.experimental.startup.profile.SyntheticStartupMethod;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.synthesis.SyntheticItems;
-import com.android.tools.r8.utils.LazyBox;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-public class NonEmptyStartupOrder extends StartupOrder {
-
-  private final LinkedHashSet<StartupItem> startupItems;
-
-  // Sets to allow efficient querying without boxing.
-  private final Set<DexType> nonSyntheticStartupClasses = Sets.newIdentityHashSet();
-  private final Set<DexType> syntheticStartupClasses = Sets.newIdentityHashSet();
-
-  private final Set<DexMethod> nonSyntheticStartupMethods = Sets.newIdentityHashSet();
-
-  NonEmptyStartupOrder(LinkedHashSet<StartupItem> startupItems) {
-    assert !startupItems.isEmpty();
-    this.startupItems = startupItems;
-    for (StartupItem startupItem : startupItems) {
-      startupItem.accept(
-          startupClass -> nonSyntheticStartupClasses.add(startupClass.getReference()),
-          startupMethod -> {
-            nonSyntheticStartupClasses.add(startupMethod.getReference().getHolderType());
-            nonSyntheticStartupMethods.add(startupMethod.getReference());
-          },
-          syntheticStartupMethod ->
-              syntheticStartupClasses.add(syntheticStartupMethod.getSyntheticContextType()));
-    }
-  }
-
-  @Override
-  public boolean contains(DexMethod method, SyntheticItems syntheticItems) {
-    if (nonSyntheticStartupMethods.contains(method)) {
-      return true;
-    }
-    if (syntheticItems.isSyntheticClass(method.getHolderType())) {
-      return containsSyntheticClass(method.getHolderType(), syntheticItems);
-    }
-    return false;
-  }
-
-  @Override
-  public boolean contains(DexType type, SyntheticItems syntheticItems) {
-    return syntheticItems.isSyntheticClass(type)
-        ? containsSyntheticClass(type, syntheticItems)
-        : containsNonSyntheticClass(type);
-  }
-
-  private boolean containsNonSyntheticClass(DexType type) {
-    return nonSyntheticStartupClasses.contains(type);
-  }
-
-  private boolean containsSyntheticClass(DexType type, SyntheticItems syntheticItems) {
-    assert syntheticItems.isSyntheticClass(type);
-    return Iterables.any(
-        syntheticItems.getSynthesizingContextTypes(type),
-        this::containsSyntheticClassesSynthesizedFrom);
-  }
-
-  private boolean containsSyntheticClassesSynthesizedFrom(DexType synthesizingContextType) {
-    return syntheticStartupClasses.contains(synthesizingContextType);
-  }
-
-  @Override
-  public Collection<StartupItem> getItems() {
-    return startupItems;
-  }
-
-  @Override
-  public boolean isEmpty() {
-    return false;
-  }
-
-  @Override
-  public StartupOrder rewrittenWithLens(GraphLens graphLens) {
-    LinkedHashSet<StartupItem> rewrittenStartupItems = new LinkedHashSet<>(startupItems.size());
-    for (StartupItem startupItem : startupItems) {
-      // TODO(b/238173796): This should account for one-to-many mappings. e.g., when a bridge is
-      //  created.
-      startupItem.apply(
-          startupClass ->
-              rewrittenStartupItems.add(
-                  StartupClass.builder()
-                      .setClassReference(graphLens.lookupType(startupClass.getReference()))
-                      .build()),
-          startupMethod ->
-              rewrittenStartupItems.add(
-                  StartupMethod.builder()
-                      .setMethodReference(
-                          graphLens.getRenamedMethodSignature(startupMethod.getReference()))
-                      .build()),
-          syntheticStartupMethod ->
-              rewrittenStartupItems.add(
-                  SyntheticStartupMethod.builder()
-                      .setSyntheticContextReference(
-                          graphLens.lookupType(syntheticStartupMethod.getSyntheticContextType()))
-                      .build()));
-    }
-    return createNonEmpty(rewrittenStartupItems);
-  }
-
-  /**
-   * This is called to process the startup order before computing the startup layouts.
-   *
-   * <p>This processing makes two key changes to the startup order:
-   *
-   * <ul>
-   *   <li>Synthetic startup classes on the form "SLcom/example/SyntheticContext;" represents that
-   *       any method of any synthetic class that have been synthesized from SyntheticContext has
-   *       been executed. This pass removes such entries from the startup order, and replaces them
-   *       by all the methods from all of the synthetics that have been synthesized from
-   *       SyntheticContext.
-   *   <li>Moreover, this inserts a StartupClass event for all supertypes of a given class next to
-   *       the class in the startup order. This ensures that the classes from the super hierarchy
-   *       will be laid out close to their subclasses, at the point where the subclasses are used
-   *       during startup.
-   *       <p>Note that this normally follows from the trace already, except that the class
-   *       initializers of interfaces are not executed when a subclass is used.
-   * </ul>
-   */
-  @Override
-  public StartupOrder toStartupOrderForWriting(AppView<?> appView) {
-    LinkedHashSet<StartupItem> rewrittenStartupItems = new LinkedHashSet<>(startupItems.size());
-    Map<DexType, List<DexProgramClass>> syntheticContextsToSyntheticClasses =
-        appView.getSyntheticItems().computeSyntheticContextsToSyntheticClasses(appView);
-    for (StartupItem startupItem : startupItems) {
-      addStartupItem(
-          startupItem, rewrittenStartupItems, syntheticContextsToSyntheticClasses, appView);
-    }
-    assert rewrittenStartupItems.stream().noneMatch(StartupItem::isSyntheticStartupMethod);
-    return createNonEmpty(rewrittenStartupItems);
-  }
-
-  private static void addStartupItem(
-      StartupItem startupItem,
-      LinkedHashSet<StartupItem> rewrittenStartupItems,
-      Map<DexType, List<DexProgramClass>> syntheticContextsToSyntheticClasses,
-      AppView<?> appView) {
-    if (startupItem.isSyntheticStartupMethod()) {
-      SyntheticStartupMethod syntheticStartupMethod = startupItem.asSyntheticStartupMethod();
-      List<DexProgramClass> syntheticClassesForContext =
-          syntheticContextsToSyntheticClasses.getOrDefault(
-              syntheticStartupMethod.getSyntheticContextType(), Collections.emptyList());
-      for (DexProgramClass clazz : syntheticClassesForContext) {
-        addClassAndParentClasses(clazz, rewrittenStartupItems, appView);
-        addAllMethods(clazz, rewrittenStartupItems);
-      }
-    } else {
-      if (startupItem.isStartupClass()) {
-        addClassAndParentClasses(
-            startupItem.asStartupClass().getReference(), rewrittenStartupItems, appView);
-      } else {
-        rewrittenStartupItems.add(startupItem);
-      }
-    }
-  }
-
-  private static boolean addClass(
-      DexProgramClass clazz, LinkedHashSet<StartupItem> rewrittenStartupItems) {
-    return rewrittenStartupItems.add(
-        StartupClass.builder().setClassReference(clazz.getType()).build());
-  }
-
-  private static void addClassAndParentClasses(
-      DexType type, LinkedHashSet<StartupItem> rewrittenStartupItems, AppView<?> appView) {
-    DexProgramClass definition = appView.app().programDefinitionFor(type);
-    if (definition != null) {
-      addClassAndParentClasses(definition, rewrittenStartupItems, appView);
-    }
-  }
-
-  private static void addClassAndParentClasses(
-      DexProgramClass clazz, LinkedHashSet<StartupItem> rewrittenStartupItems, AppView<?> appView) {
-    if (addClass(clazz, rewrittenStartupItems)) {
-      addParentClasses(clazz, rewrittenStartupItems, appView);
-    }
-  }
-
-  private static void addParentClasses(
-      DexProgramClass clazz, LinkedHashSet<StartupItem> rewrittenStartupItems, AppView<?> appView) {
-    clazz.forEachImmediateSupertype(
-        supertype -> addClassAndParentClasses(supertype, rewrittenStartupItems, appView));
-  }
-
-  private static void addAllMethods(
-      DexProgramClass clazz, LinkedHashSet<StartupItem> rewrittenStartupItems) {
-    clazz.forEachProgramMethod(
-        method ->
-            rewrittenStartupItems.add(
-                StartupMethod.builder().setMethodReference(method.getReference()).build()));
-  }
-
-  @Override
-  public StartupOrder withoutPrunedItems(PrunedItems prunedItems, SyntheticItems syntheticItems) {
-    LinkedHashSet<StartupItem> rewrittenStartupItems = new LinkedHashSet<>(startupItems.size());
-    LazyBox<Set<DexType>> contextsOfLiveSynthetics =
-        new LazyBox<>(
-            () -> computeContextsOfLiveSynthetics(prunedItems.getPrunedApp(), syntheticItems));
-    for (StartupItem startupItem : startupItems) {
-      // Only prune non-synthetic classes, since the pruning of a class does not imply that all
-      // classes synthesized from it have been pruned.
-      startupItem.accept(
-          startupClass -> {
-            if (!prunedItems.isRemoved(startupClass.getReference())) {
-              rewrittenStartupItems.add(startupItem);
-            }
-          },
-          startupMethod -> {
-            if (!prunedItems.isRemoved(startupMethod.getReference())) {
-              rewrittenStartupItems.add(startupItem);
-            }
-          },
-          syntheticStartupMethod -> {
-            if (contextsOfLiveSynthetics
-                .computeIfAbsent()
-                .contains(syntheticStartupMethod.getSyntheticContextType())) {
-              rewrittenStartupItems.add(syntheticStartupMethod);
-            }
-          });
-    }
-    return createNonEmpty(rewrittenStartupItems);
-  }
-
-  private Set<DexType> computeContextsOfLiveSynthetics(
-      DexApplication app, SyntheticItems syntheticItems) {
-    Set<DexType> contextsOfLiveSynthetics = Sets.newIdentityHashSet();
-    for (DexProgramClass clazz : app.classes()) {
-      if (syntheticItems.isSyntheticClass(clazz)) {
-        contextsOfLiveSynthetics.addAll(
-            syntheticItems.getSynthesizingContextTypes(clazz.getType()));
-      }
-    }
-    return contextsOfLiveSynthetics;
-  }
-
-  private StartupOrder createNonEmpty(LinkedHashSet<StartupItem> startupItems) {
-    if (startupItems.isEmpty()) {
-      assert false;
-      return empty();
-    }
-    return new NonEmptyStartupOrder(startupItems);
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupCompleteness.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupCompleteness.java
index 6ef463d..e3699f7 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupCompleteness.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupCompleteness.java
@@ -4,40 +4,32 @@
 
 package com.android.tools.r8.experimental.startup;
 
-import com.android.tools.r8.experimental.startup.profile.StartupItem;
+import com.android.tools.r8.experimental.startup.profile.StartupProfileRule;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexReference;
-import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.ThrowNullCode;
-import com.android.tools.r8.profile.art.ArtProfileBuilderUtils.SyntheticToSyntheticContextGeneralization;
+import com.android.tools.r8.startup.diagnostic.MissingStartupProfileItemsDiagnostic;
 import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.Sets;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMaps;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
 import java.util.Set;
 
 public class StartupCompleteness {
 
   private final AppView<?> appView;
-  private final StartupOrder startupOrder;
+  private final StartupProfile startupProfile;
 
   private StartupCompleteness(AppView<?> appView) {
-    SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization =
-        appView.enableWholeProgramOptimizations()
-            ? SyntheticToSyntheticContextGeneralization.createForR8()
-            : SyntheticToSyntheticContextGeneralization.createForD8();
     this.appView = appView;
-    this.startupOrder =
+    this.startupProfile =
         appView.hasClassHierarchy()
-            ? appView.appInfoWithClassHierarchy().getStartupOrder()
-            : StartupOrder.createInitialStartupOrder(
-                appView.options(), null, syntheticToSyntheticContextGeneralization);
+            ? appView.getStartupProfile()
+            : StartupProfile.createInitialStartupProfile(
+                appView.options(), origin -> MissingStartupProfileItemsDiagnostic.Builder.nop());
   }
 
   /**
@@ -82,22 +74,10 @@
 
   private Set<DexReference> computeStartupItems() {
     Set<DexReference> startupItems = Sets.newIdentityHashSet();
-    Map<DexType, List<DexProgramClass>> syntheticContextsToSyntheticClasses =
-        appView.getSyntheticItems().computeSyntheticContextsToSyntheticClasses(appView);
-    for (StartupItem startupItem : startupOrder.getItems()) {
+    for (StartupProfileRule startupItem : startupProfile.getRules()) {
       startupItem.accept(
           startupClass -> startupItems.add(startupClass.getReference()),
-          startupMethod -> startupItems.add(startupMethod.getReference()),
-          syntheticStartupMethod -> {
-            List<DexProgramClass> syntheticClasses =
-                syntheticContextsToSyntheticClasses.getOrDefault(
-                    syntheticStartupMethod.getSyntheticContextType(), Collections.emptyList());
-            for (DexProgramClass syntheticClass : syntheticClasses) {
-              startupItems.add(syntheticClass.getType());
-              syntheticClass.forEachProgramMethod(
-                  method -> startupItems.add(method.getReference()));
-            }
-          });
+          startupMethod -> startupItems.add(startupMethod.getReference()));
     }
     return startupItems;
   }
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java
deleted file mode 100644
index b369a94..0000000
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (c) 2022, 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.experimental.startup;
-
-import com.android.tools.r8.experimental.startup.profile.StartupItem;
-import com.android.tools.r8.experimental.startup.profile.StartupProfile;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexDefinitionSupplier;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.profile.art.ArtProfileBuilderUtils.SyntheticToSyntheticContextGeneralization;
-import com.android.tools.r8.synthesis.SyntheticItems;
-import com.android.tools.r8.utils.InternalOptions;
-import java.util.Collection;
-import java.util.LinkedHashSet;
-
-public abstract class StartupOrder {
-
-  StartupOrder() {}
-
-  public static StartupOrder createInitialStartupOrder(
-      InternalOptions options,
-      DexDefinitionSupplier definitions,
-      SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization) {
-    StartupProfile startupProfile =
-        StartupProfile.parseStartupProfile(
-            options, definitions, syntheticToSyntheticContextGeneralization);
-    if (startupProfile == null || startupProfile.getStartupItems().isEmpty()) {
-      return empty();
-    }
-    return new NonEmptyStartupOrder(new LinkedHashSet<>(startupProfile.getStartupItems()));
-  }
-
-  public static StartupOrder createInitialStartupOrderForD8(AppView<?> appView) {
-    return createInitialStartupOrder(
-        appView.options(), appView, SyntheticToSyntheticContextGeneralization.createForD8());
-  }
-
-  public static StartupOrder createInitialStartupOrderForR8(DexApplication application) {
-    return createInitialStartupOrder(
-        application.options, application, SyntheticToSyntheticContextGeneralization.createForR8());
-  }
-
-  public static StartupOrder empty() {
-    return new EmptyStartupOrder();
-  }
-
-  public abstract boolean contains(DexMethod method, SyntheticItems syntheticItems);
-
-  public abstract boolean contains(DexType type, SyntheticItems syntheticItems);
-
-  public abstract Collection<StartupItem> getItems();
-
-  public abstract boolean isEmpty();
-
-  public abstract StartupOrder rewrittenWithLens(GraphLens graphLens);
-
-  public abstract StartupOrder toStartupOrderForWriting(AppView<?> appView);
-
-  public abstract StartupOrder withoutPrunedItems(
-      PrunedItems prunedItems, SyntheticItems syntheticItems);
-}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupProfile.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupProfile.java
new file mode 100644
index 0000000..b85dbaa
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupProfile.java
@@ -0,0 +1,271 @@
+// Copyright (c) 2022, 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.experimental.startup;
+
+import com.android.tools.r8.TextInputStream;
+import com.android.tools.r8.experimental.startup.profile.NonEmptyStartupProfile;
+import com.android.tools.r8.experimental.startup.profile.StartupProfileClassRule;
+import com.android.tools.r8.experimental.startup.profile.StartupProfileMethodRule;
+import com.android.tools.r8.experimental.startup.profile.StartupProfileRule;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.profile.AbstractProfile;
+import com.android.tools.r8.profile.AbstractProfileRule;
+import com.android.tools.r8.profile.art.ArtProfileBuilderUtils;
+import com.android.tools.r8.profile.art.HumanReadableArtProfileParser;
+import com.android.tools.r8.profile.art.HumanReadableArtProfileParserBuilder;
+import com.android.tools.r8.startup.StartupClassBuilder;
+import com.android.tools.r8.startup.StartupMethodBuilder;
+import com.android.tools.r8.startup.StartupProfileBuilder;
+import com.android.tools.r8.startup.StartupProfileProvider;
+import com.android.tools.r8.startup.diagnostic.MissingStartupProfileItemsDiagnostic;
+import com.android.tools.r8.synthesis.SyntheticItems;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Reporter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+public abstract class StartupProfile
+    implements AbstractProfile<StartupProfileClassRule, StartupProfileMethodRule> {
+
+  protected StartupProfile() {}
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static Builder builderWithCapacity(int capacity) {
+    return new Builder(capacity);
+  }
+
+  public static Builder builder(
+      InternalOptions options,
+      MissingStartupProfileItemsDiagnostic.Builder missingItemsDiagnosticBuilder,
+      StartupProfileProvider startupProfileProvider) {
+    return new Builder(options, missingItemsDiagnosticBuilder, startupProfileProvider);
+  }
+
+  public static StartupProfile createInitialStartupProfile(
+      InternalOptions options,
+      Function<Origin, MissingStartupProfileItemsDiagnostic.Builder>
+          missingItemsDiagnosticBuilderFactory) {
+    StartupProfile startupProfile =
+        StartupProfile.parseStartupProfile(options, missingItemsDiagnosticBuilderFactory);
+    return startupProfile != null ? startupProfile : empty();
+  }
+
+  public static StartupProfile createInitialStartupProfileForD8(AppView<?> appView) {
+    return createInitialStartupProfile(
+        appView.options(),
+        origin -> new MissingStartupProfileItemsDiagnostic.Builder(appView).setOrigin(origin));
+  }
+
+  public static StartupProfile createInitialStartupProfileForR8(DexApplication application) {
+    // In R8 we expect a startup profile that matches the input app. Since profiles gathered from
+    // running on ART will include synthetics, and these synthetics are not in the input app, we do
+    // not raise warnings if some rules in the profile do not match anything.
+    return createInitialStartupProfile(
+        application.options, origin -> MissingStartupProfileItemsDiagnostic.Builder.nop());
+  }
+
+  public static StartupProfile empty() {
+    return new EmptyStartupProfile();
+  }
+
+  public static StartupProfile merge(Collection<StartupProfile> startupProfiles) {
+    Builder builder = builder();
+    for (StartupProfile startupProfile : startupProfiles) {
+      startupProfile.getRules().forEach(builder::addStartupItem);
+    }
+    return builder.build();
+  }
+
+  /**
+   * Parses the supplied startup configuration, if any. The startup configuration is a list of class
+   * and method descriptors.
+   *
+   * <p>Example:
+   *
+   * <pre>
+   * Landroidx/compose/runtime/ComposerImpl;->updateValue(Ljava/lang/Object;)V
+   * Landroidx/compose/runtime/ComposerImpl;->updatedNodeCount(I)I
+   * Landroidx/compose/runtime/ComposerImpl;->validateNodeExpected()V
+   * Landroidx/compose/runtime/CompositionImpl;->applyChanges()V
+   * Landroidx/compose/runtime/ComposerKt;->findLocation(Ljava/util/List;I)I
+   * Landroidx/compose/runtime/ComposerImpl;
+   * </pre>
+   */
+  public static StartupProfile parseStartupProfile(
+      InternalOptions options,
+      Function<Origin, MissingStartupProfileItemsDiagnostic.Builder>
+          missingItemsDiagnosticBuilderFactory) {
+    if (!options.getStartupOptions().hasStartupProfileProviders()) {
+      return null;
+    }
+    Collection<StartupProfileProvider> startupProfileProviders =
+        options.getStartupOptions().getStartupProfileProviders();
+    List<StartupProfile> startupProfiles = new ArrayList<>(startupProfileProviders.size());
+    for (StartupProfileProvider startupProfileProvider : startupProfileProviders) {
+      MissingStartupProfileItemsDiagnostic.Builder missingItemsDiagnosticBuilder =
+          missingItemsDiagnosticBuilderFactory.apply(startupProfileProvider.getOrigin());
+      StartupProfile.Builder startupProfileBuilder =
+          StartupProfile.builder(options, missingItemsDiagnosticBuilder, startupProfileProvider);
+      startupProfileProvider.getStartupProfile(startupProfileBuilder);
+      startupProfiles.add(startupProfileBuilder.build());
+      if (missingItemsDiagnosticBuilder.hasMissingStartupItems()) {
+        options.reporter.warning(missingItemsDiagnosticBuilder.build());
+      }
+    }
+    return StartupProfile.merge(startupProfiles);
+  }
+
+  public abstract Collection<StartupProfileRule> getRules();
+
+  public abstract boolean isEmpty();
+
+  public abstract StartupProfile rewrittenWithLens(GraphLens graphLens);
+
+  public abstract StartupProfile toStartupProfileForWriting(AppView<?> appView);
+
+  public abstract StartupProfile withoutMissingItems(AppView<?> appView);
+
+  public abstract StartupProfile withoutPrunedItems(
+      PrunedItems prunedItems, SyntheticItems syntheticItems);
+
+  public static class Builder
+      implements AbstractProfile.Builder<
+              StartupProfileClassRule, StartupProfileMethodRule, StartupProfile, Builder>,
+          StartupProfileBuilder {
+
+    private final DexItemFactory dexItemFactory;
+    private final MissingStartupProfileItemsDiagnostic.Builder missingItemsDiagnosticBuilder;
+    private Reporter reporter;
+    private final StartupProfileProvider startupProfileProvider;
+
+    private final LinkedHashMap<DexReference, StartupProfileRule> startupItems;
+
+    Builder() {
+      this.dexItemFactory = null;
+      this.missingItemsDiagnosticBuilder = null;
+      this.reporter = null;
+      this.startupItems = new LinkedHashMap<>();
+      this.startupProfileProvider = null;
+    }
+
+    Builder(int capacity) {
+      this.dexItemFactory = null;
+      this.missingItemsDiagnosticBuilder = null;
+      this.reporter = null;
+      this.startupItems = new LinkedHashMap<>(capacity);
+      this.startupProfileProvider = null;
+    }
+
+    Builder(
+        InternalOptions options,
+        MissingStartupProfileItemsDiagnostic.Builder missingItemsDiagnosticBuilder,
+        StartupProfileProvider startupProfileProvider) {
+      this.dexItemFactory = options.dexItemFactory();
+      this.missingItemsDiagnosticBuilder = missingItemsDiagnosticBuilder;
+      this.reporter = options.reporter;
+      this.startupItems = new LinkedHashMap<>();
+      this.startupProfileProvider = startupProfileProvider;
+    }
+
+    @Override
+    public Builder addRule(AbstractProfileRule rule) {
+      return addStartupItem(rule.asStartupProfileRule());
+    }
+
+    @Override
+    public Builder addClassRule(StartupProfileClassRule classRule) {
+      return addStartupItem(classRule);
+    }
+
+    @Override
+    public Builder addMethodRule(StartupProfileMethodRule methodRule) {
+      return addStartupItem(methodRule);
+    }
+
+    @Override
+    public Builder addStartupClass(Consumer<StartupClassBuilder> startupClassBuilderConsumer) {
+      StartupProfileClassRule.Builder startupClassBuilder =
+          StartupProfileClassRule.builder(dexItemFactory);
+      startupClassBuilderConsumer.accept(startupClassBuilder);
+      StartupProfileClassRule startupClass = startupClassBuilder.build();
+      if (missingItemsDiagnosticBuilder.registerStartupClass(startupClass)) {
+        return this;
+      }
+      return addStartupItem(startupClass);
+    }
+
+    @Override
+    public Builder addStartupMethod(Consumer<StartupMethodBuilder> startupMethodBuilderConsumer) {
+      StartupProfileMethodRule.Builder startupMethodBuilder =
+          StartupProfileMethodRule.builder(dexItemFactory);
+      startupMethodBuilderConsumer.accept(startupMethodBuilder);
+      StartupProfileMethodRule startupMethod = startupMethodBuilder.build();
+      if (missingItemsDiagnosticBuilder.registerStartupMethod(startupMethod)) {
+        return this;
+      }
+      return addStartupItem(startupMethod);
+    }
+
+    @Override
+    public StartupProfileBuilder addHumanReadableArtProfile(
+        TextInputStream textInputStream,
+        Consumer<HumanReadableArtProfileParserBuilder> parserBuilderConsumer) {
+      HumanReadableArtProfileParser.Builder parserBuilder =
+          HumanReadableArtProfileParser.builder()
+              .setReporter(reporter)
+              .setProfileBuilder(
+                  ArtProfileBuilderUtils.createBuilderForArtProfileToStartupProfileConversion(
+                      this));
+      parserBuilderConsumer.accept(parserBuilder);
+      HumanReadableArtProfileParser parser = parserBuilder.build();
+      parser.parse(textInputStream, startupProfileProvider.getOrigin());
+      return this;
+    }
+
+    public Builder addStartupItem(StartupProfileRule startupItem) {
+      startupItems.put(startupItem.getReference(), startupItem);
+      return this;
+    }
+
+    public Builder apply(Consumer<Builder> consumer) {
+      consumer.accept(this);
+      return this;
+    }
+
+    public Builder setIgnoreWarnings() {
+      return setReporter(null);
+    }
+
+    public Builder setReporter(Reporter reporter) {
+      this.reporter = reporter;
+      return this;
+    }
+
+    public int size() {
+      return startupItems.size();
+    }
+
+    @Override
+    public StartupProfile build() {
+      if (startupItems.isEmpty()) {
+        return empty();
+      }
+      return new NonEmptyStartupProfile(startupItems);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupProfileProviderUtils.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupProfileProviderUtils.java
index a4b0f5e..e5b6b58 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupProfileProviderUtils.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupProfileProviderUtils.java
@@ -6,11 +6,9 @@
 
 import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
 
-import com.android.tools.r8.experimental.startup.profile.StartupItem;
-import com.android.tools.r8.experimental.startup.profile.StartupProfile;
+import com.android.tools.r8.experimental.startup.profile.StartupProfileRule;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.PathOrigin;
-import com.android.tools.r8.profile.art.ArtProfileBuilderUtils.SyntheticToSyntheticContextGeneralization;
 import com.android.tools.r8.profile.art.HumanReadableArtProfileParserBuilder;
 import com.android.tools.r8.startup.StartupProfileBuilder;
 import com.android.tools.r8.startup.StartupProfileProvider;
@@ -51,27 +49,21 @@
 
   /** Serialize the given {@param startupProfileProvider} to a string for writing it to a dump. */
   public static String serializeToString(
-      InternalOptions options, StartupProfileProvider startupProfileProvider) {
+      InternalOptions options, StartupProfileProvider startupProfileProvider) throws IOException {
     // Do not report missing items.
     MissingStartupProfileItemsDiagnostic.Builder missingItemsDiagnosticBuilder =
         MissingStartupProfileItemsDiagnostic.Builder.nop();
-    // Do not generalize synthetic items to their synthetic context.
-    SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization =
-        SyntheticToSyntheticContextGeneralization.createForD8();
     StartupProfile.Builder startupProfileBuilder =
-        StartupProfile.builder(
-            options,
-            missingItemsDiagnosticBuilder,
-            startupProfileProvider,
-            syntheticToSyntheticContextGeneralization);
+        StartupProfile.builder(options, missingItemsDiagnosticBuilder, startupProfileProvider);
     // Do not report warnings for lines that cannot be parsed.
     startupProfileBuilder.setIgnoreWarnings();
     // Populate the startup profile builder.
     startupProfileProvider.getStartupProfile(startupProfileBuilder);
     // Serialize the startup items.
     StringBuilder resultBuilder = new StringBuilder();
-    for (StartupItem startupItem : startupProfileBuilder.build().getStartupItems()) {
-      resultBuilder.append(startupItem.serializeToString()).append('\n');
+    for (StartupProfileRule startupItem : startupProfileBuilder.build().getRules()) {
+      startupItem.write(resultBuilder);
+      resultBuilder.append('\n');
     }
     return resultBuilder.toString();
   }
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/profile/NonEmptyStartupProfile.java b/src/main/java/com/android/tools/r8/experimental/startup/profile/NonEmptyStartupProfile.java
new file mode 100644
index 0000000..ab251a8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/experimental/startup/profile/NonEmptyStartupProfile.java
@@ -0,0 +1,185 @@
+// Copyright (c) 2022, 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.experimental.startup.profile;
+
+import com.android.tools.r8.experimental.startup.StartupProfile;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
+import com.android.tools.r8.synthesis.SyntheticItems;
+import com.android.tools.r8.utils.ThrowingConsumer;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.function.BiConsumer;
+
+public class NonEmptyStartupProfile extends StartupProfile {
+
+  private final LinkedHashMap<DexReference, StartupProfileRule> startupItems;
+
+  public NonEmptyStartupProfile(LinkedHashMap<DexReference, StartupProfileRule> startupItems) {
+    assert !startupItems.isEmpty();
+    this.startupItems = startupItems;
+  }
+
+  @Override
+  public boolean containsMethodRule(DexMethod method) {
+    return startupItems.containsKey(method);
+  }
+
+  @Override
+  public boolean containsClassRule(DexType type) {
+    return startupItems.containsKey(type);
+  }
+
+  @Override
+  public <E1 extends Exception, E2 extends Exception> void forEachRule(
+      ThrowingConsumer<StartupProfileClassRule, E1> classRuleConsumer,
+      ThrowingConsumer<StartupProfileMethodRule, E2> methodRuleConsumer)
+      throws E1, E2 {
+    for (StartupProfileRule rule : getRules()) {
+      rule.accept(classRuleConsumer, methodRuleConsumer);
+    }
+  }
+
+  @Override
+  public StartupProfileClassRule getClassRule(DexType type) {
+    return (StartupProfileClassRule) startupItems.get(type);
+  }
+
+  @Override
+  public StartupProfileMethodRule getMethodRule(DexMethod method) {
+    return (StartupProfileMethodRule) startupItems.get(method);
+  }
+
+  @Override
+  public Collection<StartupProfileRule> getRules() {
+    return startupItems.values();
+  }
+
+  @Override
+  public boolean isEmpty() {
+    return false;
+  }
+
+  @Override
+  public StartupProfile rewrittenWithLens(GraphLens graphLens) {
+    return transform(
+        (classRule, builder) ->
+            builder.addClassRule(
+                StartupProfileClassRule.builder()
+                    .setClassReference(graphLens.lookupType(classRule.getReference()))
+                    .build()),
+        (methodRule, builder) ->
+            builder.addMethodRule(
+                StartupProfileMethodRule.builder()
+                    .setMethod(graphLens.getRenamedMethodSignature(methodRule.getReference()))
+                    .build()));
+  }
+
+  public int size() {
+    return startupItems.size();
+  }
+
+  /**
+   * This is called to process the startup profile before computing the startup layouts.
+   *
+   * <p>This processing makes the following key change to the startup profile: A {@link
+   * StartupProfileClassRule} is inserted for all supertypes of a given class next to the class in
+   * the startup profile. This ensures that the classes from the super hierarchy will be laid out
+   * close to their subclasses, at the point where the subclasses are used during startup.
+   *
+   * <p>This normally follows from the trace already, except that the class initializers of
+   * interfaces are not executed when a subclass is used.
+   */
+  @Override
+  public StartupProfile toStartupProfileForWriting(AppView<?> appView) {
+    return transform(
+        (classRule, builder) -> addStartupItem(classRule, builder, appView),
+        (methodRule, builder) -> addStartupItem(methodRule, builder, appView));
+  }
+
+  private static void addStartupItem(
+      StartupProfileRule startupItem, Builder builder, AppView<?> appView) {
+    startupItem.accept(
+        classRule -> addClassAndParentClasses(classRule.getReference(), builder, appView),
+        builder::addMethodRule);
+  }
+
+  private static boolean addClass(DexProgramClass clazz, Builder builder) {
+    int oldSize = builder.size();
+    builder.addClassRule(
+        StartupProfileClassRule.builder().setClassReference(clazz.getType()).build());
+    return builder.size() > oldSize;
+  }
+
+  private static void addClassAndParentClasses(DexType type, Builder builder, AppView<?> appView) {
+    DexProgramClass definition = appView.app().programDefinitionFor(type);
+    if (definition != null) {
+      addClassAndParentClasses(definition, builder, appView);
+    }
+  }
+
+  private static void addClassAndParentClasses(
+      DexProgramClass clazz, Builder builder, AppView<?> appView) {
+    if (addClass(clazz, builder)) {
+      addParentClasses(clazz, builder, appView);
+    }
+  }
+
+  private static void addParentClasses(DexProgramClass clazz, Builder builder, AppView<?> appView) {
+    clazz.forEachImmediateSupertype(
+        supertype -> addClassAndParentClasses(supertype, builder, appView));
+  }
+
+  @Override
+  public StartupProfile withoutMissingItems(AppView<?> appView) {
+    AppInfo appInfo = appView.appInfo();
+    return transform(
+        (classRule, builder) -> {
+          if (appInfo.hasDefinitionForWithoutExistenceAssert(classRule.getReference())) {
+            builder.addClassRule(classRule);
+          }
+        },
+        (methodRule, builder) -> {
+          DexClass clazz =
+              appInfo.definitionForWithoutExistenceAssert(
+                  methodRule.getReference().getHolderType());
+          if (methodRule.getReference().isDefinedOnClass(clazz)) {
+            builder.addMethodRule(methodRule);
+          }
+        });
+  }
+
+  @Override
+  public StartupProfile withoutPrunedItems(PrunedItems prunedItems, SyntheticItems syntheticItems) {
+    return transform(
+        (classRule, builder) -> {
+          if (!prunedItems.isRemoved(classRule.getReference())) {
+            builder.addClassRule(classRule);
+          }
+        },
+        (methodRule, builder) -> {
+          if (!prunedItems.isRemoved(methodRule.getReference())) {
+            builder.addMethodRule(methodRule);
+          }
+        });
+  }
+
+  private StartupProfile transform(
+      BiConsumer<StartupProfileClassRule, Builder> classRuleTransformer,
+      BiConsumer<StartupProfileMethodRule, Builder> methodRuleTransformer) {
+    Builder builder = builderWithCapacity(startupItems.size());
+    forEachRule(
+        classRule -> classRuleTransformer.accept(classRule, builder),
+        methodRule -> methodRuleTransformer.accept(methodRule, builder));
+    return builder.build();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupItem.java b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupItem.java
deleted file mode 100644
index b7e6f1c..0000000
--- a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupItem.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (c) 2022, 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.experimental.startup.profile;
-
-import java.util.function.Consumer;
-import java.util.function.Function;
-
-public abstract class StartupItem {
-
-  public abstract void accept(
-      Consumer<StartupClass> classConsumer,
-      Consumer<StartupMethod> methodConsumer,
-      Consumer<SyntheticStartupMethod> syntheticMethodConsumer);
-
-  public abstract <T> T apply(
-      Function<StartupClass, T> classFunction,
-      Function<StartupMethod, T> methodFunction,
-      Function<SyntheticStartupMethod, T> syntheticMethodFunction);
-
-  public boolean isStartupClass() {
-    return false;
-  }
-
-  public StartupClass asStartupClass() {
-    assert false;
-    return null;
-  }
-
-  public boolean isStartupMethod() {
-    return false;
-  }
-
-  public StartupMethod asStartupMethod() {
-    assert false;
-    return null;
-  }
-
-  public boolean isSyntheticStartupMethod() {
-    return false;
-  }
-
-  public SyntheticStartupMethod asSyntheticStartupMethod() {
-    assert false;
-    return null;
-  }
-
-  public abstract String serializeToString();
-}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupMethod.java b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupMethod.java
deleted file mode 100644
index 18045ef..0000000
--- a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupMethod.java
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright (c) 2022, 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.experimental.startup.profile;
-
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.references.MethodReference;
-import com.android.tools.r8.startup.StartupMethodBuilder;
-import com.android.tools.r8.utils.MethodReferenceUtils;
-import java.util.function.Consumer;
-import java.util.function.Function;
-
-public class StartupMethod extends StartupItem {
-
-  private final DexMethod method;
-
-  StartupMethod(DexMethod method) {
-    this.method = method;
-  }
-
-  public static Builder builder() {
-    return new Builder();
-  }
-
-  public static Builder builder(DexItemFactory dexItemFactory) {
-    return new Builder(dexItemFactory);
-  }
-
-  @Override
-  public void accept(
-      Consumer<StartupClass> classConsumer,
-      Consumer<StartupMethod> methodConsumer,
-      Consumer<SyntheticStartupMethod> syntheticMethodConsumer) {
-    methodConsumer.accept(this);
-  }
-
-  @Override
-  public <T> T apply(
-      Function<StartupClass, T> classFunction,
-      Function<StartupMethod, T> methodFunction,
-      Function<SyntheticStartupMethod, T> syntheticMethodFunction) {
-    return methodFunction.apply(this);
-  }
-
-  public DexMethod getReference() {
-    return method;
-  }
-
-  @Override
-  public boolean isStartupMethod() {
-    return true;
-  }
-
-  @Override
-  public StartupMethod asStartupMethod() {
-    return this;
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (this == o) {
-      return true;
-    }
-    if (o == null || getClass() != o.getClass()) {
-      return false;
-    }
-    StartupMethod that = (StartupMethod) o;
-    return method == that.method;
-  }
-
-  @Override
-  public int hashCode() {
-    return method.hashCode();
-  }
-
-  @Override
-  public String serializeToString() {
-    return method.toSmaliString();
-  }
-
-  public static class Builder implements StartupMethodBuilder {
-
-    private final DexItemFactory dexItemFactory;
-
-    private DexMethod method;
-
-    Builder() {
-      this(null);
-    }
-
-    Builder(DexItemFactory dexItemFactory) {
-      this.dexItemFactory = dexItemFactory;
-    }
-
-    @Override
-    public Builder setMethodReference(MethodReference classReference) {
-      assert dexItemFactory != null;
-      return setMethodReference(MethodReferenceUtils.toDexMethod(classReference, dexItemFactory));
-    }
-
-    public Builder setMethodReference(DexMethod method) {
-      this.method = method;
-      return this;
-    }
-
-    public StartupMethod build() {
-      return new StartupMethod(method);
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfile.java b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfile.java
deleted file mode 100644
index 3a63098..0000000
--- a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfile.java
+++ /dev/null
@@ -1,201 +0,0 @@
-// Copyright (c) 2022, 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.experimental.startup.profile;
-
-import com.android.tools.r8.TextInputStream;
-import com.android.tools.r8.graph.DexDefinitionSupplier;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.profile.art.ArtProfileBuilderUtils;
-import com.android.tools.r8.profile.art.ArtProfileBuilderUtils.SyntheticToSyntheticContextGeneralization;
-import com.android.tools.r8.profile.art.HumanReadableArtProfileParser;
-import com.android.tools.r8.profile.art.HumanReadableArtProfileParserBuilder;
-import com.android.tools.r8.startup.StartupClassBuilder;
-import com.android.tools.r8.startup.StartupMethodBuilder;
-import com.android.tools.r8.startup.StartupProfileBuilder;
-import com.android.tools.r8.startup.StartupProfileProvider;
-import com.android.tools.r8.startup.SyntheticStartupMethodBuilder;
-import com.android.tools.r8.startup.diagnostic.MissingStartupProfileItemsDiagnostic;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.Reporter;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.function.Consumer;
-
-public class StartupProfile {
-
-  private final LinkedHashSet<StartupItem> startupItems;
-
-  StartupProfile(LinkedHashSet<StartupItem> startupItems) {
-    this.startupItems = startupItems;
-  }
-
-  public static Builder builder(
-      InternalOptions options,
-      MissingStartupProfileItemsDiagnostic.Builder missingItemsDiagnosticBuilder,
-      StartupProfileProvider startupProfileProvider,
-      SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization) {
-    return new Builder(
-        options,
-        missingItemsDiagnosticBuilder,
-        startupProfileProvider,
-        syntheticToSyntheticContextGeneralization);
-  }
-
-  public static StartupProfile merge(Collection<StartupProfile> startupProfiles) {
-    LinkedHashSet<StartupItem> mergedStartupItems = new LinkedHashSet<>();
-    for (StartupProfile startupProfile : startupProfiles) {
-      mergedStartupItems.addAll(startupProfile.getStartupItems());
-    }
-    return new StartupProfile(mergedStartupItems);
-  }
-
-  /**
-   * Parses the supplied startup configuration, if any. The startup configuration is a list of class
-   * and method descriptors.
-   *
-   * <p>Example:
-   *
-   * <pre>
-   * Landroidx/compose/runtime/ComposerImpl;->updateValue(Ljava/lang/Object;)V
-   * Landroidx/compose/runtime/ComposerImpl;->updatedNodeCount(I)I
-   * Landroidx/compose/runtime/ComposerImpl;->validateNodeExpected()V
-   * Landroidx/compose/runtime/CompositionImpl;->applyChanges()V
-   * Landroidx/compose/runtime/ComposerKt;->findLocation(Ljava/util/List;I)I
-   * Landroidx/compose/runtime/ComposerImpl;
-   * </pre>
-   */
-  public static StartupProfile parseStartupProfile(
-      InternalOptions options,
-      DexDefinitionSupplier definitions,
-      SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization) {
-    if (!options.getStartupOptions().hasStartupProfileProviders()) {
-      return null;
-    }
-    Collection<StartupProfileProvider> startupProfileProviders =
-        options.getStartupOptions().getStartupProfileProviders();
-    List<StartupProfile> startupProfiles = new ArrayList<>(startupProfileProviders.size());
-    for (StartupProfileProvider startupProfileProvider : startupProfileProviders) {
-      MissingStartupProfileItemsDiagnostic.Builder missingItemsDiagnosticBuilder =
-          new MissingStartupProfileItemsDiagnostic.Builder(definitions)
-              .setOrigin(startupProfileProvider.getOrigin());
-      StartupProfile.Builder startupProfileBuilder =
-          StartupProfile.builder(
-              options,
-              missingItemsDiagnosticBuilder,
-              startupProfileProvider,
-              syntheticToSyntheticContextGeneralization);
-      startupProfileProvider.getStartupProfile(startupProfileBuilder);
-      startupProfiles.add(startupProfileBuilder.build());
-      if (missingItemsDiagnosticBuilder.hasMissingStartupItems()) {
-        options.reporter.warning(missingItemsDiagnosticBuilder.build());
-      }
-    }
-    return StartupProfile.merge(startupProfiles);
-  }
-
-  public Collection<StartupItem> getStartupItems() {
-    return startupItems;
-  }
-
-  public static class Builder implements StartupProfileBuilder {
-
-    private final DexItemFactory dexItemFactory;
-    private final MissingStartupProfileItemsDiagnostic.Builder missingItemsDiagnosticBuilder;
-    private Reporter reporter;
-    private final StartupProfileProvider startupProfileProvider;
-    private final SyntheticToSyntheticContextGeneralization
-        syntheticToSyntheticContextGeneralization;
-
-    private final LinkedHashSet<StartupItem> startupItems = new LinkedHashSet<>();
-
-    Builder(
-        InternalOptions options,
-        MissingStartupProfileItemsDiagnostic.Builder missingItemsDiagnosticBuilder,
-        StartupProfileProvider startupProfileProvider,
-        SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization) {
-      this.dexItemFactory = options.dexItemFactory();
-      this.missingItemsDiagnosticBuilder = missingItemsDiagnosticBuilder;
-      this.reporter = options.reporter;
-      this.startupProfileProvider = startupProfileProvider;
-      this.syntheticToSyntheticContextGeneralization = syntheticToSyntheticContextGeneralization;
-    }
-
-    @Override
-    public Builder addStartupClass(Consumer<StartupClassBuilder> startupClassBuilderConsumer) {
-      StartupClass.Builder startupClassBuilder = StartupClass.builder(dexItemFactory);
-      startupClassBuilderConsumer.accept(startupClassBuilder);
-      StartupClass startupClass = startupClassBuilder.build();
-      if (missingItemsDiagnosticBuilder.registerStartupClass(startupClass)) {
-        return this;
-      }
-      return addStartupItem(startupClass);
-    }
-
-    @Override
-    public Builder addStartupMethod(Consumer<StartupMethodBuilder> startupMethodBuilderConsumer) {
-      StartupMethod.Builder startupMethodBuilder = StartupMethod.builder(dexItemFactory);
-      startupMethodBuilderConsumer.accept(startupMethodBuilder);
-      StartupMethod startupMethod = startupMethodBuilder.build();
-      if (missingItemsDiagnosticBuilder.registerStartupMethod(startupMethod)) {
-        return this;
-      }
-      return addStartupItem(startupMethod);
-    }
-
-    @Override
-    public StartupProfileBuilder addSyntheticStartupMethod(
-        Consumer<SyntheticStartupMethodBuilder> syntheticStartupMethodBuilderConsumer) {
-      SyntheticStartupMethod.Builder syntheticStartupMethodBuilder =
-          SyntheticStartupMethod.builder(dexItemFactory);
-      syntheticStartupMethodBuilderConsumer.accept(syntheticStartupMethodBuilder);
-      SyntheticStartupMethod syntheticStartupMethod = syntheticStartupMethodBuilder.build();
-      if (missingItemsDiagnosticBuilder.registerSyntheticStartupMethod(syntheticStartupMethod)) {
-        return this;
-      }
-      return addStartupItem(syntheticStartupMethod);
-    }
-
-    @Override
-    public StartupProfileBuilder addHumanReadableArtProfile(
-        TextInputStream textInputStream,
-        Consumer<HumanReadableArtProfileParserBuilder> parserBuilderConsumer) {
-      HumanReadableArtProfileParser.Builder parserBuilder =
-          HumanReadableArtProfileParser.builder()
-              .setReporter(reporter)
-              .setProfileBuilder(
-                  ArtProfileBuilderUtils.createBuilderForArtProfileToStartupProfileConversion(
-                      this, syntheticToSyntheticContextGeneralization));
-      parserBuilderConsumer.accept(parserBuilder);
-      HumanReadableArtProfileParser parser = parserBuilder.build();
-      parser.parse(textInputStream, startupProfileProvider.getOrigin());
-      return this;
-    }
-
-    private Builder addStartupItem(StartupItem startupItem) {
-      this.startupItems.add(startupItem);
-      return this;
-    }
-
-    public Builder apply(Consumer<Builder> consumer) {
-      consumer.accept(this);
-      return this;
-    }
-
-    public Builder setIgnoreWarnings() {
-      return setReporter(null);
-    }
-
-    public Builder setReporter(Reporter reporter) {
-      this.reporter = reporter;
-      return this;
-    }
-
-    public StartupProfile build() {
-      return new StartupProfile(startupItems);
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupClass.java b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfileClassRule.java
similarity index 63%
rename from src/main/java/com/android/tools/r8/experimental/startup/profile/StartupClass.java
rename to src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfileClassRule.java
index d281a65..c474ff2 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupClass.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfileClassRule.java
@@ -6,17 +6,20 @@
 
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.profile.AbstractProfileClassRule;
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.startup.StartupClassBuilder;
 import com.android.tools.r8.utils.ClassReferenceUtils;
-import java.util.function.Consumer;
+import com.android.tools.r8.utils.ThrowingConsumer;
+import java.io.IOException;
 import java.util.function.Function;
 
-public class StartupClass extends StartupItem {
+public class StartupProfileClassRule extends StartupProfileRule
+    implements AbstractProfileClassRule {
 
   private final DexType type;
 
-  StartupClass(DexType type) {
+  StartupProfileClassRule(DexType type) {
     this.type = type;
   }
 
@@ -29,36 +32,26 @@
   }
 
   @Override
-  public void accept(
-      Consumer<StartupClass> classConsumer,
-      Consumer<StartupMethod> methodConsumer,
-      Consumer<SyntheticStartupMethod> syntheticMethodConsumer) {
+  public <E1 extends Exception, E2 extends Exception> void accept(
+      ThrowingConsumer<StartupProfileClassRule, E1> classConsumer,
+      ThrowingConsumer<StartupProfileMethodRule, E2> methodConsumer)
+      throws E1 {
     classConsumer.accept(this);
   }
 
   @Override
   public <T> T apply(
-      Function<StartupClass, T> classFunction,
-      Function<StartupMethod, T> methodFunction,
-      Function<SyntheticStartupMethod, T> syntheticMethodFunction) {
+      Function<StartupProfileClassRule, T> classFunction,
+      Function<StartupProfileMethodRule, T> methodFunction) {
     return classFunction.apply(this);
   }
 
+  @Override
   public DexType getReference() {
     return type;
   }
 
   @Override
-  public boolean isStartupClass() {
-    return true;
-  }
-
-  @Override
-  public StartupClass asStartupClass() {
-    return this;
-  }
-
-  @Override
   public boolean equals(Object o) {
     if (this == o) {
       return true;
@@ -66,7 +59,7 @@
     if (o == null || getClass() != o.getClass()) {
       return false;
     }
-    StartupClass that = (StartupClass) o;
+    StartupProfileClassRule that = (StartupProfileClassRule) o;
     return type == that.type;
   }
 
@@ -76,11 +69,12 @@
   }
 
   @Override
-  public String serializeToString() {
-    return getReference().toDescriptorString();
+  public void write(Appendable appendable) throws IOException {
+    appendable.append(getReference().toDescriptorString());
   }
 
-  public static class Builder implements StartupClassBuilder {
+  public static class Builder
+      implements AbstractProfileClassRule.Builder<StartupProfileClassRule>, StartupClassBuilder {
 
     private final DexItemFactory dexItemFactory;
 
@@ -105,8 +99,9 @@
       return this;
     }
 
-    public StartupClass build() {
-      return new StartupClass(type);
+    @Override
+    public StartupProfileClassRule build() {
+      return new StartupProfileClassRule(type);
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfileMethodRule.java b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfileMethodRule.java
new file mode 100644
index 0000000..0a3a36a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfileMethodRule.java
@@ -0,0 +1,124 @@
+// Copyright (c) 2022, 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.experimental.startup.profile;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.profile.AbstractProfileMethodRule;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.startup.StartupMethodBuilder;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.ThrowingConsumer;
+import java.io.IOException;
+import java.util.function.Function;
+
+public class StartupProfileMethodRule extends StartupProfileRule
+    implements AbstractProfileMethodRule {
+
+  private final DexMethod method;
+
+  StartupProfileMethodRule(DexMethod method) {
+    this.method = method;
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static Builder builder(DexItemFactory dexItemFactory) {
+    return new Builder(dexItemFactory);
+  }
+
+  @Override
+  public <E1 extends Exception, E2 extends Exception> void accept(
+      ThrowingConsumer<StartupProfileClassRule, E1> classConsumer,
+      ThrowingConsumer<StartupProfileMethodRule, E2> methodConsumer)
+      throws E2 {
+    methodConsumer.accept(this);
+  }
+
+  @Override
+  public <T> T apply(
+      Function<StartupProfileClassRule, T> classFunction,
+      Function<StartupProfileMethodRule, T> methodFunction) {
+    return methodFunction.apply(this);
+  }
+
+  @Override
+  public DexMethod getReference() {
+    return method;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    StartupProfileMethodRule that = (StartupProfileMethodRule) o;
+    return method == that.method;
+  }
+
+  @Override
+  public int hashCode() {
+    return method.hashCode();
+  }
+
+  @Override
+  public void write(Appendable appendable) throws IOException {
+    appendable.append(method.toSmaliString());
+  }
+
+  public static class Builder
+      implements AbstractProfileMethodRule.Builder<StartupProfileMethodRule, Builder>,
+          StartupMethodBuilder {
+
+    private final DexItemFactory dexItemFactory;
+
+    private DexMethod method;
+
+    Builder() {
+      this(null);
+    }
+
+    Builder(DexItemFactory dexItemFactory) {
+      this.dexItemFactory = dexItemFactory;
+    }
+
+    @Override
+    public boolean isGreaterThanOrEqualTo(Builder builder) {
+      return true;
+    }
+
+    @Override
+    public Builder join(Builder builder) {
+      return this;
+    }
+
+    @Override
+    public Builder join(StartupProfileMethodRule methodRule) {
+      return this;
+    }
+
+    @Override
+    public Builder setMethod(DexMethod method) {
+      this.method = method;
+      return this;
+    }
+
+    @Override
+    public Builder setMethodReference(MethodReference classReference) {
+      assert dexItemFactory != null;
+      return setMethod(MethodReferenceUtils.toDexMethod(classReference, dexItemFactory));
+    }
+
+    @Override
+    public StartupProfileMethodRule build() {
+      return new StartupProfileMethodRule(method);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfileRule.java b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfileRule.java
new file mode 100644
index 0000000..200d364
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfileRule.java
@@ -0,0 +1,33 @@
+// Copyright (c) 2022, 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.experimental.startup.profile;
+
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.profile.AbstractProfileRule;
+import com.android.tools.r8.utils.ThrowingConsumer;
+import java.io.IOException;
+import java.util.function.Function;
+
+public abstract class StartupProfileRule
+    implements AbstractProfileRule, Comparable<StartupProfileRule> {
+
+  public abstract <E1 extends Exception, E2 extends Exception> void accept(
+      ThrowingConsumer<StartupProfileClassRule, E1> classConsumer,
+      ThrowingConsumer<StartupProfileMethodRule, E2> methodConsumer)
+      throws E1, E2;
+
+  public abstract <T> T apply(
+      Function<StartupProfileClassRule, T> classFunction,
+      Function<StartupProfileMethodRule, T> methodFunction);
+
+  @Override
+  public final int compareTo(StartupProfileRule rule) {
+    return getReference().compareTo(rule.getReference());
+  }
+
+  public abstract DexReference getReference();
+
+  public abstract void write(Appendable appendable) throws IOException;
+}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/profile/SyntheticStartupMethod.java b/src/main/java/com/android/tools/r8/experimental/startup/profile/SyntheticStartupMethod.java
deleted file mode 100644
index c68a198..0000000
--- a/src/main/java/com/android/tools/r8/experimental/startup/profile/SyntheticStartupMethod.java
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright (c) 2022, 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.experimental.startup.profile;
-
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.startup.SyntheticStartupMethodBuilder;
-import com.android.tools.r8.utils.ClassReferenceUtils;
-import java.util.function.Consumer;
-import java.util.function.Function;
-
-public class SyntheticStartupMethod extends StartupItem {
-
-  private final DexType syntheticContextType;
-
-  SyntheticStartupMethod(DexType syntheticContextType) {
-    this.syntheticContextType = syntheticContextType;
-  }
-
-  public static Builder builder() {
-    return new Builder();
-  }
-
-  public static Builder builder(DexItemFactory dexItemFactory) {
-    return new Builder(dexItemFactory);
-  }
-
-  @Override
-  public void accept(
-      Consumer<StartupClass> classConsumer,
-      Consumer<StartupMethod> methodConsumer,
-      Consumer<SyntheticStartupMethod> syntheticMethodConsumer) {
-    syntheticMethodConsumer.accept(this);
-  }
-
-  @Override
-  public <T> T apply(
-      Function<StartupClass, T> classFunction,
-      Function<StartupMethod, T> methodFunction,
-      Function<SyntheticStartupMethod, T> syntheticMethodFunction) {
-    return syntheticMethodFunction.apply(this);
-  }
-
-  public DexType getSyntheticContextType() {
-    return syntheticContextType;
-  }
-
-  @Override
-  public boolean isSyntheticStartupMethod() {
-    return true;
-  }
-
-  @Override
-  public SyntheticStartupMethod asSyntheticStartupMethod() {
-    return this;
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (this == o) {
-      return true;
-    }
-    if (o == null || getClass() != o.getClass()) {
-      return false;
-    }
-    SyntheticStartupMethod that = (SyntheticStartupMethod) o;
-    return syntheticContextType == that.syntheticContextType;
-  }
-
-  @Override
-  public int hashCode() {
-    return syntheticContextType.hashCode();
-  }
-
-  @Override
-  public String serializeToString() {
-    return 'S' + syntheticContextType.toDescriptorString();
-  }
-
-  public static class Builder implements SyntheticStartupMethodBuilder {
-
-    private final DexItemFactory dexItemFactory;
-
-    private DexType syntheticContextReference;
-
-    Builder() {
-      this(null);
-    }
-
-    Builder(DexItemFactory dexItemFactory) {
-      this.dexItemFactory = dexItemFactory;
-    }
-
-    @Override
-    public Builder setSyntheticContextReference(ClassReference syntheticContextReference) {
-      assert dexItemFactory != null;
-      return setSyntheticContextReference(
-          ClassReferenceUtils.toDexType(syntheticContextReference, dexItemFactory));
-    }
-
-    public Builder setSyntheticContextReference(DexType syntheticContextReference) {
-      this.syntheticContextReference = syntheticContextReference;
-      return this;
-    }
-
-    public SyntheticStartupMethod build() {
-      return new SyntheticStartupMethod(syntheticContextReference);
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/rewriting/StartupProfileAdditions.java b/src/main/java/com/android/tools/r8/experimental/startup/rewriting/StartupProfileAdditions.java
new file mode 100644
index 0000000..2522234
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/experimental/startup/rewriting/StartupProfileAdditions.java
@@ -0,0 +1,61 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.experimental.startup.rewriting;
+
+import com.android.tools.r8.experimental.startup.StartupProfile;
+import com.android.tools.r8.experimental.startup.profile.StartupProfileClassRule;
+import com.android.tools.r8.experimental.startup.profile.StartupProfileMethodRule;
+import com.android.tools.r8.experimental.startup.profile.StartupProfileRule;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.profile.AbstractProfileRule;
+import com.android.tools.r8.profile.art.rewriting.ProfileAdditions;
+import java.util.Comparator;
+
+public class StartupProfileAdditions
+    extends ProfileAdditions<
+        StartupProfileAdditions,
+        StartupProfileClassRule,
+        StartupProfileClassRule.Builder,
+        StartupProfileMethodRule,
+        StartupProfileMethodRule.Builder,
+        StartupProfileRule,
+        StartupProfile,
+        StartupProfile.Builder> {
+
+  public StartupProfileAdditions(StartupProfile profile) {
+    super(profile);
+  }
+
+  @Override
+  public StartupProfileAdditions create() {
+    return new StartupProfileAdditions(profile);
+  }
+
+  @Override
+  public StartupProfileClassRule.Builder createClassRuleBuilder(DexType type) {
+    return StartupProfileClassRule.builder().setClassReference(type);
+  }
+
+  @Override
+  public StartupProfileMethodRule.Builder createMethodRuleBuilder(DexMethod method) {
+    return StartupProfileMethodRule.builder().setMethod(method);
+  }
+
+  @Override
+  public StartupProfile.Builder createProfileBuilder() {
+    return StartupProfile.builder();
+  }
+
+  @Override
+  public Comparator<AbstractProfileRule> getRuleComparator() {
+    return Comparator.comparing(AbstractProfileRule::asStartupProfileRule);
+  }
+
+  @Override
+  public StartupProfileAdditions self() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
index fd710bb..dc33db7 100644
--- a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
+++ b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.ProgramResource;
 import com.android.tools.r8.ProgramResourceProvider;
 import com.android.tools.r8.ResourceException;
-import com.android.tools.r8.experimental.startup.StartupOrder;
+import com.android.tools.r8.experimental.startup.StartupProfile;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -106,20 +106,17 @@
   public Map<FeatureSplit, Set<DexProgramClass>> getFeatureSplitClasses(
       Set<DexProgramClass> classes, AppView<? extends AppInfoWithClassHierarchy> appView) {
     return getFeatureSplitClasses(
-        classes,
-        appView.options(),
-        appView.appInfo().getStartupOrder(),
-        appView.getSyntheticItems());
+        classes, appView.options(), appView.getStartupProfile(), appView.getSyntheticItems());
   }
 
   public Map<FeatureSplit, Set<DexProgramClass>> getFeatureSplitClasses(
       Set<DexProgramClass> classes,
       InternalOptions options,
-      StartupOrder startupOrder,
+      StartupProfile startupProfile,
       SyntheticItems syntheticItems) {
     Map<FeatureSplit, Set<DexProgramClass>> result = new IdentityHashMap<>();
     for (DexProgramClass clazz : classes) {
-      FeatureSplit featureSplit = getFeatureSplit(clazz, options, startupOrder, syntheticItems);
+      FeatureSplit featureSplit = getFeatureSplit(clazz, options, startupProfile, syntheticItems);
       if (featureSplit != null && !featureSplit.isBase()) {
         result.computeIfAbsent(featureSplit, ignore -> Sets.newIdentityHashSet()).add(clazz);
       }
@@ -130,34 +127,31 @@
   public FeatureSplit getFeatureSplit(
       ProgramDefinition definition, AppView<? extends AppInfoWithClassHierarchy> appView) {
     return getFeatureSplit(
-        definition,
-        appView.options(),
-        appView.appInfo().getStartupOrder(),
-        appView.getSyntheticItems());
+        definition, appView.options(), appView.getStartupProfile(), appView.getSyntheticItems());
   }
 
   public FeatureSplit getFeatureSplit(
       ProgramDefinition definition,
       InternalOptions options,
-      StartupOrder startupOrder,
+      StartupProfile startupProfile,
       SyntheticItems syntheticItems) {
-    return getFeatureSplit(definition.getContextType(), options, startupOrder, syntheticItems);
+    return getFeatureSplit(definition.getContextType(), options, startupProfile, syntheticItems);
   }
 
   public FeatureSplit getFeatureSplit(
       DexType type, AppView<? extends AppInfoWithClassHierarchy> appView) {
     return getFeatureSplit(
-        type, appView.options(), appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
+        type, appView.options(), appView.getStartupProfile(), appView.getSyntheticItems());
   }
 
   public FeatureSplit getFeatureSplit(
       DexType type,
       InternalOptions options,
-      StartupOrder startupOrder,
+      StartupProfile startupProfile,
       SyntheticItems syntheticItems) {
     if (syntheticItems == null) {
       // Called from AndroidApp.dumpProgramResources().
-      assert startupOrder.isEmpty();
+      assert startupProfile.isEmpty();
       return classToFeatureSplitMap.getOrDefault(type, FeatureSplit.BASE);
     }
     FeatureSplit feature;
@@ -168,7 +162,7 @@
         // the shared utility class in case it is used during startup. The use of base startup
         // allows for merging startup classes with the shared utility class, however, which could be
         // bad for startup if the shared utility class is not used during startup.
-        return startupOrder.isEmpty()
+        return startupProfile.isEmpty()
                 || options.getStartupOptions().isStartupBoundaryOptimizationsEnabled()
             ? FeatureSplit.BASE
             : FeatureSplit.BASE_STARTUP;
@@ -181,7 +175,7 @@
       feature = classToFeatureSplitMap.getOrDefault(type, FeatureSplit.BASE);
     }
     if (feature.isBase()) {
-      return !startupOrder.contains(type, syntheticItems)
+      return !startupProfile.containsClassRule(type)
               || options.getStartupOptions().isStartupBoundaryOptimizationsEnabled()
           ? FeatureSplit.BASE
           : FeatureSplit.BASE_STARTUP;
@@ -199,15 +193,15 @@
   public boolean isInBase(
       DexProgramClass clazz, AppView<? extends AppInfoWithClassHierarchy> appView) {
     return isInBase(
-        clazz, appView.options(), appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
+        clazz, appView.options(), appView.getStartupProfile(), appView.getSyntheticItems());
   }
 
   public boolean isInBase(
       DexProgramClass clazz,
       InternalOptions options,
-      StartupOrder startupOrder,
+      StartupProfile startupProfile,
       SyntheticItems syntheticItems) {
-    return getFeatureSplit(clazz, options, startupOrder, syntheticItems).isBase();
+    return getFeatureSplit(clazz, options, startupProfile, syntheticItems).isBase();
   }
 
   public boolean isInBaseOrSameFeatureAs(
@@ -218,7 +212,7 @@
         clazz,
         context,
         appView.options(),
-        appView.appInfo().getStartupOrder(),
+        appView.getStartupProfile(),
         appView.getSyntheticItems());
   }
 
@@ -226,10 +220,10 @@
       DexProgramClass clazz,
       ProgramDefinition context,
       InternalOptions options,
-      StartupOrder startupOrder,
+      StartupProfile startupProfile,
       SyntheticItems syntheticItems) {
     return isInBaseOrSameFeatureAs(
-        clazz.getContextType(), context, options, startupOrder, syntheticItems);
+        clazz.getContextType(), context, options, startupProfile, syntheticItems);
   }
 
   public boolean isInBaseOrSameFeatureAs(
@@ -240,7 +234,7 @@
         clazz,
         context,
         appView.options(),
-        appView.appInfo().getStartupOrder(),
+        appView.getStartupProfile(),
         appView.getSyntheticItems());
   }
 
@@ -248,51 +242,51 @@
       DexType clazz,
       ProgramDefinition context,
       InternalOptions options,
-      StartupOrder startupOrder,
+      StartupProfile startupProfile,
       SyntheticItems syntheticItems) {
-    FeatureSplit split = getFeatureSplit(clazz, options, startupOrder, syntheticItems);
+    FeatureSplit split = getFeatureSplit(clazz, options, startupProfile, syntheticItems);
     return split.isBase()
-        || split == getFeatureSplit(context, options, startupOrder, syntheticItems);
+        || split == getFeatureSplit(context, options, startupProfile, syntheticItems);
   }
 
   public boolean isInFeature(
       DexProgramClass clazz,
       InternalOptions options,
-      StartupOrder startupOrder,
+      StartupProfile startupProfile,
       SyntheticItems syntheticItems) {
-    return !isInBase(clazz, options, startupOrder, syntheticItems);
+    return !isInBase(clazz, options, startupProfile, syntheticItems);
   }
 
   public boolean isInSameFeatureOrBothInSameBase(
       ProgramMethod a, ProgramMethod b, AppView<? extends AppInfoWithClassHierarchy> appView) {
     return isInSameFeatureOrBothInSameBase(
-        a, b, appView.options(), appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
+        a, b, appView.options(), appView.getStartupProfile(), appView.getSyntheticItems());
   }
 
   public boolean isInSameFeatureOrBothInSameBase(
       ProgramMethod a,
       ProgramMethod b,
       InternalOptions options,
-      StartupOrder startupOrder,
+      StartupProfile startupProfile,
       SyntheticItems syntheticItems) {
     return isInSameFeatureOrBothInSameBase(
-        a.getHolder(), b.getHolder(), options, startupOrder, syntheticItems);
+        a.getHolder(), b.getHolder(), options, startupProfile, syntheticItems);
   }
 
   public boolean isInSameFeatureOrBothInSameBase(
       DexProgramClass a, DexProgramClass b, AppView<? extends AppInfoWithClassHierarchy> appView) {
     return isInSameFeatureOrBothInSameBase(
-        a, b, appView.options(), appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
+        a, b, appView.options(), appView.getStartupProfile(), appView.getSyntheticItems());
   }
 
   public boolean isInSameFeatureOrBothInSameBase(
       DexProgramClass a,
       DexProgramClass b,
       InternalOptions options,
-      StartupOrder startupOrder,
+      StartupProfile startupProfile,
       SyntheticItems syntheticItems) {
-    return getFeatureSplit(a, options, startupOrder, syntheticItems)
-        == getFeatureSplit(b, options, startupOrder, syntheticItems);
+    return getFeatureSplit(a, options, startupProfile, syntheticItems)
+        == getFeatureSplit(b, options, startupProfile, syntheticItems);
   }
 
   public ClassToFeatureSplitMap rewrittenWithLens(GraphLens lens) {
@@ -335,9 +329,6 @@
       DexProgramClass clazz, AppView<? extends AppInfoWithClassHierarchy> appView) {
     return getMap(appView)
         .isInFeature(
-            clazz,
-            appView.options(),
-            appView.appInfo().getStartupOrder(),
-            appView.getSyntheticItems());
+            clazz, appView.options(), appView.getStartupProfile(), appView.getSyntheticItems());
   }
 }
diff --git a/src/main/java/com/android/tools/r8/features/FeatureSplitBoundaryOptimizationUtils.java b/src/main/java/com/android/tools/r8/features/FeatureSplitBoundaryOptimizationUtils.java
index 8e2fbb4..37f1494 100644
--- a/src/main/java/com/android/tools/r8/features/FeatureSplitBoundaryOptimizationUtils.java
+++ b/src/main/java/com/android/tools/r8/features/FeatureSplitBoundaryOptimizationUtils.java
@@ -5,7 +5,7 @@
 package com.android.tools.r8.features;
 
 import com.android.tools.r8.FeatureSplit;
-import com.android.tools.r8.experimental.startup.StartupOrder;
+import com.android.tools.r8.experimental.startup.StartupProfile;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedMember;
@@ -44,10 +44,10 @@
       ProgramDefinition accessor,
       ClassToFeatureSplitMap classToFeatureSplitMap,
       InternalOptions options,
-      StartupOrder startupOrder,
+      StartupProfile startupProfile,
       SyntheticItems syntheticItems) {
     return classToFeatureSplitMap.isInBaseOrSameFeatureAs(
-        accessedClass, accessor, options, startupOrder, syntheticItems);
+        accessedClass, accessor, options, startupProfile, syntheticItems);
   }
 
   public static boolean isSafeForInlining(
@@ -66,34 +66,32 @@
     }
 
     // Next perform startup checks.
-    StartupOrder startupOrder = appView.appInfo().getStartupOrder();
-    SyntheticItems syntheticItems = appView.getSyntheticItems();
-    OptionalBool callerIsStartupMethod = isStartupMethod(caller, startupOrder, syntheticItems);
+    StartupProfile startupProfile = appView.getStartupProfile();
+    OptionalBool callerIsStartupMethod = isStartupMethod(caller, startupProfile);
     if (callerIsStartupMethod.isTrue()) {
       // If the caller is a startup method, then only allow inlining if the callee is also a startup
       // method.
-      if (isStartupMethod(callee, startupOrder, syntheticItems).isFalse()) {
+      if (isStartupMethod(callee, startupProfile).isFalse()) {
         return false;
       }
     } else if (callerIsStartupMethod.isFalse()) {
       // If the caller is not a startup method, then only allow inlining if the caller is not a
       // startup class or the callee is a startup class.
-      if (startupOrder.contains(caller.getHolderType(), syntheticItems)
-          && !startupOrder.contains(callee.getHolderType(), syntheticItems)) {
+      if (startupProfile.containsClassRule(caller.getHolderType())
+          && !startupProfile.containsClassRule(callee.getHolderType())) {
         return false;
       }
     }
     return true;
   }
 
-  private static OptionalBool isStartupMethod(
-      ProgramMethod method, StartupOrder startupOrder, SyntheticItems syntheticItems) {
+  private static OptionalBool isStartupMethod(ProgramMethod method, StartupProfile startupProfile) {
     if (method.getDefinition().isD8R8Synthesized()) {
       // Due to inadequate rewriting of the startup list during desugaring, we do not give an
       // accurate result in this case.
       return OptionalBool.unknown();
     }
-    return OptionalBool.of(startupOrder.contains(method.getReference(), syntheticItems));
+    return OptionalBool.of(startupProfile.containsMethodRule(method.getReference()));
   }
 
   public static boolean isSafeForVerticalClassMerging(
@@ -115,10 +113,9 @@
 
     // If the source class is a startup class then require that the target class is also a startup
     // class.
-    StartupOrder startupOrder = appView.appInfo().getStartupOrder();
-    SyntheticItems syntheticItems = appView.getSyntheticItems();
-    if (startupOrder.contains(sourceClass.getType(), syntheticItems)
-        && !startupOrder.contains(targetClass.getType(), syntheticItems)) {
+    StartupProfile startupProfile = appView.getStartupProfile();
+    if (startupProfile.containsClassRule(sourceClass.getType())
+        && !startupProfile.containsClassRule(targetClass.getType())) {
       return false;
     }
     return true;
diff --git a/src/main/java/com/android/tools/r8/graph/AccessControl.java b/src/main/java/com/android/tools/r8/graph/AccessControl.java
index 9211db6..ecb16404 100644
--- a/src/main/java/com/android/tools/r8/graph/AccessControl.java
+++ b/src/main/java/com/android/tools/r8/graph/AccessControl.java
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
-import com.android.tools.r8.experimental.startup.StartupOrder;
+import com.android.tools.r8.experimental.startup.StartupProfile;
 import com.android.tools.r8.features.ClassToFeatureSplitMap;
 import com.android.tools.r8.features.FeatureSplitBoundaryOptimizationUtils;
 import com.android.tools.r8.synthesis.SyntheticItems;
@@ -27,7 +27,7 @@
         context,
         appView.appInfo().getClassToFeatureSplitMap(),
         appView.options(),
-        appView.appInfo().getStartupOrder(),
+        appView.getStartupProfile(),
         appView.getSyntheticItems());
   }
 
@@ -36,7 +36,7 @@
       Definition context,
       ClassToFeatureSplitMap classToFeatureSplitMap,
       InternalOptions options,
-      StartupOrder startupOrder,
+      StartupProfile startupProfile,
       SyntheticItems syntheticItems) {
     if (!clazz.isPublic() && !clazz.getType().isSamePackage(context.getContextType())) {
       return OptionalBool.FALSE;
@@ -48,7 +48,7 @@
             context.asProgramDefinition(),
             classToFeatureSplitMap,
             options,
-            startupOrder,
+            startupProfile,
             syntheticItems)) {
       return OptionalBool.UNKNOWN;
     }
@@ -58,37 +58,40 @@
   /** Intentionally package-private, use {@link MemberResolutionResult#isAccessibleFrom}. */
   static OptionalBool isMemberAccessible(
       SuccessfulMemberResolutionResult<?, ?> resolutionResult,
-      ProgramDefinition context,
+      Definition context,
+      AppView<?> appView,
       AppInfoWithClassHierarchy appInfo) {
     return isMemberAccessible(
         resolutionResult.getResolutionPair(),
         resolutionResult.getInitialResolutionHolder(),
-        context.getContextClass(),
+        context,
+        appView,
         appInfo);
   }
 
   public static OptionalBool isMemberAccessible(
       DexClassAndMember<?, ?> member,
-      DexClass initialResolutionHolder,
-      ProgramDefinition context,
+      Definition initialResolutionContext,
+      Definition context,
       AppView<? extends AppInfoWithClassHierarchy> appView) {
     return isMemberAccessible(
-        member, initialResolutionHolder, context.getContextClass(), appView.appInfo());
+        member, initialResolutionContext, context, appView, appView.appInfo());
   }
 
   static OptionalBool isMemberAccessible(
       DexClassAndMember<?, ?> member,
-      DexClass initialResolutionHolder,
-      DexClass context,
+      Definition initialResolutionContext,
+      Definition context,
+      AppView<?> appView,
       AppInfoWithClassHierarchy appInfo) {
     AccessFlags<?> memberFlags = member.getDefinition().getAccessFlags();
     OptionalBool classAccessibility =
         isClassAccessible(
-            initialResolutionHolder,
+            initialResolutionContext.getContextClass(),
             context,
             appInfo.getClassToFeatureSplitMap(),
             appInfo.options(),
-            appInfo.getStartupOrder(),
+            appView.getStartupProfile(),
             appInfo.getSyntheticItems());
     if (classAccessibility.isFalse()) {
       return OptionalBool.FALSE;
@@ -97,15 +100,16 @@
       return classAccessibility;
     }
     if (memberFlags.isPrivate()) {
-      if (!isNestMate(member.getHolder(), context)) {
+      if (!isNestMate(member.getHolder(), context.getContextClass())) {
         return OptionalBool.FALSE;
       }
       return classAccessibility;
     }
-    if (member.getHolderType().isSamePackage(context.getType())) {
+    if (member.getHolderType().isSamePackage(context.getContextType())) {
       return classAccessibility;
     }
-    if (memberFlags.isProtected() && appInfo.isSubtype(context.getType(), member.getHolderType())) {
+    if (memberFlags.isProtected()
+        && appInfo.isSubtype(context.getContextType(), member.getHolderType())) {
       return classAccessibility;
     }
     return OptionalBool.FALSE;
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
index 1536cd8..e834153 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -7,7 +7,6 @@
 import static com.android.tools.r8.utils.TraversalContinuation.doBreak;
 import static com.android.tools.r8.utils.TraversalContinuation.doContinue;
 
-import com.android.tools.r8.experimental.startup.StartupOrder;
 import com.android.tools.r8.features.ClassToFeatureSplitMap;
 import com.android.tools.r8.ir.analysis.type.InterfaceCollection;
 import com.android.tools.r8.ir.analysis.type.InterfaceCollection.Builder;
@@ -50,18 +49,15 @@
       DexApplication application,
       ClassToFeatureSplitMap classToFeatureSplitMap,
       MainDexInfo mainDexInfo,
-      GlobalSyntheticsStrategy globalSyntheticsStrategy,
-      StartupOrder startupOrder) {
+      GlobalSyntheticsStrategy globalSyntheticsStrategy) {
     return new AppInfoWithClassHierarchy(
         SyntheticItems.createInitialSyntheticItems(application, globalSyntheticsStrategy),
         classToFeatureSplitMap,
         mainDexInfo,
-        MissingClasses.empty(),
-        startupOrder);
+        MissingClasses.empty());
   }
 
   private final ClassToFeatureSplitMap classToFeatureSplitMap;
-  private final StartupOrder startupOrder;
 
   /** Set of types that are mentioned in the program, but for which no definition exists. */
   // TODO(b/175659048): Consider hoisting to AppInfo to allow using MissingClasses in D8 desugar.
@@ -72,12 +68,10 @@
       CommittedItems committedItems,
       ClassToFeatureSplitMap classToFeatureSplitMap,
       MainDexInfo mainDexInfo,
-      MissingClasses missingClasses,
-      StartupOrder startupOrder) {
+      MissingClasses missingClasses) {
     super(committedItems, mainDexInfo);
     this.classToFeatureSplitMap = classToFeatureSplitMap;
     this.missingClasses = missingClasses;
-    this.startupOrder = startupOrder;
   }
 
   // For desugaring.
@@ -87,7 +81,6 @@
     // TODO(b/175659048): Migrate the reporting of missing classes in D8 desugar to MissingClasses,
     //  and use the missing classes from AppInfo instead of MissingClasses.empty().
     this.missingClasses = MissingClasses.empty();
-    this.startupOrder = StartupOrder.empty();
   }
 
   public static AppInfoWithClassHierarchy createForDesugaring(AppInfo appInfo) {
@@ -97,11 +90,7 @@
 
   public final AppInfoWithClassHierarchy rebuildWithClassHierarchy(CommittedItems commit) {
     return new AppInfoWithClassHierarchy(
-        commit,
-        getClassToFeatureSplitMap(),
-        getMainDexInfo(),
-        getMissingClasses(),
-        getStartupOrder());
+        commit, getClassToFeatureSplitMap(), getMainDexInfo(), getMissingClasses());
   }
 
   public AppInfoWithClassHierarchy rebuildWithClassHierarchy(
@@ -111,8 +100,7 @@
         getSyntheticItems().commit(fn.apply(app())),
         getClassToFeatureSplitMap(),
         getMainDexInfo(),
-        getMissingClasses(),
-        getStartupOrder());
+        getMissingClasses());
   }
 
   @Override
@@ -123,8 +111,7 @@
         getSyntheticItems().commit(app()),
         getClassToFeatureSplitMap(),
         mainDexInfo,
-        getMissingClasses(),
-        getStartupOrder());
+        getMissingClasses());
   }
 
   @Override
@@ -140,8 +127,7 @@
         getSyntheticItems().commitPrunedItems(prunedItems),
         getClassToFeatureSplitMap().withoutPrunedItems(prunedItems),
         getMainDexInfo().withoutPrunedItems(prunedItems),
-        getMissingClasses(),
-        getStartupOrder().withoutPrunedItems(prunedItems, getSyntheticItems()));
+        getMissingClasses());
   }
 
   public ClassToFeatureSplitMap getClassToFeatureSplitMap() {
@@ -152,10 +138,6 @@
     return missingClasses;
   }
 
-  public StartupOrder getStartupOrder() {
-    return startupOrder;
-  }
-
   @Override
   public boolean hasClassHierarchy() {
     assert checkIfObsolete();
@@ -621,14 +603,37 @@
    * @return The actual target for {@code method} or {@code null} if none found.
    */
   // TODO(b/155968472): This should take a parameter `boolean isInterface` and use resolveMethod().
-  public DexEncodedMethod lookupStaticTarget(DexMethod method, DexProgramClass context) {
+  public DexEncodedMethod lookupStaticTarget(
+      DexMethod method,
+      DexProgramClass context,
+      AppView<? extends AppInfoWithClassHierarchy> appView) {
+    return lookupStaticTarget(method, context, appView, appView.appInfo());
+  }
+
+  public DexEncodedMethod lookupStaticTarget(
+      DexMethod method,
+      DexProgramClass context,
+      AppView<?> appView,
+      AppInfoWithClassHierarchy appInfo) {
     assert checkIfObsolete();
-    return unsafeResolveMethodDueToDexFormatLegacy(method).lookupInvokeStaticTarget(context, this);
+    return unsafeResolveMethodDueToDexFormatLegacy(method)
+        .lookupInvokeStaticTarget(context, appView, appInfo);
   }
 
   // TODO(b/155968472): This should take a parameter `boolean isInterface` and use resolveMethod().
-  public DexEncodedMethod lookupStaticTarget(DexMethod method, ProgramMethod context) {
-    return lookupStaticTarget(method, context.getHolder());
+  public DexEncodedMethod lookupStaticTarget(
+      DexMethod method,
+      ProgramMethod context,
+      AppView<? extends AppInfoWithClassHierarchy> appView) {
+    return lookupStaticTarget(method, context.getHolder(), appView);
+  }
+
+  public DexEncodedMethod lookupStaticTarget(
+      DexMethod method,
+      ProgramMethod context,
+      AppView<?> appView,
+      AppInfoWithClassHierarchy appInfo) {
+    return lookupStaticTarget(method, context.getHolder(), appView, appInfo);
   }
 
   /**
@@ -642,14 +647,37 @@
    * @return The actual target for {@code method} or {@code null} if none found.
    */
   // TODO(b/155968472): This should take a parameter `boolean isInterface` and use resolveMethod().
-  public DexClassAndMethod lookupSuperTarget(DexMethod method, DexProgramClass context) {
+  public DexClassAndMethod lookupSuperTarget(
+      DexMethod method,
+      DexProgramClass context,
+      AppView<? extends AppInfoWithClassHierarchy> appView) {
+    return lookupSuperTarget(method, context, appView, appView.appInfo());
+  }
+
+  public DexClassAndMethod lookupSuperTarget(
+      DexMethod method,
+      DexProgramClass context,
+      AppView<?> appView,
+      AppInfoWithClassHierarchy appInfo) {
     assert checkIfObsolete();
-    return unsafeResolveMethodDueToDexFormatLegacy(method).lookupInvokeSuperTarget(context, this);
+    return unsafeResolveMethodDueToDexFormatLegacy(method)
+        .lookupInvokeSuperTarget(context, appView, appInfo);
   }
 
   // TODO(b/155968472): This should take a parameter `boolean isInterface` and use resolveMethod().
-  public DexClassAndMethod lookupSuperTarget(DexMethod method, ProgramMethod context) {
-    return lookupSuperTarget(method, context.getHolder());
+  public final DexClassAndMethod lookupSuperTarget(
+      DexMethod method,
+      ProgramMethod context,
+      AppView<? extends AppInfoWithClassHierarchy> appView) {
+    return lookupSuperTarget(method, context, appView, appView.appInfo());
+  }
+
+  public final DexClassAndMethod lookupSuperTarget(
+      DexMethod method,
+      ProgramMethod context,
+      AppView<?> appView,
+      AppInfoWithClassHierarchy appInfo) {
+    return lookupSuperTarget(method, context.getHolder(), appView, appInfo);
   }
 
   /**
@@ -661,14 +689,37 @@
    * @return The actual target for {@code method} or {@code null} if none found.
    */
   // TODO(b/155968472): This should take a parameter `boolean isInterface` and use resolveMethod().
-  public DexEncodedMethod lookupDirectTarget(DexMethod method, DexProgramClass context) {
+  public DexEncodedMethod lookupDirectTarget(
+      DexMethod method,
+      DexProgramClass context,
+      AppView<? extends AppInfoWithClassHierarchy> appView) {
+    return lookupDirectTarget(method, context, appView, appView.appInfo());
+  }
+
+  public DexEncodedMethod lookupDirectTarget(
+      DexMethod method,
+      DexProgramClass context,
+      AppView<?> appView,
+      AppInfoWithClassHierarchy appInfo) {
     assert checkIfObsolete();
-    return unsafeResolveMethodDueToDexFormatLegacy(method).lookupInvokeDirectTarget(context, this);
+    return unsafeResolveMethodDueToDexFormatLegacy(method)
+        .lookupInvokeDirectTarget(context, appView, appInfo);
   }
 
   // TODO(b/155968472): This should take a parameter `boolean isInterface` and use resolveMethod().
-  public DexEncodedMethod lookupDirectTarget(DexMethod method, ProgramMethod context) {
-    return lookupDirectTarget(method, context.getHolder());
+  public DexEncodedMethod lookupDirectTarget(
+      DexMethod method,
+      ProgramMethod context,
+      AppView<? extends AppInfoWithClassHierarchy> appView) {
+    return lookupDirectTarget(method, context, appView, appView.appInfo());
+  }
+
+  public DexEncodedMethod lookupDirectTarget(
+      DexMethod method,
+      ProgramMethod context,
+      AppView<?> appView,
+      AppInfoWithClassHierarchy appInfo) {
+    return lookupDirectTarget(method, context.getHolder(), appView, appInfo);
   }
 
   /**
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 9560129..92848aa 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -9,7 +9,7 @@
 import com.android.tools.r8.contexts.CompilationContext;
 import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
 import com.android.tools.r8.errors.dontwarn.DontWarnConfiguration;
-import com.android.tools.r8.experimental.startup.StartupOrder;
+import com.android.tools.r8.experimental.startup.StartupProfile;
 import com.android.tools.r8.features.ClassToFeatureSplitMap;
 import com.android.tools.r8.graph.DexValue.DexValueString;
 import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
@@ -87,6 +87,8 @@
   private ProguardCompatibilityActions proguardCompatibilityActions;
   private RootSet rootSet;
   private MainDexRootSet mainDexRootSet = null;
+  private StartupProfile startupProfile;
+
   // This should preferably always be obtained via AppInfoWithLiveness.
   // Currently however the liveness may be downgraded thus loosing the computed keep info.
   private KeepInfoCollection keepInfo = null;
@@ -138,14 +140,22 @@
   private AppView(
       T appInfo,
       ArtProfileCollection artProfileCollection,
+      StartupProfile startupProfile,
       WholeProgramOptimizations wholeProgramOptimizations,
       TypeRewriter mapper) {
-    this(appInfo, artProfileCollection, wholeProgramOptimizations, mapper, Timing.empty());
+    this(
+        appInfo,
+        artProfileCollection,
+        startupProfile,
+        wholeProgramOptimizations,
+        mapper,
+        Timing.empty());
   }
 
   private AppView(
       T appInfo,
       ArtProfileCollection artProfileCollection,
+      StartupProfile startupProfile,
       WholeProgramOptimizations wholeProgramOptimizations,
       TypeRewriter mapper,
       Timing timing) {
@@ -155,6 +165,7 @@
         timing.time(
             "Compilation context", () -> CompilationContext.createInitialContext(options()));
     this.artProfileCollection = artProfileCollection;
+    this.startupProfile = startupProfile;
     this.dontWarnConfiguration =
         timing.time(
             "Dont warn config",
@@ -200,6 +211,7 @@
     return new AppView<>(
         appInfo,
         ArtProfileCollection.createInitialArtProfileCollection(appInfo, appInfo.options()),
+        StartupProfile.empty(),
         WholeProgramOptimizations.OFF,
         defaultTypeRewriter(appInfo));
   }
@@ -208,6 +220,7 @@
     return new AppView<>(
         appInfo,
         ArtProfileCollection.empty(),
+        StartupProfile.empty(),
         WholeProgramOptimizations.OFF,
         defaultTypeRewriter(appInfo));
   }
@@ -217,6 +230,7 @@
     return new AppView<>(
         appInfo,
         ArtProfileCollection.createInitialArtProfileCollection(appInfo, appInfo.options()),
+        StartupProfile.empty(),
         WholeProgramOptimizations.OFF,
         mapper,
         timing);
@@ -230,17 +244,16 @@
       DexApplication application, MainDexInfo mainDexInfo) {
     ClassToFeatureSplitMap classToFeatureSplitMap =
         ClassToFeatureSplitMap.createInitialClassToFeatureSplitMap(application.options);
-    StartupOrder startupOrder = StartupOrder.createInitialStartupOrderForR8(application);
     AppInfoWithClassHierarchy appInfo =
         AppInfoWithClassHierarchy.createInitialAppInfoWithClassHierarchy(
             application,
             classToFeatureSplitMap,
             mainDexInfo,
-            GlobalSyntheticsStrategy.forSingleOutputMode(),
-            startupOrder);
+            GlobalSyntheticsStrategy.forSingleOutputMode());
     return new AppView<>(
         appInfo,
         ArtProfileCollection.createInitialArtProfileCollection(appInfo, appInfo.options()),
+        StartupProfile.createInitialStartupProfileForR8(application),
         WholeProgramOptimizations.ON,
         defaultTypeRewriter(appInfo));
   }
@@ -249,6 +262,7 @@
     return new AppView<>(
         appInfo,
         ArtProfileCollection.createInitialArtProfileCollection(appInfo, appInfo.options()),
+        StartupProfile.empty(),
         WholeProgramOptimizations.OFF,
         mapper);
   }
@@ -257,6 +271,7 @@
     return new AppView<>(
         appInfo,
         ArtProfileCollection.empty(),
+        StartupProfile.empty(),
         WholeProgramOptimizations.OFF,
         defaultTypeRewriter(appInfo));
   }
@@ -266,6 +281,7 @@
     return new AppView<>(
         appInfo,
         ArtProfileCollection.empty(),
+        StartupProfile.empty(),
         WholeProgramOptimizations.ON,
         defaultTypeRewriter(appInfo));
   }
@@ -361,6 +377,14 @@
     this.artProfileCollection = artProfileCollection;
   }
 
+  public StartupProfile getStartupProfile() {
+    return startupProfile;
+  }
+
+  public void setStartupProfile(StartupProfile startupProfile) {
+    this.startupProfile = startupProfile;
+  }
+
   public AssumeInfoCollection getAssumeInfoCollection() {
     return assumeInfoCollection;
   }
@@ -827,6 +851,7 @@
     if (hasRootSet()) {
       rootSet.pruneItems(prunedItems);
     }
+    setStartupProfile(getStartupProfile().withoutPrunedItems(prunedItems, getSyntheticItems()));
     if (hasMainDexRootSet()) {
       setMainDexRootSet(mainDexRootSet.withoutPrunedItems(prunedItems));
     }
@@ -946,6 +971,7 @@
           if (appView.hasRootSet()) {
             appView.setRootSet(appView.rootSet().rewrittenWithLens(lens));
           }
+          appView.setStartupProfile(appView.getStartupProfile().rewrittenWithLens(lens));
         });
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
index e24a22c..f72877a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
@@ -62,7 +62,7 @@
   private static final int UNKNOWN_API_LEVEL = -1;
   private static final int NOT_SET_API_LEVEL = -2;
 
-  private static void specify(StructuralSpecification<DexAnnotation, ?> spec) {
+  protected static void specify(StructuralSpecification<DexAnnotation, ?> spec) {
     spec.withItem(a -> a.annotation).withInt(a -> a.visibility);
   }
 
@@ -71,6 +71,14 @@
     this.annotation = annotation;
   }
 
+  public boolean isTypeAnnotation() {
+    return false;
+  }
+
+  public DexTypeAnnotation asTypeAnnotation() {
+    return null;
+  }
+
   @Override
   public DexAnnotation self() {
     return this;
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
index bcddd66..1cd2f4b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
@@ -81,6 +81,8 @@
   public static DexType findDuplicateEntryType(List<DexAnnotation> annotations) {
     Set<DexType> seenTypes = Sets.newIdentityHashSet();
     for (DexAnnotation annotation : annotations) {
+      // This is only reachable from DEX where type annotations are not supported.
+      assert !annotation.isTypeAnnotation();
       if (!seenTypes.add(annotation.annotation.type)) {
         return annotation.annotation.type;
       }
diff --git a/src/main/java/com/android/tools/r8/graph/DexTypeAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexTypeAnnotation.java
new file mode 100644
index 0000000..99dadb8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexTypeAnnotation.java
@@ -0,0 +1,70 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.utils.structural.StructuralMapping;
+import org.objectweb.asm.TypePath;
+
+public class DexTypeAnnotation extends DexAnnotation {
+
+  private final int typeRef;
+  private final TypePath typePath;
+
+  public DexTypeAnnotation(
+      int visibility, DexEncodedAnnotation annotation, int typeRef, TypePath typePath) {
+    super(visibility, annotation);
+    this.typeRef = typeRef;
+    this.typePath = typePath;
+  }
+
+  @Override
+  public boolean isTypeAnnotation() {
+    return true;
+  }
+
+  @Override
+  public DexTypeAnnotation asTypeAnnotation() {
+    return this;
+  }
+
+  @Override
+  public void collectIndexedItems(AppView<?> appView, IndexedItemCollection indexedItems) {
+    throw new Unreachable("Should not collect type annotation in DEX");
+  }
+
+  @Override
+  void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+    throw new Unreachable("Should not collect type annotation in DEX");
+  }
+
+  public int getTypeRef() {
+    return typeRef;
+  }
+
+  public TypePath getTypePath() {
+    return typePath;
+  }
+
+  @Override
+  public StructuralMapping<DexAnnotation> getStructuralMapping() {
+    return spec ->
+        spec.withInt(t -> typeRef)
+            .withIntArray(
+                annotation -> {
+                  int totalCount = typePath.getLength() * 2;
+                  int[] serializedArr = new int[totalCount];
+                  for (int i = 0; i < totalCount; i++) {
+                    int startIndex = i * 2;
+                    serializedArr[startIndex] = typePath.getStep(i);
+                    serializedArr[startIndex + 1] = typePath.getStepArgument(i);
+                  }
+                  return serializedArr;
+                })
+            .withSpec(DexAnnotation::specify);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java b/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
index 196864a..9a6d7cb 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
@@ -197,8 +197,8 @@
 
     @Override
     public OptionalBool isAccessibleFrom(
-        ProgramDefinition context, AppInfoWithClassHierarchy appInfo) {
-      return AccessControl.isMemberAccessible(this, context, appInfo);
+        ProgramDefinition context, AppView<?> appView, AppInfoWithClassHierarchy appInfo) {
+      return AccessControl.isMemberAccessible(this, context, appView, appInfo);
     }
 
     @Override
@@ -382,7 +382,7 @@
 
     @Override
     public OptionalBool isAccessibleFrom(
-        ProgramDefinition context, AppInfoWithClassHierarchy appInfo) {
+        ProgramDefinition context, AppView<?> appView, AppInfoWithClassHierarchy appInfo) {
       throw new Unimplemented("Should not be called on MultipleFieldResolutionResult");
     }
 
@@ -456,7 +456,7 @@
 
     @Override
     public OptionalBool isAccessibleFrom(
-        ProgramDefinition context, AppInfoWithClassHierarchy appInfo) {
+        ProgramDefinition context, AppView<?> appView, AppInfoWithClassHierarchy appInfo) {
       return OptionalBool.FALSE;
     }
 
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index 76ca9ac..368ffea 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -56,6 +56,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
 import java.util.function.Consumer;
 import java.util.zip.CRC32;
 import org.objectweb.asm.AnnotationVisitor;
@@ -175,22 +176,46 @@
     return MethodAccessFlags.fromCfAccessFlags(cleanAccessFlags(access), isConstructor);
   }
 
-  private static AnnotationVisitor createAnnotationVisitor(String desc, boolean visible,
+  private static AnnotationVisitor createAnnotationVisitor(
+      String desc,
+      boolean visible,
       List<DexAnnotation> annotations,
-      JarApplicationReader application) {
-    assert annotations != null;
+      JarApplicationReader application,
+      BiFunction<Integer, DexEncodedAnnotation, DexAnnotation> newAnnotationConstructor) {
+    assert newAnnotationConstructor != null;
     if (visible || retainCompileTimeAnnotation(desc, application)) {
-      int visiblity = visible ? DexAnnotation.VISIBILITY_RUNTIME : DexAnnotation.VISIBILITY_BUILD;
+      int visibility = visible ? DexAnnotation.VISIBILITY_RUNTIME : DexAnnotation.VISIBILITY_BUILD;
       return new CreateAnnotationVisitor(
           application,
           (names, values) ->
               annotations.add(
-                  new DexAnnotation(
-                      visiblity, createEncodedAnnotation(desc, names, values, application))));
+                  newAnnotationConstructor.apply(
+                      visibility, createEncodedAnnotation(desc, names, values, application))));
     }
     return null;
   }
 
+  private static AnnotationVisitor createTypeAnnotationVisitor(
+      String desc,
+      boolean visible,
+      List<DexAnnotation> annotations,
+      JarApplicationReader application,
+      int typeRef,
+      TypePath typePath) {
+    assert annotations != null;
+    // Java 8 type annotations are not supported by Dex. Ignore them.
+    if (!application.options.isGeneratingClassFiles()) {
+      return null;
+    }
+    return createAnnotationVisitor(
+        desc,
+        visible,
+        annotations,
+        application,
+        (visibility, annotation) ->
+            new DexTypeAnnotation(visibility, annotation, typeRef, typePath));
+  }
+
   private static boolean retainCompileTimeAnnotation(
       String desc, JarApplicationReader application) {
     return DexAnnotation.retainCompileTimeAnnotation(
@@ -446,14 +471,15 @@
 
     @Override
     public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
-      return createAnnotationVisitor(desc, visible, getAnnotations(), application);
+      return createAnnotationVisitor(
+          desc, visible, getAnnotations(), application, DexAnnotation::new);
     }
 
     @Override
     public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc,
         boolean visible) {
-      // Java 8 type annotations are not supported by Dex, thus ignore them.
-      return null;
+      return createTypeAnnotationVisitor(
+          desc, visible, getAnnotations(), application, typeRef, typePath);
     }
 
     @Override
@@ -674,14 +700,15 @@
 
     @Override
     public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
-      return createAnnotationVisitor(desc, visible, getAnnotations(), parent.application);
+      return createAnnotationVisitor(
+          desc, visible, getAnnotations(), parent.application, DexAnnotation::new);
     }
 
     @Override
     public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc,
         boolean visible) {
-      // Java 8 type annotations are not supported by Dex, thus ignore them.
-      return null;
+      return createTypeAnnotationVisitor(
+          desc, visible, getAnnotations(), parent.application, typeRef, typePath);
     }
 
     @Override
@@ -815,7 +842,8 @@
 
     @Override
     public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
-      return createAnnotationVisitor(desc, visible, getAnnotations(), parent.application);
+      return createAnnotationVisitor(
+          desc, visible, getAnnotations(), parent.application, DexAnnotation::new);
     }
 
     @Override
@@ -827,13 +855,6 @@
     }
 
     @Override
-    public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc,
-        boolean visible) {
-      // Java 8 type annotations are not supported by Dex, thus ignore them.
-      return null;
-    }
-
-    @Override
     public void visitAnnotableParameterCount(int parameterCount, boolean visible) {
       if (annotableParameterCount != -1) {
         // TODO(113565942): We assume that the runtime visible and runtime invisible parameter
@@ -856,28 +877,42 @@
         }
       }
       assert mv == null;
-      return createAnnotationVisitor(desc, visible,
-          parameterAnnotationsLists.get(parameter), parent.application);
+      return createAnnotationVisitor(
+          desc,
+          visible,
+          parameterAnnotationsLists.get(parameter),
+          parent.application,
+          DexAnnotation::new);
+    }
+
+    @Override
+    public AnnotationVisitor visitTypeAnnotation(
+        int typeRef, TypePath typePath, String desc, boolean visible) {
+      return createTypeAnnotationVisitor(
+          desc, visible, getAnnotations(), parent.application, typeRef, typePath);
     }
 
     @Override
     public AnnotationVisitor visitInsnAnnotation(int typeRef, TypePath typePath, String desc,
         boolean visible) {
-      // Java 8 type annotations are not supported by Dex, thus ignore them.
+      // We do not support code type annotations since that would require us to maintain these
+      // through IR where we may as well invalidate any assumptions made on the code.
       return null;
     }
 
     @Override
     public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath,
         Label[] start, Label[] end, int[] index, String desc, boolean visible) {
-      // Java 8 type annotations are not supported by Dex, thus ignore them.
+      // We do not support code type annotations since that would require us to maintain these
+      // through IR where we may as well invalidate any assumptions made on the code.
       return null;
     }
 
     @Override
     public AnnotationVisitor visitTryCatchAnnotation(int typeRef, TypePath typePath, String desc,
         boolean visible) {
-      // Java 8 type annotations are not supported by Dex, thus ignore them.
+      // We do not support code type annotations since that would require us to maintain these
+      // through IR where we may as well invalidate any assumptions made on the code.
       return null;
     }
 
diff --git a/src/main/java/com/android/tools/r8/graph/MemberResolutionResult.java b/src/main/java/com/android/tools/r8/graph/MemberResolutionResult.java
index e5467ff..47f8a33 100644
--- a/src/main/java/com/android/tools/r8/graph/MemberResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/MemberResolutionResult.java
@@ -17,14 +17,14 @@
 
   public abstract SuccessfulMemberResolutionResult<D, R> asSuccessfulMemberResolutionResult();
 
-  public abstract OptionalBool isAccessibleFrom(
-      ProgramDefinition context, AppInfoWithClassHierarchy appInfo);
-
   public final OptionalBool isAccessibleFrom(
       ProgramDefinition context, AppView<? extends AppInfoWithClassHierarchy> appView) {
-    return isAccessibleFrom(context, appView.appInfo());
+    return isAccessibleFrom(context, appView, appView.appInfo());
   }
 
+  public abstract OptionalBool isAccessibleFrom(
+      ProgramDefinition context, AppView<?> appView, AppInfoWithClassHierarchy appInfo);
+
   /**
    * Returns true if resolution failed.
    *
diff --git a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
index ef0d7ff..f2a07b1 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
@@ -75,7 +75,13 @@
     return false;
   }
 
-  public boolean isNoSuchMethodErrorResult(DexClass context, AppInfoWithClassHierarchy appInfo) {
+  public final boolean isNoSuchMethodErrorResult(
+      DexClass context, AppView<? extends AppInfoWithClassHierarchy> appView) {
+    return isNoSuchMethodErrorResult(context, appView, appView.appInfo());
+  }
+
+  public boolean isNoSuchMethodErrorResult(
+      DexClass context, AppView<?> appView, AppInfoWithClassHierarchy appInfo) {
     return false;
   }
 
@@ -83,7 +89,13 @@
     return false;
   }
 
-  public boolean isIllegalAccessErrorResult(DexClass context, AppInfoWithClassHierarchy appInfo) {
+  public final boolean isIllegalAccessErrorResult(
+      DexClass context, AppView<? extends AppInfoWithClassHierarchy> appView) {
+    return isIllegalAccessErrorResult(context, appView, appView.appInfo());
+  }
+
+  public boolean isIllegalAccessErrorResult(
+      DexClass context, AppView<?> appView, AppInfoWithClassHierarchy appInfo) {
     return false;
   }
 
@@ -139,41 +151,57 @@
   }
 
   public abstract OptionalBool isAccessibleForVirtualDispatchFrom(
-      ProgramDefinition context, AppInfoWithClassHierarchy appInfo);
+      ProgramDefinition context, AppView<? extends AppInfoWithClassHierarchy> appView);
 
   public abstract boolean isVirtualTarget();
 
   /** Lookup the single target of an invoke-special on this resolution result if possible. */
   public abstract DexClassAndMethod lookupInvokeSpecialTarget(
-      DexProgramClass context, AppInfoWithClassHierarchy appInfo);
+      DexProgramClass context, AppView<? extends AppInfoWithClassHierarchy> appView);
+
+  public final DexClassAndMethod lookupInvokeSuperTarget(
+      DexProgramClass context, AppView<? extends AppInfoWithClassHierarchy> appView) {
+    return lookupInvokeSuperTarget(context, appView, appView.appInfo());
+  }
 
   /** Lookup the single target of an invoke-super on this resolution result if possible. */
   public abstract DexClassAndMethod lookupInvokeSuperTarget(
-      DexProgramClass context, AppInfoWithClassHierarchy appInfo);
+      DexProgramClass context, AppView<?> appView, AppInfoWithClassHierarchy appInfo);
 
   /** Lookup the single target of an invoke-direct on this resolution result if possible. */
+  public final DexEncodedMethod lookupInvokeDirectTarget(
+      DexProgramClass context, AppView<? extends AppInfoWithClassHierarchy> appView) {
+    return lookupInvokeDirectTarget(context, appView, appView.appInfo());
+  }
+
   public abstract DexEncodedMethod lookupInvokeDirectTarget(
-      DexProgramClass context, AppInfoWithClassHierarchy appInfo);
+      DexProgramClass context, AppView<?> appView, AppInfoWithClassHierarchy appInfo);
 
   /** Lookup the single target of an invoke-static on this resolution result if possible. */
+  public final DexEncodedMethod lookupInvokeStaticTarget(
+      DexProgramClass context, AppView<? extends AppInfoWithClassHierarchy> appView) {
+    return lookupInvokeStaticTarget(context, appView, appView.appInfo());
+  }
+
   public abstract DexEncodedMethod lookupInvokeStaticTarget(
-      DexProgramClass context, AppInfoWithClassHierarchy appInfo);
+      DexProgramClass context, AppView<?> appView, AppInfoWithClassHierarchy appInfo);
 
   public abstract LookupResult lookupVirtualDispatchTargets(
       DexProgramClass context,
-      AppInfoWithClassHierarchy appInfo,
+      AppView<? extends AppInfoWithClassHierarchy> appView,
       InstantiatedSubTypeInfo instantiatedInfo,
       PinnedPredicate pinnedPredicate);
 
   public final LookupResult lookupVirtualDispatchTargets(
-      DexProgramClass context, AppInfoWithLiveness appInfo) {
+      DexProgramClass context, AppView<AppInfoWithLiveness> appView) {
+    AppInfoWithLiveness appInfo = appView.appInfo();
     return lookupVirtualDispatchTargets(
-        context, appInfo, appInfo, appInfo::isPinnedNotProgramOrLibraryOverride);
+        context, appView, appInfo, appInfo::isPinnedNotProgramOrLibraryOverride);
   }
 
   public abstract LookupResult lookupVirtualDispatchTargets(
       DexProgramClass context,
-      AppInfoWithLiveness appInfo,
+      AppView<AppInfoWithLiveness> appView,
       DexProgramClass refinedReceiverUpperBound,
       DexProgramClass refinedReceiverLowerBound);
 
@@ -309,15 +337,15 @@
 
     @Override
     public OptionalBool isAccessibleFrom(
-        ProgramDefinition context, AppInfoWithClassHierarchy appInfo) {
-      return AccessControl.isMemberAccessible(this, context, appInfo);
+        ProgramDefinition context, AppView<?> appView, AppInfoWithClassHierarchy appInfo) {
+      return AccessControl.isMemberAccessible(this, context, appView, appInfo);
     }
 
     @Override
     public OptionalBool isAccessibleForVirtualDispatchFrom(
-        ProgramDefinition context, AppInfoWithClassHierarchy appInfo) {
+        ProgramDefinition context, AppView<? extends AppInfoWithClassHierarchy> appView) {
       if (resolvedMethod.isVirtualMethod()) {
-        return isAccessibleFrom(context, appInfo);
+        return isAccessibleFrom(context, appView, appView.appInfo());
       }
       return OptionalBool.FALSE;
     }
@@ -335,9 +363,10 @@
      */
     @Override
     public DexClassAndMethod lookupInvokeSpecialTarget(
-        DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
+        DexProgramClass context, AppView<? extends AppInfoWithClassHierarchy> appView) {
       // If the resolution is non-accessible then no target exists.
-      if (isAccessibleFrom(context, appInfo).isPossiblyTrue()) {
+      AppInfoWithClassHierarchy appInfo = appView.appInfo();
+      if (isAccessibleFrom(context, appView).isPossiblyTrue()) {
         return internalInvokeSpecialOrSuper(
             context, appInfo, (sup, sub) -> isSuperclass(sup, sub, appInfo));
       }
@@ -372,14 +401,14 @@
      */
     @Override
     public DexClassAndMethod lookupInvokeSuperTarget(
-        DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
+        DexProgramClass context, AppView<?> appView, AppInfoWithClassHierarchy appInfo) {
       if (resolvedMethod.isInstanceInitializer()
           || (initialResolutionHolder != context
               && !isSuperclass(initialResolutionHolder, context, appInfo))) {
         // If the target is <init> or not on a super class then the call is invalid.
         return null;
       }
-      if (isAccessibleFrom(context, appInfo).isPossiblyTrue()) {
+      if (isAccessibleFrom(context, appView, appInfo).isPossiblyTrue()) {
         return internalInvokeSpecialOrSuper(context, appInfo, (sup, sub) -> true);
       }
       return null;
@@ -397,8 +426,8 @@
      */
     @Override
     public DexEncodedMethod lookupInvokeStaticTarget(
-        DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
-      if (isAccessibleFrom(context, appInfo).isFalse()) {
+        DexProgramClass context, AppView<?> appView, AppInfoWithClassHierarchy appInfo) {
+      if (isAccessibleFrom(context, appView, appInfo).isFalse()) {
         return null;
       }
       if (resolvedMethod.isStatic()) {
@@ -418,8 +447,8 @@
      */
     @Override
     public DexEncodedMethod lookupInvokeDirectTarget(
-        DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
-      if (isAccessibleFrom(context, appInfo).isFalse()) {
+        DexProgramClass context, AppView<?> appView, AppInfoWithClassHierarchy appInfo) {
+      if (isAccessibleFrom(context, appView, appInfo).isFalse()) {
         return null;
       }
       if (resolvedMethod.isDirectMethod()) {
@@ -513,13 +542,14 @@
     @Override
     public LookupResult lookupVirtualDispatchTargets(
         DexProgramClass context,
-        AppInfoWithClassHierarchy appInfo,
+        AppView<? extends AppInfoWithClassHierarchy> appView,
         InstantiatedSubTypeInfo instantiatedInfo,
         PinnedPredicate pinnedPredicate) {
       // Check that the initial resolution holder is accessible from the context.
+      AppInfoWithClassHierarchy appInfo = appView.appInfo();
       assert appInfo.isSubtype(initialResolutionHolder.type, resolvedHolder.type)
           : initialResolutionHolder.type + " is not a subtype of " + resolvedHolder.type;
-      if (context != null && isAccessibleFrom(context, appInfo).isFalse()) {
+      if (context != null && isAccessibleFrom(context, appView).isFalse()) {
         return LookupResult.createFailedResult();
       }
       if (resolvedMethod.isPrivateMethod()) {
@@ -558,7 +588,7 @@
           },
           lambda -> {
             assert resolvedHolder.isInterface()
-                || resolvedHolder.type == appInfo.dexItemFactory().objectType;
+                || resolvedHolder.type == appView.dexItemFactory().objectType;
             LookupTarget target =
                 lookupVirtualDispatchTarget(
                     lambda,
@@ -582,9 +612,10 @@
     @Override
     public LookupResult lookupVirtualDispatchTargets(
         DexProgramClass context,
-        AppInfoWithLiveness appInfo,
+        AppView<AppInfoWithLiveness> appView,
         DexProgramClass refinedReceiverUpperBound,
         DexProgramClass refinedReceiverLowerBound) {
+      AppInfoWithLiveness appInfo = appView.appInfo();
       assert refinedReceiverUpperBound != null;
       assert appInfo.isSubtype(refinedReceiverUpperBound.type, initialResolutionHolder.type);
       assert refinedReceiverLowerBound == null
@@ -600,7 +631,7 @@
       LookupResult lookupResult =
           lookupVirtualDispatchTargets(
               context,
-              appInfo,
+              appView,
               instantiatedSubTypeInfo,
               appInfo::isPinnedNotProgramOrLibraryOverride);
       if (hasInstantiatedLambdas.get() && lookupResult.isLookupResultSuccess()) {
@@ -977,32 +1008,32 @@
 
     @Override
     public final DexClassAndMethod lookupInvokeSpecialTarget(
-        DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
+        DexProgramClass context, AppView<? extends AppInfoWithClassHierarchy> appView) {
       return null;
     }
 
     @Override
     public DexClassAndMethod lookupInvokeSuperTarget(
-        DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
+        DexProgramClass context, AppView<?> appView, AppInfoWithClassHierarchy appInfo) {
       return null;
     }
 
     @Override
     public DexEncodedMethod lookupInvokeStaticTarget(
-        DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
+        DexProgramClass context, AppView<?> appView, AppInfoWithClassHierarchy appInfo) {
       return null;
     }
 
     @Override
     public DexEncodedMethod lookupInvokeDirectTarget(
-        DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
+        DexProgramClass context, AppView<?> appView, AppInfoWithClassHierarchy appInfo) {
       return null;
     }
 
     @Override
     public LookupResult lookupVirtualDispatchTargets(
         DexProgramClass context,
-        AppInfoWithClassHierarchy appInfo,
+        AppView<? extends AppInfoWithClassHierarchy> appView,
         InstantiatedSubTypeInfo instantiatedInfo,
         PinnedPredicate pinnedPredicate) {
       return LookupResult.getIncompleteEmptyResult();
@@ -1011,7 +1042,7 @@
     @Override
     public LookupResult lookupVirtualDispatchTargets(
         DexProgramClass context,
-        AppInfoWithLiveness appInfo,
+        AppView<AppInfoWithLiveness> appView,
         DexProgramClass refinedReceiverUpperBound,
         DexProgramClass refinedReceiverLowerBound) {
       return LookupResult.getIncompleteEmptyResult();
@@ -1050,13 +1081,13 @@
 
     @Override
     public OptionalBool isAccessibleFrom(
-        ProgramDefinition context, AppInfoWithClassHierarchy appInfo) {
+        ProgramDefinition context, AppView<?> appView, AppInfoWithClassHierarchy appInfo) {
       return OptionalBool.TRUE;
     }
 
     @Override
     public OptionalBool isAccessibleForVirtualDispatchFrom(
-        ProgramDefinition context, AppInfoWithClassHierarchy appInfo) {
+        ProgramDefinition context, AppView<? extends AppInfoWithClassHierarchy> appView) {
       return OptionalBool.TRUE;
     }
 
@@ -1110,13 +1141,13 @@
 
     @Override
     public OptionalBool isAccessibleFrom(
-        ProgramDefinition context, AppInfoWithClassHierarchy appInfo) {
+        ProgramDefinition context, AppView<?> appView, AppInfoWithClassHierarchy appInfo) {
       return OptionalBool.FALSE;
     }
 
     @Override
     public OptionalBool isAccessibleForVirtualDispatchFrom(
-        ProgramDefinition context, AppInfoWithClassHierarchy appInfo) {
+        ProgramDefinition context, AppView<? extends AppInfoWithClassHierarchy> appView) {
       return OptionalBool.FALSE;
     }
 
@@ -1218,7 +1249,8 @@
     }
 
     @Override
-    public boolean isNoSuchMethodErrorResult(DexClass context, AppInfoWithClassHierarchy appInfo) {
+    public boolean isNoSuchMethodErrorResult(
+        DexClass context, AppView<?> appView, AppInfoWithClassHierarchy appInfo) {
       return true;
     }
 
@@ -1262,14 +1294,15 @@
     }
 
     @Override
-    public boolean isIllegalAccessErrorResult(DexClass context, AppInfoWithClassHierarchy appInfo) {
+    public boolean isIllegalAccessErrorResult(
+        DexClass context, AppView<?> appView, AppInfoWithClassHierarchy appInfo) {
       if (!hasMethodsCausingError()) {
         return false;
       }
       BooleanBox seenNoAccess = new BooleanBox(false);
       forEachFailureDependency(
           type ->
-              appInfo
+              appView
                   .contextIndependentDefinitionForWithResolutionResult(type)
                   .forEachClassResolutionResult(
                       clazz ->
@@ -1278,27 +1311,28 @@
                                       clazz,
                                       context,
                                       appInfo.getClassToFeatureSplitMap(),
-                                      appInfo.options(),
-                                      appInfo.getStartupOrder(),
-                                      appInfo.getSyntheticItems())
+                                      appView.options(),
+                                      appView.getStartupProfile(),
+                                      appView.getSyntheticItems())
                                   .isPossiblyFalse())),
           method -> {
-            DexClass holder = appInfo.definitionFor(method.getHolderType());
+            DexClass holder = appView.definitionFor(method.getHolderType());
             DexClassAndMethod classAndMethod = DexClassAndMethod.create(holder, method);
             seenNoAccess.or(
                 AccessControl.isMemberAccessible(
-                        classAndMethod, initialResolutionHolder, context, appInfo)
+                        classAndMethod, initialResolutionHolder, context, appView, appInfo)
                     .isPossiblyFalse());
           });
       return seenNoAccess.get();
     }
 
     @Override
-    public boolean isNoSuchMethodErrorResult(DexClass context, AppInfoWithClassHierarchy appInfo) {
+    public boolean isNoSuchMethodErrorResult(
+        DexClass context, AppView<?> appView, AppInfoWithClassHierarchy appInfo) {
       if (!hasMethodsCausingError()) {
         return true;
       }
-      if (isIllegalAccessErrorResult(context, appInfo)) {
+      if (isIllegalAccessErrorResult(context, appView, appInfo)) {
         return false;
       }
       // At this point we know we have methods causing errors but we have access to them. To be
@@ -1345,13 +1379,13 @@
 
     @Override
     public OptionalBool isAccessibleFrom(
-        ProgramDefinition context, AppInfoWithClassHierarchy appInfo) {
+        ProgramDefinition context, AppView<?> appView, AppInfoWithClassHierarchy appInfo) {
       throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
     }
 
     @Override
     public OptionalBool isAccessibleForVirtualDispatchFrom(
-        ProgramDefinition context, AppInfoWithClassHierarchy appInfo) {
+        ProgramDefinition context, AppView<? extends AppInfoWithClassHierarchy> appView) {
       throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
     }
 
@@ -1362,32 +1396,32 @@
 
     @Override
     public DexClassAndMethod lookupInvokeSpecialTarget(
-        DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
+        DexProgramClass context, AppView<? extends AppInfoWithClassHierarchy> appView) {
       throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
     }
 
     @Override
     public DexClassAndMethod lookupInvokeSuperTarget(
-        DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
+        DexProgramClass context, AppView<?> appView, AppInfoWithClassHierarchy appInfo) {
       throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
     }
 
     @Override
     public DexEncodedMethod lookupInvokeDirectTarget(
-        DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
+        DexProgramClass context, AppView<?> appView, AppInfoWithClassHierarchy appInfo) {
       throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
     }
 
     @Override
     public DexEncodedMethod lookupInvokeStaticTarget(
-        DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
+        DexProgramClass context, AppView<?> appView, AppInfoWithClassHierarchy appInfo) {
       throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
     }
 
     @Override
     public LookupResult lookupVirtualDispatchTargets(
         DexProgramClass context,
-        AppInfoWithClassHierarchy appInfo,
+        AppView<? extends AppInfoWithClassHierarchy> appView,
         InstantiatedSubTypeInfo instantiatedInfo,
         PinnedPredicate pinnedPredicate) {
       throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
@@ -1396,7 +1430,7 @@
     @Override
     public LookupResult lookupVirtualDispatchTargets(
         DexProgramClass context,
-        AppInfoWithLiveness appInfo,
+        AppView<AppInfoWithLiveness> appView,
         DexProgramClass refinedReceiverUpperBound,
         DexProgramClass refinedReceiverLowerBound) {
       throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistry.java b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
index f5a1aad..be1b904c06 100644
--- a/src/main/java/com/android/tools/r8/graph/UseRegistry.java
+++ b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
@@ -12,7 +12,7 @@
 
 public abstract class UseRegistry<T extends Definition> {
 
-  private final AppView<?> appView;
+  protected final AppView<?> appView;
   private final T context;
 
   private TraversalContinuation<?, ?> continuation = TraversalContinuation.doContinue();
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
index 96bf9a5..1cf6415 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -29,7 +29,7 @@
 import com.android.tools.r8.ir.analysis.value.NumberFromIntervalValue;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ProfileCollectionAdditions;
 import com.android.tools.r8.utils.SetUtils;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
@@ -107,13 +107,11 @@
   }
 
   void mergeDirectMethods(
-      ArtProfileCollectionAdditions artProfileCollectionAdditions,
+      ProfileCollectionAdditions profileCollectionAdditions,
       SyntheticArgumentClass syntheticArgumentClass,
       SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder) {
     mergeInstanceInitializers(
-        artProfileCollectionAdditions,
-        syntheticArgumentClass,
-        syntheticInitializerConverterBuilder);
+        profileCollectionAdditions, syntheticArgumentClass, syntheticInitializerConverterBuilder);
     mergeStaticClassInitializers(syntheticInitializerConverterBuilder);
     group.forEach(this::mergeDirectMethods);
   }
@@ -192,38 +190,36 @@
   }
 
   void mergeInstanceInitializers(
-      ArtProfileCollectionAdditions artProfileCollectionAdditions,
+      ProfileCollectionAdditions profileCollectionAdditions,
       SyntheticArgumentClass syntheticArgumentClass,
       SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder) {
     instanceInitializerMergers.forEach(
         merger ->
             merger.merge(
-                artProfileCollectionAdditions,
+                profileCollectionAdditions,
                 classMethodsBuilder,
                 syntheticArgumentClass,
                 syntheticInitializerConverterBuilder));
   }
 
   void mergeMethods(
-      ArtProfileCollectionAdditions artProfileCollectionAdditions,
+      ProfileCollectionAdditions profileCollectionAdditions,
       SyntheticArgumentClass syntheticArgumentClass,
       SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder,
       Consumer<VirtuallyMergedMethodsKeepInfo> virtuallyMergedMethodsKeepInfoConsumer) {
-    mergeVirtualMethods(artProfileCollectionAdditions, virtuallyMergedMethodsKeepInfoConsumer);
+    mergeVirtualMethods(profileCollectionAdditions, virtuallyMergedMethodsKeepInfoConsumer);
     mergeDirectMethods(
-        artProfileCollectionAdditions,
-        syntheticArgumentClass,
-        syntheticInitializerConverterBuilder);
+        profileCollectionAdditions, syntheticArgumentClass, syntheticInitializerConverterBuilder);
     classMethodsBuilder.setClassMethods(group.getTarget());
   }
 
   void mergeVirtualMethods(
-      ArtProfileCollectionAdditions artProfileCollectionAdditions,
+      ProfileCollectionAdditions profileCollectionAdditions,
       Consumer<VirtuallyMergedMethodsKeepInfo> virtuallyMergedMethodsKeepInfoConsumer) {
     virtualMethodMergers.forEach(
         merger ->
             merger.merge(
-                artProfileCollectionAdditions,
+                profileCollectionAdditions,
                 classMethodsBuilder,
                 lensBuilder,
                 classIdentifiers,
@@ -342,7 +338,7 @@
   }
 
   public void mergeGroup(
-      ArtProfileCollectionAdditions artProfileCollectionAdditions,
+      ProfileCollectionAdditions profileCollectionAdditions,
       PrunedItems.Builder prunedItemsBuilder,
       SyntheticArgumentClass syntheticArgumentClass,
       SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder,
@@ -353,7 +349,7 @@
     mergeInterfaces();
     mergeFields(prunedItemsBuilder);
     mergeMethods(
-        artProfileCollectionAdditions,
+        profileCollectionAdditions,
         syntheticArgumentClass,
         syntheticInitializerConverterBuilder,
         virtuallyMergedMethodsKeepInfoConsumer);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index 8fc788b..32dd38f 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -18,7 +18,7 @@
 import com.android.tools.r8.horizontalclassmerging.code.SyntheticInitializerConverter;
 import com.android.tools.r8.ir.code.InvokeType;
 import com.android.tools.r8.profile.art.ArtProfileCompletenessChecker;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ProfileCollectionAdditions;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
 import com.android.tools.r8.shaking.KeepInfoCollection;
@@ -126,8 +126,8 @@
 
     // Merge the classes.
     List<ClassMerger> classMergers = initializeClassMergers(codeProvider, lensBuilder, groups);
-    ArtProfileCollectionAdditions artProfileCollectionAdditions =
-        ArtProfileCollectionAdditions.create(appView);
+    ProfileCollectionAdditions profileCollectionAdditions =
+        ProfileCollectionAdditions.create(appView);
     SyntheticArgumentClass syntheticArgumentClass =
         mode.isInitial()
             ? new SyntheticArgumentClass.Builder(appView.withLiveness()).build(groups)
@@ -138,7 +138,7 @@
     PrunedItems prunedItems =
         applyClassMergers(
             classMergers,
-            artProfileCollectionAdditions,
+            profileCollectionAdditions,
             syntheticArgumentClass,
             syntheticInitializerConverterBuilder,
             virtuallyMergedMethodsKeepInfos::add);
@@ -155,13 +155,9 @@
 
     HorizontalClassMergerGraphLens horizontalClassMergerGraphLens =
         createLens(
-            mergedClasses,
-            lensBuilder,
-            mode,
-            artProfileCollectionAdditions,
-            syntheticArgumentClass);
-    artProfileCollectionAdditions =
-        artProfileCollectionAdditions.rewriteMethodReferences(
+            mergedClasses, lensBuilder, mode, profileCollectionAdditions, syntheticArgumentClass);
+    profileCollectionAdditions =
+        profileCollectionAdditions.rewriteMethodReferences(
             horizontalClassMergerGraphLens::getNextMethodToInvoke);
 
     assert verifyNoCyclesInInterfaceHierarchies(appView, groups);
@@ -195,8 +191,9 @@
     codeProvider.setGraphLens(horizontalClassMergerGraphLens);
 
     // Amend art profile collection.
-    artProfileCollectionAdditions
+    profileCollectionAdditions
         .setArtProfileCollection(appView.getArtProfileCollection())
+        .setStartupProfile(appView.getStartupProfile())
         .commit(appView);
 
     // Record where the synthesized $r8$classId fields are read and written.
@@ -374,14 +371,14 @@
   /** Merges all class groups using {@link ClassMerger}. */
   private PrunedItems applyClassMergers(
       Collection<ClassMerger> classMergers,
-      ArtProfileCollectionAdditions artProfileCollectionAdditions,
+      ProfileCollectionAdditions profileCollectionAdditions,
       SyntheticArgumentClass syntheticArgumentClass,
       SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder,
       Consumer<VirtuallyMergedMethodsKeepInfo> virtuallyMergedMethodsKeepInfoConsumer) {
     PrunedItems.Builder prunedItemsBuilder = PrunedItems.builder().setPrunedApp(appView.app());
     for (ClassMerger merger : classMergers) {
       merger.mergeGroup(
-          artProfileCollectionAdditions,
+          profileCollectionAdditions,
           prunedItemsBuilder,
           syntheticArgumentClass,
           syntheticInitializerConverterBuilder,
@@ -398,14 +395,14 @@
       HorizontallyMergedClasses mergedClasses,
       HorizontalClassMergerGraphLens.Builder lensBuilder,
       Mode mode,
-      ArtProfileCollectionAdditions artProfileCollectionAdditions,
+      ProfileCollectionAdditions profileCollectionAdditions,
       SyntheticArgumentClass syntheticArgumentClass) {
     return new TreeFixer(
             appView,
             mergedClasses,
             lensBuilder,
             mode,
-            artProfileCollectionAdditions,
+            profileCollectionAdditions,
             syntheticArgumentClass)
         .fixupTypeReferences();
   }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
index d827984..e1a6710 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
@@ -24,7 +24,7 @@
 import com.android.tools.r8.ir.conversion.ExtraConstantIntParameter;
 import com.android.tools.r8.ir.conversion.ExtraParameter;
 import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ProfileCollectionAdditions;
 import com.android.tools.r8.utils.ArrayUtils;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.Box;
@@ -298,7 +298,7 @@
 
   /** Synthesize a new method which selects the constructor based on a parameter type. */
   void merge(
-      ArtProfileCollectionAdditions artProfileCollectionAdditions,
+      ProfileCollectionAdditions profileCollectionAdditions,
       ClassMethodsBuilder classMethodsBuilder,
       SyntheticArgumentClass syntheticArgumentClass,
       SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder) {
@@ -324,7 +324,7 @@
     // Amend the art profile collection.
     if (usedSyntheticArgumentClasses.isSet()) {
       for (ProgramMethod instanceInitializer : instanceInitializers) {
-        artProfileCollectionAdditions.applyIfContextIsInProfile(
+        profileCollectionAdditions.applyIfContextIsInProfile(
             instanceInitializer.getReference(),
             additionsBuilder ->
                 usedSyntheticArgumentClasses.get().forEach(additionsBuilder::addRule));
@@ -353,7 +353,7 @@
             instanceInitializer.getReference(), movedInstanceInitializer);
 
         // Amend the art profile collection.
-        artProfileCollectionAdditions.applyIfContextIsInProfile(
+        profileCollectionAdditions.applyIfContextIsInProfile(
             instanceInitializer.getReference(),
             additionsBuilder -> additionsBuilder.addRule(representative));
       }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
index a4b8ae0..948c889 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
@@ -21,7 +21,7 @@
 import com.android.tools.r8.graph.TreeFixerBase;
 import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
 import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ProfileCollectionAdditions;
 import com.android.tools.r8.shaking.AnnotationFixer;
 import com.android.tools.r8.utils.ArrayUtils;
 import com.android.tools.r8.utils.Box;
@@ -49,7 +49,7 @@
   private final Mode mode;
   private final HorizontalClassMergerGraphLens.Builder lensBuilder;
   private final DexItemFactory dexItemFactory;
-  private final ArtProfileCollectionAdditions artProfileCollectionAdditions;
+  private final ProfileCollectionAdditions profileCollectionAdditions;
   private final SyntheticArgumentClass syntheticArgumentClass;
 
   private final Map<DexProgramClass, DexType> originalSuperTypes = new IdentityHashMap<>();
@@ -61,14 +61,14 @@
       HorizontallyMergedClasses mergedClasses,
       HorizontalClassMergerGraphLens.Builder lensBuilder,
       Mode mode,
-      ArtProfileCollectionAdditions artProfileCollectionAdditions,
+      ProfileCollectionAdditions profileCollectionAdditions,
       SyntheticArgumentClass syntheticArgumentClass) {
     super(appView);
     this.appView = appView;
     this.mergedClasses = mergedClasses;
     this.mode = mode;
     this.lensBuilder = lensBuilder;
-    this.artProfileCollectionAdditions = artProfileCollectionAdditions;
+    this.profileCollectionAdditions = profileCollectionAdditions;
     this.syntheticArgumentClass = syntheticArgumentClass;
     this.dexItemFactory = appView.dexItemFactory();
   }
@@ -311,13 +311,13 @@
           Set<DexMethod> previousMethodReferences =
               lensBuilder.methodMap.getKeys(originalMethodReference);
           if (previousMethodReferences.isEmpty()) {
-            artProfileCollectionAdditions.applyIfContextIsInProfile(
+            profileCollectionAdditions.applyIfContextIsInProfile(
                 originalMethodReference,
                 additionsBuilder ->
                     usedSyntheticArgumentClasses.get().forEach(additionsBuilder::addRule));
           } else {
             for (DexMethod previousMethodReference : previousMethodReferences) {
-              artProfileCollectionAdditions.applyIfContextIsInProfile(
+              profileCollectionAdditions.applyIfContextIsInProfile(
                   previousMethodReference,
                   additionsBuilder ->
                       usedSyntheticArgumentClasses.get().forEach(additionsBuilder::addRule));
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
index b47b947..a9dd719 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
@@ -14,7 +14,7 @@
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ProfileCollectionAdditions;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.OptionalBool;
 import com.android.tools.r8.utils.structural.Ordered;
@@ -245,7 +245,7 @@
   }
 
   public void merge(
-      ArtProfileCollectionAdditions artProfileCollectionAdditions,
+      ProfileCollectionAdditions profileCollectionAdditions,
       ClassMethodsBuilder classMethodsBuilder,
       HorizontalClassMergerGraphLens.Builder lensBuilder,
       Reference2IntMap<DexType> classIdentifiers,
@@ -327,9 +327,9 @@
     lensBuilder.recordNewMethodSignature(bridgeMethodReference, newMethodReference);
 
     // Amend the art profile collection.
-    if (!artProfileCollectionAdditions.isNop()) {
+    if (!profileCollectionAdditions.isNop()) {
       for (ProgramMethod oldMethod : methods) {
-        artProfileCollectionAdditions.applyIfContextIsInProfile(
+        profileCollectionAdditions.applyIfContextIsInProfile(
             oldMethod.getReference(),
             additionsBuilder -> additionsBuilder.addRule(representativeMethod.getReference()));
       }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerCycles.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerCycles.java
index b1ad665..2e0a307 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerCycles.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerCycles.java
@@ -106,6 +106,10 @@
     this.appView = appView;
   }
 
+  private AppView<AppInfoWithLiveness> appView() {
+    return appView;
+  }
+
   // TODO(b/270398965): Replace LinkedList.
   @SuppressWarnings("JdkObsolete")
   @Override
@@ -407,7 +411,7 @@
     class TracerUseRegistry extends UseRegistry<ProgramMethod> {
 
       TracerUseRegistry(ProgramMethod context) {
-        super(appView, context);
+        super(appView(), context);
       }
 
       private void fail() {
@@ -431,7 +435,7 @@
       }
 
       private boolean isClassAlreadyInitializedInCurrentContext(DexProgramClass clazz) {
-        return appView.appInfo().isSubtype(getContext().getHolder(), clazz);
+        return appView().appInfo().isSubtype(getContext().getHolder(), clazz);
       }
 
       private void triggerClassInitializer(DexProgramClass root) {
@@ -481,7 +485,7 @@
         DexMethod rewrittenMethod =
             appView.graphLens().lookupInvokeDirect(method, getContext()).getReference();
         MethodResolutionResult resolutionResult =
-            appView.appInfo().resolveMethodOnClassHolderLegacy(rewrittenMethod);
+            appView().appInfo().resolveMethodOnClassHolderLegacy(rewrittenMethod);
         if (resolutionResult.isSingleResolution()
             && resolutionResult.getResolvedHolder().isProgramClass()) {
           enqueueMethod(resolutionResult.getResolvedProgramMethod());
@@ -493,7 +497,7 @@
         DexMethod rewrittenMethod =
             appView.graphLens().lookupInvokeInterface(method, getContext()).getReference();
         DexClassAndMethod resolvedMethod =
-            appView
+            appView()
                 .appInfo()
                 .resolveMethodOnInterfaceHolderLegacy(rewrittenMethod)
                 .getResolutionPair();
@@ -507,7 +511,7 @@
         DexMethod rewrittenMethod =
             appView.graphLens().lookupInvokeStatic(method, getContext()).getReference();
         ProgramMethod resolvedMethod =
-            appView
+            appView()
                 .appInfo()
                 .unsafeResolveMethodDueToDexFormatLegacy(rewrittenMethod)
                 .getResolvedProgramMethod();
@@ -523,7 +527,7 @@
             appView.graphLens().lookupInvokeSuper(method, getContext()).getReference();
         ProgramMethod superTarget =
             asProgramMethodOrNull(
-                appView.appInfo().lookupSuperTarget(rewrittenMethod, getContext()));
+                appView().appInfo().lookupSuperTarget(rewrittenMethod, getContext(), appView()));
         if (superTarget != null) {
           enqueueMethod(superTarget);
         }
@@ -534,7 +538,10 @@
         DexMethod rewrittenMethod =
             appView.graphLens().lookupInvokeVirtual(method, getContext()).getReference();
         DexClassAndMethod resolvedMethod =
-            appView.appInfo().resolveMethodOnClassHolderLegacy(rewrittenMethod).getResolutionPair();
+            appView()
+                .appInfo()
+                .resolveMethodOnClassHolderLegacy(rewrittenMethod)
+                .getResolutionPair();
         if (resolvedMethod != null) {
           if (!resolvedMethod.getHolder().isEffectivelyFinal(appView)) {
             fail();
@@ -569,7 +576,7 @@
 
       @Override
       public void registerCallSite(DexCallSite callSite) {
-        if (isLambdaMetafactoryMethod(callSite, appView.appInfo())) {
+        if (isLambdaMetafactoryMethod(callSite, appView().appInfo())) {
           // Use of lambda metafactory does not trigger any class initialization.
         } else {
           fail();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
index acc6daa..4525030 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
@@ -82,6 +82,10 @@
     this.postMethodProcessorBuilder = postMethodProcessorBuilder;
   }
 
+  private AppView<AppInfoWithLiveness> appView() {
+    return appView;
+  }
+
   public void run(
       ExecutorService executorService, OptimizationFeedbackDelayed feedback, Timing timing)
       throws ExecutionException {
@@ -312,7 +316,7 @@
   class TrivialFieldAccessUseRegistry extends UseRegistry<ProgramMethod> {
 
     TrivialFieldAccessUseRegistry(ProgramMethod method) {
-      super(appView, method);
+      super(appView(), method);
     }
 
     private void registerFieldAccess(
@@ -320,7 +324,7 @@
         boolean isStatic,
         boolean isWrite,
         BytecodeInstructionMetadata metadata) {
-      FieldResolutionResult resolutionResult = appView.appInfo().resolveField(reference);
+      FieldResolutionResult resolutionResult = appView().appInfo().resolveField(reference);
       if (!resolutionResult.hasProgramResult()) {
         // We don't care about field accesses that may not resolve to a program field.
         return;
@@ -332,8 +336,8 @@
       if (definition.isStatic() != isStatic
           || appView.isCfByteCodePassThrough(getContext().getDefinition())
           || !resolutionResult.isSingleProgramFieldResolutionResult()
-          || resolutionResult.isAccessibleFrom(getContext(), appView).isPossiblyFalse()
-          || appView.appInfo().isNeverReprocessMethod(getContext())) {
+          || resolutionResult.isAccessibleFrom(getContext(), appView()).isPossiblyFalse()
+          || appView().appInfo().isNeverReprocessMethod(getContext())) {
         recordAccessThatCannotBeOptimized(field, definition);
         return;
       }
@@ -355,7 +359,7 @@
       }
 
       // Record access.
-      if (field.isProgramField() && appView.appInfo().mayPropagateValueFor(appView, field)) {
+      if (field.isProgramField() && appView().appInfo().mayPropagateValueFor(appView(), field)) {
         if (field.getAccessFlags().isStatic() == isStatic) {
           if (isWrite) {
             recordFieldAccessContext(definition, writtenFields, readFields);
@@ -400,7 +404,7 @@
     private void recordAccessThatCannotBeOptimized(
         DexClassAndField field, DexEncodedField definition) {
       constantFields.remove(definition);
-      if (field.isProgramField() && appView.appInfo().mayPropagateValueFor(appView, field)) {
+      if (field.isProgramField() && appView().appInfo().mayPropagateValueFor(appView(), field)) {
         destroyFieldAccessContexts(definition);
       }
     }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Cmp.java b/src/main/java/com/android/tools/r8/ir/code/Cmp.java
index a03637e..bc4cf73 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Cmp.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Cmp.java
@@ -19,6 +19,7 @@
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.lightir.LirBuilder;
 import com.android.tools.r8.utils.LongInterval;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.StringUtils.BraceType;
@@ -236,6 +237,11 @@
   }
 
   @Override
+  public void buildLir(LirBuilder<Value, ?> builder) {
+    builder.addCmp(type, bias, leftValue(), rightValue());
+  }
+
+  @Override
   public TypeElement evaluate(AppView<?> appView) {
     return TypeElement.getInt();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
index 3551561..86ef0d7 100644
--- a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
@@ -92,7 +92,7 @@
     // Check if the resolution target is accessible.
     if (singleFieldResolutionResult.getResolvedHolder() != context.getHolder()) {
       if (singleFieldResolutionResult
-          .isAccessibleFrom(context, appView.appInfo().withClassHierarchy())
+          .isAccessibleFrom(context, appView.withClassHierarchy())
           .isPossiblyFalse()) {
         return true;
       }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
index d01444f..e14f898 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -32,6 +32,7 @@
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.lightir.LirBuilder;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.Set;
 
@@ -266,6 +267,11 @@
     registry.registerInstanceFieldRead(getField());
   }
 
+  @Override
+  public void buildLir(LirBuilder<Value, ?> builder) {
+    builder.addInstanceGet(getField(), object());
+  }
+
   public static class Builder extends BuilderBase<Builder, InstanceGet> {
 
     private DexField field;
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
index bc4d8df..acc4428 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -33,6 +33,7 @@
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.android.tools.r8.lightir.LirBuilder;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.Arrays;
 
@@ -283,4 +284,9 @@
   void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
     registry.registerInstanceFieldWrite(getField());
   }
+
+  @Override
+  public void buildLir(LirBuilder<Value, ?> builder) {
+    builder.addInstancePut(getField(), object(), value());
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
index 4fa5742..92ecb73 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
@@ -25,6 +25,7 @@
 import com.android.tools.r8.ir.desugar.LambdaDescriptor;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.List;
 
 public final class InvokeCustom extends Invoke {
@@ -70,8 +71,8 @@
     if (!appView.appInfo().hasLiveness()) {
       return returnType;
     }
-    List<DexType> lambdaInterfaces =
-        LambdaDescriptor.getInterfaces(callSite, appView.appInfo().withClassHierarchy());
+    AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
+    List<DexType> lambdaInterfaces = LambdaDescriptor.getInterfaces(callSite, appViewWithLiveness);
     if (lambdaInterfaces == null || lambdaInterfaces.isEmpty()) {
       return returnType;
     }
@@ -98,7 +99,7 @@
     assert verifyLambdaInterfaces(returnType, lambdaInterfaceSet, objectType);
 
     return ClassTypeElement.create(
-        objectType, Nullability.maybeNull(), appView.withClassHierarchy(), lambdaInterfaceSet);
+        objectType, Nullability.maybeNull(), appViewWithLiveness, lambdaInterfaceSet);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index 146f61a..3891d20 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -140,9 +140,10 @@
       AppView<?> appView, ProgramMethod context, DynamicType dynamicReceiverType) {
     DexMethod invokedMethod = getInvokedMethod();
     DexEncodedMethod result;
-    if (appView.appInfo().hasLiveness()) {
-      AppInfoWithLiveness appInfo = appView.appInfo().withLiveness();
-      result = appInfo.lookupDirectTarget(invokedMethod, context);
+    if (appView.hasLiveness()) {
+      AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
+      AppInfoWithLiveness appInfo = appViewWithLiveness.appInfo();
+      result = appInfo.lookupDirectTarget(invokedMethod, context, appViewWithLiveness);
       assert verifyD8LookupResult(
           result, appView.appInfo().lookupDirectTargetOnItself(invokedMethod, context));
     } else {
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
index 94ba6c4..2936292 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
@@ -26,6 +26,7 @@
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.lightir.LirBuilder;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.List;
 
@@ -165,4 +166,9 @@
   void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
     registry.registerInvokeInterface(getInvokedMethod());
   }
+
+  @Override
+  public void buildLir(LirBuilder<Value, ?> builder) {
+    builder.addInvokeInterface(getInvokedMethod(), arguments());
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
index 7f5c809..b7a8fae 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -156,14 +156,9 @@
     if (refinedReceiverUpperBound != null) {
       lookupResult =
           resolutionResult.lookupVirtualDispatchTargets(
-              context.getHolder(),
-              appView.withLiveness().appInfo(),
-              refinedReceiverUpperBound,
-              refinedReceiverLowerBound);
+              context.getHolder(), appView, refinedReceiverUpperBound, refinedReceiverLowerBound);
     } else {
-      lookupResult =
-          resolutionResult.lookupVirtualDispatchTargets(
-              context.getHolder(), appView.withLiveness().appInfo());
+      lookupResult = resolutionResult.lookupVirtualDispatchTargets(context.getHolder(), appView);
     }
     if (lookupResult.isLookupResultFailure()) {
       return null;
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
index 1bf7eb7..3acd5fc 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -29,6 +29,7 @@
 import com.android.tools.r8.ir.optimize.Inliner.Reason;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
+import com.android.tools.r8.lightir.LirBuilder;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.List;
 
@@ -120,10 +121,11 @@
     DexMethod invokedMethod = getInvokedMethod();
     DexEncodedMethod result;
     if (appView.appInfo().hasLiveness()) {
-      AppInfoWithLiveness appInfo = appView.appInfo().withLiveness();
-      result = appInfo.lookupStaticTarget(invokedMethod, context);
+      AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
+      AppInfoWithLiveness appInfo = appViewWithLiveness.appInfo();
+      result = appInfo.lookupStaticTarget(invokedMethod, context, appViewWithLiveness);
       assert verifyD8LookupResult(
-          result, appView.appInfo().lookupStaticTargetOnItself(invokedMethod, context));
+          result, appInfo.lookupStaticTargetOnItself(invokedMethod, context));
     } else {
       // Allow optimizing static library invokes in D8.
       DexClass clazz = appView.definitionForHolder(getInvokedMethod());
@@ -216,7 +218,7 @@
 
     // Verify that the target method is static and accessible.
     if (!singleTarget.getDefinition().isStatic()
-        || resolutionResult.isAccessibleFrom(context, appInfoWithLiveness).isPossiblyFalse()) {
+        || resolutionResult.isAccessibleFrom(context, appViewWithLiveness).isPossiblyFalse()) {
       return true;
     }
 
@@ -243,6 +245,11 @@
     registry.registerInvokeStatic(getInvokedMethod());
   }
 
+  @Override
+  public void buildLir(LirBuilder<Value, ?> builder) {
+    builder.addInvokeStatic(getInvokedMethod(), arguments());
+  }
+
   public static class Builder extends InvokeMethod.Builder<Builder, InvokeStatic> {
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
index 0b7dce8..4eaae3c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
@@ -113,7 +113,7 @@
       AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
       AppInfoWithLiveness appInfo = appViewWithLiveness.appInfo();
       if (appInfo.isSubtype(context.getHolderType(), getInvokedMethod().holder)) {
-        return appInfo.lookupSuperTarget(getInvokedMethod(), context);
+        return appInfo.lookupSuperTarget(getInvokedMethod(), context, appViewWithLiveness);
       }
     }
     return null;
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
index 40e7d5d..0fa2583 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
@@ -29,6 +29,7 @@
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.lightir.LirBuilder;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
 public class NewInstance extends Instruction {
@@ -239,6 +240,11 @@
     registry.registerNewInstance(clazz);
   }
 
+  @Override
+  public void buildLir(LirBuilder<Value, ?> builder) {
+    builder.addNewInstance(clazz);
+  }
+
   public static class Builder extends BuilderBase<Builder, NewInstance> {
 
     private DexType type;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
index 7cbb122..9782300 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
@@ -13,7 +13,7 @@
 import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.itf.InterfaceProcessor;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ProfileCollectionAdditions;
 import com.android.tools.r8.utils.MapUtils;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.collections.ImmutableDeque;
@@ -114,11 +114,10 @@
       ClassConverterResult.Builder resultBuilder, ExecutorService executorService)
       throws ExecutionException {
     Collection<DexProgramClass> classes = appView.appInfo().classes();
-    ArtProfileCollectionAdditions artProfileCollectionAdditions =
-        methodProcessor.getArtProfileCollectionAdditions();
+    ProfileCollectionAdditions profileCollectionAdditions =
+        methodProcessor.getProfileCollectionAdditions();
     CfClassSynthesizerDesugaringEventConsumer classSynthesizerEventConsumer =
-        CfClassSynthesizerDesugaringEventConsumer.createForD8(
-            appView, artProfileCollectionAdditions);
+        CfClassSynthesizerDesugaringEventConsumer.createForD8(appView, profileCollectionAdditions);
     converter.classSynthesisDesugaring(executorService, classSynthesizerEventConsumer);
     if (!classSynthesizerEventConsumer.getSynthesizedClasses().isEmpty()) {
       classes =
@@ -130,7 +129,7 @@
 
     CfInstructionDesugaringEventConsumer instructionDesugaringEventConsumerForPrepareStep =
         CfInstructionDesugaringEventConsumer.createForD8(
-            appView, artProfileCollectionAdditions, resultBuilder, methodProcessor);
+            appView, profileCollectionAdditions, resultBuilder, methodProcessor);
     converter.prepareDesugaring(instructionDesugaringEventConsumerForPrepareStep, executorService);
     assert instructionDesugaringEventConsumerForPrepareStep.verifyNothingToFinalize();
 
@@ -153,7 +152,7 @@
 
       CfInstructionDesugaringEventConsumer instructionDesugaringEventConsumerForWave =
           CfInstructionDesugaringEventConsumer.createForD8(
-              appView, artProfileCollectionAdditions, resultBuilder, methodProcessor);
+              appView, profileCollectionAdditions, resultBuilder, methodProcessor);
 
       // Process the wave and wait for all IR processing to complete.
       methodProcessor.newWave();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
index 9a81a28..fec53b0 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
@@ -12,7 +12,7 @@
 import com.android.tools.r8.ir.conversion.callgraph.CallSiteInformation;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ProfileCollectionAdditions;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
@@ -25,7 +25,7 @@
 
 public class D8MethodProcessor extends MethodProcessor {
 
-  private final ArtProfileCollectionAdditions artProfileCollectionAdditions;
+  private final ProfileCollectionAdditions profileCollectionAdditions;
   private final PrimaryD8L8IRConverter converter;
   private final MethodProcessorEventConsumer eventConsumer;
   private final ExecutorService executorService;
@@ -44,12 +44,12 @@
   private ProcessorContext processorContext;
 
   public D8MethodProcessor(
-      ArtProfileCollectionAdditions artProfileCollectionAdditions,
+      ProfileCollectionAdditions profileCollectionAdditions,
       PrimaryD8L8IRConverter converter,
       ExecutorService executorService) {
-    this.artProfileCollectionAdditions = artProfileCollectionAdditions;
+    this.profileCollectionAdditions = profileCollectionAdditions;
     this.converter = converter;
-    this.eventConsumer = MethodProcessorEventConsumer.createForD8(artProfileCollectionAdditions);
+    this.eventConsumer = MethodProcessorEventConsumer.createForD8(profileCollectionAdditions);
     this.executorService = executorService;
     this.processorContext = converter.appView.createProcessorContext();
   }
@@ -68,8 +68,8 @@
     return processorContext.createMethodProcessingContext(method);
   }
 
-  public ArtProfileCollectionAdditions getArtProfileCollectionAdditions() {
-    return artProfileCollectionAdditions;
+  public ProfileCollectionAdditions getProfileCollectionAdditions() {
+    return profileCollectionAdditions;
   }
 
   @Override
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 0050541..c2917b8 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
@@ -1056,13 +1056,15 @@
       OptimizationFeedback feedback,
       BytecodeMetadataProvider bytecodeMetadataProvider,
       Timing timing) {
-    IRCode round1 = doRoundtripWithStrategy(code, new ExternalPhisStrategy(), "indirect phis");
-    IRCode round2 = doRoundtripWithStrategy(round1, new PhiInInstructionsStrategy(), "inline phis");
+    IRCode round1 =
+        doRoundtripWithStrategy(code, new ExternalPhisStrategy(), "indirect phis", timing);
+    IRCode round2 =
+        doRoundtripWithStrategy(round1, new PhiInInstructionsStrategy(), "inline phis", timing);
     return round2;
   }
 
   private <EV, S extends LirStrategy<Value, EV>> IRCode doRoundtripWithStrategy(
-      IRCode code, S strategy, String name) {
+      IRCode code, S strategy, String name, Timing timing) {
     timing.begin("IR->LIR (" + name + ")");
     LirCode<EV> lirCode =
         IR2LirConverter.translate(code, strategy.getEncodingStrategy(), appView.dexItemFactory());
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorEventConsumer.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorEventConsumer.java
index e360a95..a14171a 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorEventConsumer.java
@@ -12,8 +12,8 @@
 import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizationsEventConsumer;
 import com.android.tools.r8.ir.optimize.api.InstanceInitializerOutlinerEventConsumer;
 import com.android.tools.r8.ir.optimize.enums.EnumUnboxerMethodProcessorEventConsumer;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingMethodProcessorEventConsumer;
+import com.android.tools.r8.profile.art.rewriting.ProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ProfileRewritingMethodProcessorEventConsumer;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
 public abstract class MethodProcessorEventConsumer
@@ -26,14 +26,13 @@
   public void finished(AppView<AppInfoWithLiveness> appView) {}
 
   public static MethodProcessorEventConsumer createForD8(
-      ArtProfileCollectionAdditions artProfileCollectionAdditions) {
-    return ArtProfileRewritingMethodProcessorEventConsumer.attach(
-        artProfileCollectionAdditions, empty());
+      ProfileCollectionAdditions profileCollectionAdditions) {
+    return ProfileRewritingMethodProcessorEventConsumer.attach(profileCollectionAdditions, empty());
   }
 
   public static MethodProcessorEventConsumer createForR8(
       AppView<? extends AppInfoWithClassHierarchy> appView) {
-    return ArtProfileRewritingMethodProcessorEventConsumer.attach(appView, empty());
+    return ProfileRewritingMethodProcessorEventConsumer.attach(appView, empty());
   }
 
   public static MethodProcessorEventConsumer empty() {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
index cf7a083..b2959f2 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
@@ -33,7 +33,7 @@
 import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
 import com.android.tools.r8.position.MethodPosition;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ProfileCollectionAdditions;
 import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.ThreadUtils;
@@ -54,10 +54,10 @@
     LambdaDeserializationMethodRemover.run(appView);
     workaroundAbstractMethodOnNonAbstractClassVerificationBug(executorService);
     DexApplication application = appView.appInfo().app();
-    ArtProfileCollectionAdditions artProfileCollectionAdditions =
-        ArtProfileCollectionAdditions.create(appView);
+    ProfileCollectionAdditions profileCollectionAdditions =
+        ProfileCollectionAdditions.create(appView);
     D8MethodProcessor methodProcessor =
-        new D8MethodProcessor(artProfileCollectionAdditions, this, executorService);
+        new D8MethodProcessor(profileCollectionAdditions, this, executorService);
     InterfaceProcessor interfaceProcessor = InterfaceProcessor.create(appView);
 
     timing.begin("IR conversion");
@@ -90,7 +90,7 @@
       new L8InnerOuterAttributeEraser(appView).run();
     }
 
-    processCovariantReturnTypeAnnotations(builder, artProfileCollectionAdditions);
+    processCovariantReturnTypeAnnotations(builder, profileCollectionAdditions);
 
     timing.end();
 
@@ -100,7 +100,7 @@
             appView.appInfo().getSyntheticItems().commit(application),
             appView.appInfo().getMainDexInfo()));
 
-    artProfileCollectionAdditions.commit(appView);
+    profileCollectionAdditions.commit(appView);
   }
 
   void convertMethods(
@@ -310,7 +310,7 @@
     CfPostProcessingDesugaringEventConsumer eventConsumer =
         CfPostProcessingDesugaringEventConsumer.createForD8(
             appView,
-            methodProcessor.getArtProfileCollectionAdditions(),
+            methodProcessor.getProfileCollectionAdditions(),
             methodProcessor,
             instructionDesugaring);
     methodProcessor.newWave();
@@ -341,12 +341,11 @@
   }
 
   private void processCovariantReturnTypeAnnotations(
-      Builder<?> builder, ArtProfileCollectionAdditions artProfileCollectionAdditions) {
+      Builder<?> builder, ProfileCollectionAdditions profileCollectionAdditions) {
     if (covariantReturnTypeAnnotationTransformer != null) {
       covariantReturnTypeAnnotationTransformer.process(
           builder,
-          CovariantReturnTypeAnnotationTransformerEventConsumer.create(
-              artProfileCollectionAdditions));
+          CovariantReturnTypeAnnotationTransformerEventConsumer.create(profileCollectionAdditions));
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/InvokeExtractor.java b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/InvokeExtractor.java
index 3c940a6..34e00ef 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/InvokeExtractor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/InvokeExtractor.java
@@ -118,7 +118,7 @@
                   appView.appInfo().resolveMethodLegacy(method, isInterface);
               if (resolution.isVirtualTarget()) {
                 LookupResult lookupResult =
-                    resolution.lookupVirtualDispatchTargets(context.getHolder(), appView.appInfo());
+                    resolution.lookupVirtualDispatchTargets(context.getHolder(), appView);
                 if (lookupResult.isLookupResultSuccess()) {
                   ProgramMethodSet targets = ProgramMethodSet.create();
                   lookupResult
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 508c314..47d762c 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
@@ -1015,6 +1015,20 @@
               BackportedMethods::LongMethods_toUnsignedStringWithRadix,
               "toUnsignedStringWithRadix"));
 
+      // Method
+      type = factory.methodType;
+
+      // int Method.getParameterCount()
+      name = factory.createString("getParameterCount");
+      proto = factory.createProto(factory.intType);
+      method = factory.createMethod(type, proto, name);
+      addProvider(
+          new StatifyingMethodGenerator(
+              method,
+              BackportedMethods::MethodMethods_getParameterCount,
+              "getParameterCount",
+              type));
+
       // String
       type = factory.stringType;
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java
index 49a7dd8..0db5e13 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java
@@ -14,8 +14,8 @@
 import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceSynthesizerEventConsumer.L8ProgramEmulatedInterfaceSynthesizerEventConsumer;
 import com.android.tools.r8.ir.desugar.records.RecordDesugaringEventConsumer.RecordClassSynthesizerDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.varhandle.VarHandleDesugaringEventConsumer;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer;
+import com.android.tools.r8.profile.art.rewriting.ProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ProfileRewritingCfClassSynthesizerDesugaringEventConsumer;
 import com.google.common.collect.Sets;
 import java.util.Set;
 
@@ -29,19 +29,18 @@
   protected CfClassSynthesizerDesugaringEventConsumer() {}
 
   public static CfClassSynthesizerDesugaringEventConsumer createForD8(
-      AppView<?> appView, ArtProfileCollectionAdditions artProfileCollectionAdditions) {
+      AppView<?> appView, ProfileCollectionAdditions profileCollectionAdditions) {
     CfClassSynthesizerDesugaringEventConsumer eventConsumer =
         new D8R8CfClassSynthesizerDesugaringEventConsumer();
-    return ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer.attach(
-        appView, eventConsumer, artProfileCollectionAdditions);
+    return ProfileRewritingCfClassSynthesizerDesugaringEventConsumer.attach(
+        appView, eventConsumer, profileCollectionAdditions);
   }
 
   public static CfClassSynthesizerDesugaringEventConsumer createForR8(
       AppView<? extends AppInfoWithClassHierarchy> appView) {
     CfClassSynthesizerDesugaringEventConsumer eventConsumer =
         new D8R8CfClassSynthesizerDesugaringEventConsumer();
-    return ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer.attach(
-        appView, eventConsumer);
+    return ProfileRewritingCfClassSynthesizerDesugaringEventConsumer.attach(appView, eventConsumer);
   }
 
   public void finished(AppView<? extends AppInfoWithClassHierarchy> appView) {}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
index 7290623..509a1ff 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
@@ -32,8 +32,8 @@
 import com.android.tools.r8.ir.desugar.records.RecordDesugaringEventConsumer.RecordInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.twr.TwrCloseResourceDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.varhandle.VarHandleDesugaringEventConsumer;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingCfInstructionDesugaringEventConsumer;
+import com.android.tools.r8.profile.art.rewriting.ProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ProfileRewritingCfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.shaking.Enqueuer.SyntheticAdditions;
 import com.android.tools.r8.shaking.KeepMethodInfo.Joiner;
 import com.google.common.collect.Sets;
@@ -70,22 +70,22 @@
 
   public static CfInstructionDesugaringEventConsumer createForD8(
       AppView<?> appView,
-      ArtProfileCollectionAdditions artProfileCollectionAdditions,
+      ProfileCollectionAdditions profileCollectionAdditions,
       ClassConverterResult.Builder classConverterResultBuilder,
       D8MethodProcessor methodProcessor) {
     D8CfInstructionDesugaringEventConsumer eventConsumer =
         new D8CfInstructionDesugaringEventConsumer(
             appView, classConverterResultBuilder, methodProcessor);
     CfInstructionDesugaringEventConsumer outermostEventConsumer =
-        ArtProfileRewritingCfInstructionDesugaringEventConsumer.attach(
-            appView, artProfileCollectionAdditions, eventConsumer);
+        ProfileRewritingCfInstructionDesugaringEventConsumer.attach(
+            appView, profileCollectionAdditions, eventConsumer);
     eventConsumer.setOutermostEventConsumer(outermostEventConsumer);
     return outermostEventConsumer;
   }
 
   public static CfInstructionDesugaringEventConsumer createForR8(
       AppView<? extends AppInfoWithClassHierarchy> appView,
-      ArtProfileCollectionAdditions artProfileCollectionAdditions,
+      ProfileCollectionAdditions profileCollectionAdditions,
       BiConsumer<LambdaClass, ProgramMethod> lambdaClassConsumer,
       BiConsumer<ConstantDynamicClass, ProgramMethod> constantDynamicClassConsumer,
       BiConsumer<ProgramMethod, ProgramMethod> twrCloseResourceMethodConsumer,
@@ -99,8 +99,8 @@
             twrCloseResourceMethodConsumer,
             additions,
             companionMethodConsumer);
-    return ArtProfileRewritingCfInstructionDesugaringEventConsumer.attach(
-        appView, artProfileCollectionAdditions, eventConsumer);
+    return ProfileRewritingCfInstructionDesugaringEventConsumer.attach(
+        appView, profileCollectionAdditions, eventConsumer);
   }
 
   public abstract List<ProgramMethod> finalizeDesugaring();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
index 7a38cf1..adeecfe 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
@@ -18,8 +18,8 @@
 import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterPostProcessingEventConsumer;
 import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper;
 import com.android.tools.r8.ir.desugar.itf.InterfaceProcessingDesugaringEventConsumer;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingCfPostProcessingDesugaringEventConsumer;
+import com.android.tools.r8.profile.art.rewriting.ProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ProfileRewritingCfPostProcessingDesugaringEventConsumer;
 import com.android.tools.r8.shaking.Enqueuer.SyntheticAdditions;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import java.util.Collections;
@@ -39,25 +39,25 @@
 
   public static CfPostProcessingDesugaringEventConsumer createForD8(
       AppView<?> appView,
-      ArtProfileCollectionAdditions artProfileCollectionAdditions,
+      ProfileCollectionAdditions profileCollectionAdditions,
       D8MethodProcessor methodProcessor,
       CfInstructionDesugaringCollection instructionDesugaring) {
     CfPostProcessingDesugaringEventConsumer eventConsumer =
         new D8CfPostProcessingDesugaringEventConsumer(methodProcessor, instructionDesugaring);
-    return ArtProfileRewritingCfPostProcessingDesugaringEventConsumer.attach(
-        appView, artProfileCollectionAdditions, eventConsumer);
+    return ProfileRewritingCfPostProcessingDesugaringEventConsumer.attach(
+        appView, profileCollectionAdditions, eventConsumer);
   }
 
   public static CfPostProcessingDesugaringEventConsumer createForR8(
       AppView<?> appView,
       SyntheticAdditions additions,
-      ArtProfileCollectionAdditions artProfileCollectionAdditions,
+      ProfileCollectionAdditions profileCollectionAdditions,
       CfInstructionDesugaringCollection desugaring,
       BiConsumer<DexProgramClass, DexType> missingClassConsumer) {
     CfPostProcessingDesugaringEventConsumer eventConsumer =
         new R8PostProcessingDesugaringEventConsumer(additions, desugaring, missingClassConsumer);
-    return ArtProfileRewritingCfPostProcessingDesugaringEventConsumer.attach(
-        appView, artProfileCollectionAdditions, eventConsumer);
+    return ProfileRewritingCfPostProcessingDesugaringEventConsumer.attach(
+        appView, profileCollectionAdditions, eventConsumer);
   }
 
   public abstract Set<DexMethod> getNewlyLiveMethods();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformerEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformerEventConsumer.java
index dc4add0..86c74d8 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformerEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformerEventConsumer.java
@@ -5,20 +5,20 @@
 package com.android.tools.r8.ir.desugar;
 
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingCovariantReturnTypeAnnotationTransformerEventConsumer;
+import com.android.tools.r8.profile.art.rewriting.ProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ProfileRewritingCovariantReturnTypeAnnotationTransformerEventConsumer;
 
 public interface CovariantReturnTypeAnnotationTransformerEventConsumer {
 
   void acceptCovariantReturnTypeBridgeMethod(ProgramMethod bridge, ProgramMethod target);
 
   static CovariantReturnTypeAnnotationTransformerEventConsumer create(
-      ArtProfileCollectionAdditions artProfileCollectionAdditions) {
-    if (artProfileCollectionAdditions.isNop()) {
+      ProfileCollectionAdditions profileCollectionAdditions) {
+    if (profileCollectionAdditions.isNop()) {
       return empty();
     }
-    return ArtProfileRewritingCovariantReturnTypeAnnotationTransformerEventConsumer.attach(
-        artProfileCollectionAdditions, empty());
+    return ProfileRewritingCovariantReturnTypeAnnotationTransformerEventConsumer.attach(
+        profileCollectionAdditions, empty());
   }
 
   static EmptyCovariantReturnTypeAnnotationTransformerEventConsumer empty() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
index df7be1b..bbd4fe0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
@@ -6,6 +6,7 @@
 
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -68,6 +69,7 @@
   }
 
   private LambdaDescriptor(
+      AppView<?> appView,
       AppInfoWithClassHierarchy appInfo,
       ProgramMethod context,
       DexCallSite callSite,
@@ -94,7 +96,8 @@
     this.captures = captures;
 
     this.interfaces.add(mainInterface);
-    DexEncodedMethod targetMethod = context == null ? null : lookupTargetMethod(appInfo, context);
+    DexEncodedMethod targetMethod =
+        context == null ? null : lookupTargetMethod(appView, appInfo, context);
     if (targetMethod != null) {
       targetAccessFlags = targetMethod.accessFlags.copy();
       targetHolder = targetMethod.getHolderType();
@@ -114,7 +117,7 @@
   }
 
   private DexEncodedMethod lookupTargetMethod(
-      AppInfoWithClassHierarchy appInfo, ProgramMethod context) {
+      AppView<?> appView, AppInfoWithClassHierarchy appInfo, ProgramMethod context) {
     assert context != null;
     // Find the lambda's impl-method target.
     DexMethod method = implHandle.asMethod();
@@ -126,7 +129,7 @@
                   .resolveMethodOnLegacy(getImplReceiverType(), method, implHandle.isInterface)
                   .getSingleTarget();
         if (target == null) {
-            target = appInfo.lookupDirectTarget(method, context);
+            target = appInfo.lookupDirectTarget(method, context, appView, appInfo);
         }
         assert target == null
             || (implHandle.type.isInvokeInstance() && isInstanceMethod(target))
@@ -136,13 +139,13 @@
       }
 
       case INVOKE_STATIC: {
-          DexEncodedMethod target = appInfo.lookupStaticTarget(method, context);
+          DexEncodedMethod target = appInfo.lookupStaticTarget(method, context, appView, appInfo);
         assert target == null || target.accessFlags.isStatic();
         return target;
       }
 
       case INVOKE_CONSTRUCTOR: {
-          DexEncodedMethod target = appInfo.lookupDirectTarget(method, context);
+          DexEncodedMethod target = appInfo.lookupDirectTarget(method, context, appView, appInfo);
         assert target == null || target.accessFlags.isConstructor();
         return target;
       }
@@ -267,8 +270,11 @@
    * information, or null if match failed.
    */
   public static LambdaDescriptor tryInfer(
-      DexCallSite callSite, AppInfoWithClassHierarchy appInfo, ProgramMethod context) {
-    LambdaDescriptor descriptor = infer(callSite, appInfo, context);
+      DexCallSite callSite,
+      AppView<?> appView,
+      AppInfoWithClassHierarchy appInfo,
+      ProgramMethod context) {
+    LambdaDescriptor descriptor = infer(callSite, appView, appInfo, context);
     return descriptor == MATCH_FAILED ? null : descriptor;
   }
 
@@ -294,7 +300,10 @@
    * information, or MATCH_FAILED if match failed.
    */
   static LambdaDescriptor infer(
-      DexCallSite callSite, AppInfoWithClassHierarchy appInfo, ProgramMethod context) {
+      DexCallSite callSite,
+      AppView<?> appView,
+      AppInfoWithClassHierarchy appInfo,
+      ProgramMethod context) {
     if (!isLambdaMetafactoryMethod(callSite, appInfo)) {
       return LambdaDescriptor.MATCH_FAILED;
     }
@@ -337,6 +346,7 @@
     // Create a match.
     LambdaDescriptor match =
         new LambdaDescriptor(
+            appView,
             appInfo,
             context,
             callSite,
@@ -409,9 +419,9 @@
   }
 
   public static List<DexType> getInterfaces(
-      DexCallSite callSite, AppInfoWithClassHierarchy appInfo) {
+      DexCallSite callSite, AppView<? extends AppInfoWithClassHierarchy> appView) {
     // No need for the invocationContext to figure out only the interfaces.
-    LambdaDescriptor descriptor = infer(callSite, appInfo, null);
+    LambdaDescriptor descriptor = infer(callSite, appView, appView.appInfo(), null);
     if (descriptor == LambdaDescriptor.MATCH_FAILED) {
       return null;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index 40e5b9f..184dff0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -200,12 +200,14 @@
       ProgramAdditions programAdditions) {
     ensureCfCode(method);
     desugarings.forEach(d -> d.prepare(method, eventConsumer, programAdditions));
+    yieldingDesugarings.forEach(d -> d.prepare(method, eventConsumer, programAdditions));
   }
 
   @Override
   public void scan(ProgramMethod method, CfInstructionDesugaringEventConsumer eventConsumer) {
     ensureCfCode(method);
     desugarings.forEach(d -> d.scan(method, eventConsumer));
+    yieldingDesugarings.forEach(d -> d.scan(method, eventConsumer));
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
index 99392e1..7d65007 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
@@ -5773,6 +5773,30 @@
         ImmutableList.of());
   }
 
+  public static CfCode MethodMethods_getParameterCount(DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        1,
+        1,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/reflect/Method;"),
+                    factory.createProto(factory.createType("[Ljava/lang/Class;")),
+                    factory.createString("getParameterTypes")),
+                false),
+            new CfArrayLength(),
+            new CfReturn(ValueType.INT),
+            label1),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
   public static CfCode ObjectsMethods_checkFromIndexSize(DexItemFactory factory, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
index d260a73..63a6e08 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
@@ -157,7 +157,7 @@
       // Unconditionally throw as the RI.
       behaviour =
           resolution.isNoSuchMethodErrorResult(
-                  context.getContextClass(), appView.appInfoForDesugaring())
+                  context.getContextClass(), appView, appView.appInfoForDesugaring())
               ? THROW_NSME
               : THROW_ICCE;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java
index 292a3b3..f3e4457 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.cf.code.CfInvoke;
 import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClassAndMethod;
@@ -124,10 +125,11 @@
   private DexClassAndMethod getMethodForDesugaring(CfInvoke invoke, ProgramMethod context) {
     DexMethod invokedMethod = invoke.getMethod();
     // TODO(b/191656218): Use lookupInvokeSpecial instead when this is all to Cf.
+    AppInfoWithClassHierarchy appInfoForDesugaring = appView.appInfoForDesugaring();
     return invoke.isInvokeSuper(context.getHolderType())
-        ? appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, context)
-        : appView
-            .appInfoForDesugaring()
+        ? appInfoForDesugaring.lookupSuperTarget(
+            invokedMethod, context, appView, appInfoForDesugaring)
+        : appInfoForDesugaring
             .resolveMethodLegacy(invokedMethod, invoke.isInterface())
             .getResolutionPair();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClassesGenerator.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClassesGenerator.java
index b165a93..cb24e59 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClassesGenerator.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClassesGenerator.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.experimental.startup.StartupOrder;
 import com.android.tools.r8.features.ClassToFeatureSplitMap;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.DexApplication;
@@ -148,8 +147,7 @@
               dexApplication,
               ClassToFeatureSplitMap.createEmptyClassToFeatureSplitMap(),
               MainDexInfo.none(),
-              GlobalSyntheticsStrategy.forNonSynthesizing(),
-              StartupOrder.empty());
+              GlobalSyntheticsStrategy.forNonSynthesizing());
 
       List<DexMethod> backports =
           BackportedMethodRewriter.generateListOfBackportedMethods(dexApplication, options);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
index fd30618..85154ce 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
@@ -92,7 +92,8 @@
       return DesugarDescription.nothing();
     }
     if (cfInvoke.isInvokeSuper(context.getHolderType())) {
-      DexClassAndMethod superTarget = appInfo.lookupSuperTarget(invokedMethod, context);
+      DexClassAndMethod superTarget =
+          appInfo.lookupSuperTarget(invokedMethod, context, appView, appInfo);
       if (superTarget != null) {
         assert !superTarget.getDefinition().isStatic();
         return computeNonStaticRetarget(superTarget.getReference(), true);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java
index ff3989a..ccf096e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java
@@ -189,9 +189,11 @@
     } else if (resolutionResult.isFailedResolution()) {
       FailedResolutionResult failedResolutionResult = resolutionResult.asFailedResolution();
       AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring();
-      if (failedResolutionResult.isIllegalAccessErrorResult(context.getHolder(), appInfo)) {
+      if (failedResolutionResult.isIllegalAccessErrorResult(
+          context.getHolder(), appView, appInfo)) {
         return UtilityMethodsForCodeOptimizations::synthesizeThrowIllegalAccessErrorMethod;
-      } else if (failedResolutionResult.isNoSuchMethodErrorResult(context.getHolder(), appInfo)) {
+      } else if (failedResolutionResult.isNoSuchMethodErrorResult(
+          context.getHolder(), appView, appInfo)) {
         return UtilityMethodsForCodeOptimizations::synthesizeThrowNoSuchMethodErrorMethod;
       } else if (failedResolutionResult.isIncompatibleClassChangeErrorResult()) {
         return UtilityMethodsForCodeOptimizations
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
index 85b530e..922fd4d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
@@ -850,11 +850,11 @@
           addICCEThrowingMethod(method, clazz, resolutionResult.asFailedResolution());
           return;
         }
-        if (resolutionResult.isNoSuchMethodErrorResult(clazz, appInfo)) {
+        if (resolutionResult.isNoSuchMethodErrorResult(clazz, appView, appInfo)) {
           addNoSuchMethodErrorThrowingMethod(method, clazz, resolutionResult.asFailedResolution());
           return;
         }
-        assert resolutionResult.isIllegalAccessErrorResult(clazz, appInfo);
+        assert resolutionResult.isIllegalAccessErrorResult(clazz, appView, appInfo);
         addIllegalAccessErrorThrowingMethod(method, clazz, resolutionResult.asFailedResolution());
         return;
       }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index 6bc5e1d..15e9f06 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -329,7 +329,7 @@
               .asSingleResolution();
       if (resolution != null
           && resolution.getResolvedMethod().isPrivate()
-          && resolution.isAccessibleFrom(context, appInfoForDesugaring).isTrue()) {
+          && resolution.isAccessibleFrom(context, appView, appInfoForDesugaring).isTrue()) {
         return DesugarDescription.nothing();
       }
       if (resolution != null && resolution.getResolvedMethod().isStatic()) {
@@ -503,7 +503,7 @@
             .asSingleResolution();
     if (resolution != null
         && resolution.getResolvedMethod().isPrivate()
-        && resolution.isAccessibleFrom(context, appInfoForDesugaring).isTrue()) {
+        && resolution.isAccessibleFrom(context, appView, appInfoForDesugaring).isTrue()) {
       // TODO(b/198267586): What about the private in-accessible case?
       return computeInvokeDirect(holder, invoke, context);
     }
@@ -724,7 +724,9 @@
       // WARNING: This may result in incorrect code on older platforms!
       // Retarget call to an appropriate method of companion class.
       if (resolutionResult.getResolvedMethod().isPrivateMethod()) {
-        if (resolutionResult.isAccessibleFrom(context, appView.appInfoForDesugaring()).isFalse()) {
+        if (resolutionResult
+            .isAccessibleFrom(context, appView, appView.appInfoForDesugaring())
+            .isFalse()) {
           // TODO(b/145775365): This should throw IAE.
           return computeInvokeAsThrowRewrite(invoke, null, context);
         }
@@ -803,8 +805,11 @@
 
   private DesugarDescription computeEmulatedInterfaceInvokeSpecial(
       DexClass clazz, DexMethod invokedMethod, ProgramMethod context) {
+    AppInfoWithClassHierarchy appInfoForDesugaring = appView.appInfoForDesugaring();
     DexClassAndMethod superTarget =
-        appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, context);
+        appView
+            .appInfoForDesugaring()
+            .lookupSuperTarget(invokedMethod, context, appView, appInfoForDesugaring);
     if (clazz.isInterface()
         && clazz.isLibraryClass()
         && helper.isInDesugaredLibrary(clazz)
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
index c33361a..aea4f4b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
@@ -163,7 +163,8 @@
       MethodProcessingContext methodProcessingContext,
       DesugarInvoke desugarInvoke) {
     LambdaDescriptor descriptor =
-        LambdaDescriptor.tryInfer(invoke.getCallSite(), appView.appInfoForDesugaring(), context);
+        LambdaDescriptor.tryInfer(
+            invoke.getCallSite(), appView, appView.appInfoForDesugaring(), context);
     if (descriptor == null) {
       return null;
     }
@@ -191,6 +192,7 @@
     return instruction.isInvokeDynamic()
         && LambdaDescriptor.tryInfer(
                 instruction.asInvokeDynamic().getCallSite(),
+                appView,
                 appView.appInfoForDesugaring(),
                 context)
             != null;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java
index c7e64b08..4e037ad 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java
@@ -18,7 +18,7 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.D8MethodProcessor;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingNestBasedAccessDesugaringEventConsumer;
+import com.android.tools.r8.profile.art.rewriting.ProfileRewritingNestBasedAccessDesugaringEventConsumer;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.google.common.collect.Iterables;
 import java.util.ArrayList;
@@ -92,8 +92,8 @@
         });
 
     NestBasedAccessDesugaringEventConsumer eventConsumer =
-        ArtProfileRewritingNestBasedAccessDesugaringEventConsumer.attach(
-            methodProcessor.getArtProfileCollectionAdditions(),
+        ProfileRewritingNestBasedAccessDesugaringEventConsumer.attach(
+            methodProcessor.getProfileCollectionAdditions(),
             new NestBasedAccessDesugaringEventConsumer() {
 
               @Override
@@ -143,7 +143,7 @@
 
     NestBasedAccessDesugaringUseRegistry(
         ClasspathMethod context, NestBasedAccessDesugaringEventConsumer eventConsumer) {
-      super(appView, context);
+      super(D8NestBasedAccessDesugaring.this.appView, context);
       this.eventConsumer = eventConsumer;
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
index 91b7639..03304e5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
@@ -168,7 +168,10 @@
                 CfInvokeDynamic cfInvokeDynamic = instruction.asInvokeDynamic();
                 LambdaDescriptor lambdaDescriptor =
                     LambdaDescriptor.tryInfer(
-                        cfInvokeDynamic.getCallSite(), appView.appInfoForDesugaring(), method);
+                        cfInvokeDynamic.getCallSite(),
+                        appView,
+                        appView.appInfoForDesugaring(),
+                        method);
                 if (lambdaDescriptor != null) {
                   DexMember<?, ?> member = lambdaDescriptor.implHandle.member;
                   if (needsDesugaring(member, method)) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index 0db211d..a2572eb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -180,7 +180,7 @@
     }
 
     // Abort inlining attempt if method -> target access is not right.
-    if (resolutionResult.isAccessibleFrom(method, appView.appInfo()).isPossiblyFalse()) {
+    if (resolutionResult.isAccessibleFrom(method, appView).isPossiblyFalse()) {
       whyAreYouNotInliningReporter.reportInaccessible();
       return false;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
index 08f3912..f95b59e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
@@ -320,7 +320,7 @@
 
   /** This rebinds invoke-super instructions to their most specific target. */
   private DexClass rebindSuperInvokeToMostSpecific(DexMethod target, ProgramMethod context) {
-    DexClassAndMethod method = appView.appInfo().lookupSuperTarget(target, context);
+    DexClassAndMethod method = appView.appInfo().lookupSuperTarget(target, context, appView);
     if (method == null) {
       return null;
     }
@@ -375,7 +375,7 @@
             .asSingleResolution();
     if (resolutionResult == null
         || resolutionResult
-            .isAccessibleForVirtualDispatchFrom(context, appView.appInfo())
+            .isAccessibleForVirtualDispatchFrom(context, appView)
             .isPossiblyFalse()) {
       // Method does not resolve or is not accessible.
       return target;
@@ -391,7 +391,7 @@
         appView.appInfo().resolveMethodOnClassLegacy(receiverType, target).asSingleResolution();
     if (newResolutionResult == null
         || newResolutionResult
-            .isAccessibleForVirtualDispatchFrom(context, appView.appInfo())
+            .isAccessibleForVirtualDispatchFrom(context, appView)
             .isPossiblyFalse()
         || !newResolutionResult
             .getResolvedMethod()
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
index 900065d..7aae8a5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
@@ -115,7 +115,7 @@
                   .asSingleResolution();
           if (resolutionResult == null
               || resolutionResult
-                  .isAccessibleFrom(context, appInfoWithLiveness)
+                  .isAccessibleFrom(context, appViewWithLiveness)
                   .isPossiblyFalse()) {
             continue;
           }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
index 59c066f..3452d98 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
@@ -227,13 +227,16 @@
       MethodResolutionResult resolutionResult,
       ProgramMethod context,
       TriFunction<
-              MethodResolutionResult, DexProgramClass, AppInfoWithClassHierarchy, DexEncodedMethod>
+              MethodResolutionResult,
+              DexProgramClass,
+              AppView<? extends AppInfoWithClassHierarchy>,
+              DexEncodedMethod>
           lookup) {
     if (!resolutionResult.isSingleResolution()) {
       return null;
     }
     DexEncodedMethod dexEncodedMethod =
-        lookup.apply(resolutionResult, context.getHolder(), appView.appInfo());
+        lookup.apply(resolutionResult, context.getHolder(), appView);
     if (!isVerticalClassMerging() || dexEncodedMethod != null) {
       return dexEncodedMethod;
     }
@@ -245,7 +248,7 @@
       return null;
     }
     DexEncodedMethod alternativeDexEncodedMethod =
-        lookup.apply(resolutionResult, superContext, appView.appInfo());
+        lookup.apply(resolutionResult, superContext, appView);
     if (alternativeDexEncodedMethod != null
         && alternativeDexEncodedMethod.getHolderType() == superContext.type) {
       return alternativeDexEncodedMethod;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
index 0f7e4bb..1767d8b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
@@ -332,7 +332,7 @@
       InvokeCustom invoke, Set<DexType> eligibleEnums, ProgramMethod context) {
     invoke.getCallSite().getMethodProto().forEachType(t -> markEnumEligible(t, eligibleEnums));
     LambdaDescriptor lambdaDescriptor =
-        LambdaDescriptor.tryInfer(invoke.getCallSite(), appView.appInfo(), context);
+        LambdaDescriptor.tryInfer(invoke.getCallSite(), appView, appView.appInfo(), context);
     if (lambdaDescriptor == null) {
       // Based on lambda we can see that enums cannot be unboxed if used in call site bootstrap
       // arguments, since there might be expectations on overrides. Enums used directly in the
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlineOptimizationEventConsumer.java b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlineOptimizationEventConsumer.java
index 86ac8ab..d9e8a39 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlineOptimizationEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlineOptimizationEventConsumer.java
@@ -6,7 +6,7 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingOutlineOptimizationEventConsumer;
+import com.android.tools.r8.profile.art.rewriting.ProfileRewritingOutlineOptimizationEventConsumer;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.Collection;
 
@@ -17,7 +17,7 @@
   void finished(AppView<AppInfoWithLiveness> appView);
 
   static OutlineOptimizationEventConsumer create(AppView<AppInfoWithLiveness> appView) {
-    return ArtProfileRewritingOutlineOptimizationEventConsumer.attach(appView, empty());
+    return ProfileRewritingOutlineOptimizationEventConsumer.attach(appView, empty());
   }
 
   static EmptyOutlineOptimizationEventConsumer empty() {
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index cc88ca9..f10a6cf 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -30,6 +30,7 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeAnnotation;
 import com.android.tools.r8.graph.DexValue;
 import com.android.tools.r8.graph.DexValue.DexValueAnnotation;
 import com.android.tools.r8.graph.DexValue.DexValueArray;
@@ -72,6 +73,7 @@
 import org.objectweb.asm.MethodTooLargeException;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Type;
+import org.objectweb.asm.TypePath;
 import org.objectweb.asm.tree.ClassNode;
 import org.objectweb.asm.tree.MethodNode;
 import org.objectweb.asm.util.CheckClassAdapter;
@@ -268,7 +270,8 @@
     assert SyntheticNaming.verifyNotInternalSynthetic(name);
     writer.visit(version.raw(), access, name, signature, superName, interfaces);
     appView.getSyntheticItems().writeAttributeIfIntermediateSyntheticClass(writer, clazz, appView);
-    writeAnnotations(writer::visitAnnotation, clazz.annotations().annotations);
+    writeAnnotations(
+        writer::visitAnnotation, writer::visitTypeAnnotation, clazz.annotations().annotations);
     ImmutableMap<DexString, DexValue> defaults = getAnnotationDefaults(clazz.annotations());
 
     if (clazz.getEnclosingMethodAttribute() != null) {
@@ -454,7 +457,8 @@
     String signature = field.getGenericSignature().toRenamedString(getNamingLens(), isTypeMissing);
     Object value = getStaticValue(field);
     FieldVisitor visitor = writer.visitField(access, name, desc, signature, value);
-    writeAnnotations(visitor::visitAnnotation, field.annotations().annotations);
+    writeAnnotations(
+        visitor::visitAnnotation, visitor::visitTypeAnnotation, field.annotations().annotations);
     visitor.visitEnd();
   }
 
@@ -488,7 +492,10 @@
       }
     }
     writeMethodParametersAnnotation(visitor, definition.annotations().annotations);
-    writeAnnotations(visitor::visitAnnotation, definition.annotations().annotations);
+    writeAnnotations(
+        visitor::visitAnnotation,
+        visitor::visitTypeAnnotation,
+        definition.annotations().annotations);
     writeParameterAnnotations(visitor, definition.parameterAnnotationsList);
     if (!definition.shouldNotHaveCode()) {
       writeCode(method, classFileVersion, namingLens, rewriter, visitor);
@@ -525,11 +532,13 @@
         parameterAnnotations.getAnnotableParameterCount(), true);
     visitor.visitAnnotableParameterCount(
         parameterAnnotations.getAnnotableParameterCount(), false);
-
     for (int i = 0; i < parameterAnnotations.size(); i++) {
       int iFinal = i;
       writeAnnotations(
           (d, vis) -> visitor.visitParameterAnnotation(iFinal, d, vis),
+          (typeRef, typePath, desc, visible) -> {
+            throw new Unreachable("Type annotations are not placed on parameters");
+          },
           parameterAnnotations.get(i).annotations);
     }
   }
@@ -538,7 +547,14 @@
     AnnotationVisitor visit(String desc, boolean visible);
   }
 
-  private void writeAnnotations(AnnotationConsumer visitor, DexAnnotation[] annotations) {
+  private interface TypeAnnotationConsumer {
+    AnnotationVisitor visit(int typeRef, TypePath typePath, String desc, boolean visible);
+  }
+
+  private void writeAnnotations(
+      AnnotationConsumer visitor,
+      TypeAnnotationConsumer typeAnnotationVisitor,
+      DexAnnotation[] annotations) {
     for (DexAnnotation dexAnnotation : annotations) {
       if (dexAnnotation.visibility == DexAnnotation.VISIBILITY_SYSTEM) {
         // Annotations with VISIBILITY_SYSTEM are not annotations in CF, but are special
@@ -546,10 +562,14 @@
         // signature, throws.
         continue;
       }
+      String desc = getNamingLens().lookupDescriptor(dexAnnotation.annotation.type).toString();
+      boolean visible = dexAnnotation.visibility == DexAnnotation.VISIBILITY_RUNTIME;
+      DexTypeAnnotation dexTypeAnnotation = dexAnnotation.asTypeAnnotation();
       AnnotationVisitor v =
-          visitor.visit(
-              getNamingLens().lookupDescriptor(dexAnnotation.annotation.type).toString(),
-              dexAnnotation.visibility == DexAnnotation.VISIBILITY_RUNTIME);
+          dexTypeAnnotation == null
+              ? visitor.visit(desc, visible)
+              : typeAnnotationVisitor.visit(
+                  dexTypeAnnotation.getTypeRef(), dexTypeAnnotation.getTypePath(), desc, visible);
       if (v != null) {
         writeAnnotation(v, dexAnnotation.annotation);
         v.visitEnd();
diff --git a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
index e09e9f5..689d5ff 100644
--- a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
@@ -3,17 +3,21 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.lightir;
 
+import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
+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;
 import com.android.tools.r8.ir.code.ArrayLength;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.Cmp;
+import com.android.tools.r8.ir.code.Cmp.Bias;
 import com.android.tools.r8.ir.code.ConstNumber;
 import com.android.tools.r8.ir.code.ConstString;
 import com.android.tools.r8.ir.code.DebugLocalWrite;
@@ -23,10 +27,15 @@
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.If;
 import com.android.tools.r8.ir.code.IfType;
+import com.android.tools.r8.ir.code.InstanceGet;
+import com.android.tools.r8.ir.code.InstancePut;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.ir.code.InvokeInterface;
+import com.android.tools.r8.ir.code.InvokeStatic;
 import com.android.tools.r8.ir.code.InvokeVirtual;
 import com.android.tools.r8.ir.code.MoveException;
+import com.android.tools.r8.ir.code.NewInstance;
 import com.android.tools.r8.ir.code.NumberGenerator;
 import com.android.tools.r8.ir.code.NumericType;
 import com.android.tools.r8.ir.code.Phi;
@@ -91,8 +100,6 @@
         AppView<?> appView,
         LirDecodingStrategy<Value, EV> strategy) {
       super(code);
-      assert code.getPositionTable().length > 0;
-      assert code.getPositionTable()[0].fromInstructionIndex == 0;
       this.appView = appView;
       this.code = code;
       this.strategy = strategy;
@@ -370,6 +377,23 @@
       addInstruction(instruction);
     }
 
+    @Override
+    public void onInvokeStatic(DexMethod target, List<EV> arguments) {
+      // TODO(b/225838009): Maintain is-interface bit.
+      Value dest = getInvokeInstructionOutputValue(target);
+      List<Value> ssaArgumentValues = getValues(arguments);
+      InvokeStatic instruction = new InvokeStatic(target, dest, ssaArgumentValues);
+      addInstruction(instruction);
+    }
+
+    @Override
+    public void onInvokeInterface(DexMethod target, List<EV> arguments) {
+      Value dest = getInvokeInstructionOutputValue(target);
+      List<Value> ssaArgumentValues = getValues(arguments);
+      InvokeInterface instruction = new InvokeInterface(target, dest, ssaArgumentValues);
+      addInstruction(instruction);
+    }
+
     private Value getInvokeInstructionOutputValue(DexMethod target) {
       return target.getReturnType().isVoidType()
           ? null
@@ -377,12 +401,30 @@
     }
 
     @Override
+    public void onNewInstance(DexType clazz) {
+      TypeElement type = TypeElement.fromDexType(clazz, Nullability.definitelyNotNull(), appView);
+      Value dest = getOutValueForNextInstruction(type);
+      addInstruction(new NewInstance(clazz, dest));
+    }
+
+    @Override
     public void onStaticGet(DexField field) {
       Value dest = getOutValueForNextInstruction(field.getTypeElement(appView));
       addInstruction(new StaticGet(dest, field));
     }
 
     @Override
+    public void onInstanceGet(DexField field, EV object) {
+      Value dest = getOutValueForNextInstruction(field.getTypeElement(appView));
+      addInstruction(new InstanceGet(dest, getValue(object), field));
+    }
+
+    @Override
+    public void onInstancePut(DexField field, EV object, EV value) {
+      addInstruction(new InstancePut(field, getValue(object), getValue(value)));
+    }
+
+    @Override
     public void onReturnVoid() {
       addInstruction(new Return());
       closeCurrentBlock();
@@ -425,5 +467,39 @@
       dest.setType(type);
       addInstruction(new DebugLocalWrite(dest, src));
     }
+
+    @Override
+    public void onCmpInstruction(int opcode, EV leftIndex, EV rightIndex) {
+      NumericType type;
+      Bias bias;
+      switch (opcode) {
+        case LirOpcodes.LCMP:
+          type = NumericType.LONG;
+          bias = Bias.NONE;
+          break;
+        case LirOpcodes.FCMPL:
+          type = NumericType.FLOAT;
+          bias = Bias.LT;
+          break;
+        case LirOpcodes.FCMPG:
+          type = NumericType.FLOAT;
+          bias = Bias.GT;
+          break;
+        case LirOpcodes.DCMPL:
+          type = NumericType.DOUBLE;
+          bias = Bias.LT;
+          break;
+        case LirOpcodes.DCMPG:
+          type = NumericType.DOUBLE;
+          bias = Bias.GT;
+          break;
+        default:
+          throw new Unreachable("Unexpected cmp opcode: " + opcode);
+      }
+      Value leftValue = getValue(leftIndex);
+      Value rightValue = getValue(rightIndex);
+      Value dest = getOutValueForNextInstruction(TypeElement.getInt());
+      addInstruction(new Cmp(type, bias, dest, leftValue, rightValue));
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
index fd4113a..8209c80 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
@@ -15,6 +15,8 @@
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.Cmp;
+import com.android.tools.r8.ir.code.Cmp.Bias;
 import com.android.tools.r8.ir.code.IRMetadata;
 import com.android.tools.r8.ir.code.IfType;
 import com.android.tools.r8.ir.code.NumericType;
@@ -208,6 +210,11 @@
         opcode, Collections.emptyList(), Collections.singletonList(value));
   }
 
+  private LirBuilder<V, EV> addTwoValueInstruction(int opcode, V leftValue, V rightValue) {
+    return addInstructionTemplate(
+        opcode, Collections.emptyList(), ImmutableList.of(leftValue, rightValue));
+  }
+
   private LirBuilder<V, EV> addInstructionTemplate(
       int opcode, List<DexItem> items, List<V> values) {
     assert values.size() < MAX_VALUE_COUNT;
@@ -291,6 +298,16 @@
     return addOneItemInstruction(LirOpcodes.GETSTATIC, field);
   }
 
+  public LirBuilder<V, EV> addInstanceGet(DexField field, V object) {
+    return addInstructionTemplate(
+        LirOpcodes.GETFIELD, Collections.singletonList(field), Collections.singletonList(object));
+  }
+
+  public LirBuilder<V, EV> addInstancePut(DexField field, V object, V value) {
+    return addInstructionTemplate(
+        LirOpcodes.PUTFIELD, Collections.singletonList(field), ImmutableList.of(object, value));
+  }
+
   public LirBuilder<V, EV> addInvokeInstruction(int opcode, DexMethod method, List<V> arguments) {
     return addInstructionTemplate(opcode, Collections.singletonList(method), arguments);
   }
@@ -303,6 +320,18 @@
     return addInvokeInstruction(LirOpcodes.INVOKEVIRTUAL, method, arguments);
   }
 
+  public LirBuilder<V, EV> addInvokeStatic(DexMethod method, List<V> arguments) {
+    return addInvokeInstruction(LirOpcodes.INVOKESTATIC, method, arguments);
+  }
+
+  public LirBuilder<V, EV> addInvokeInterface(DexMethod method, List<V> arguments) {
+    return addInvokeInstruction(LirOpcodes.INVOKEINTERFACE, method, arguments);
+  }
+
+  public LirBuilder<V, EV> addNewInstance(DexType clazz) {
+    return addOneItemInstruction(LirOpcodes.NEW, clazz);
+  }
+
   public LirBuilder<V, EV> addReturn(V value) {
     throw new Unimplemented();
   }
@@ -435,4 +464,21 @@
         debugTable,
         strategy.getStrategyInfo());
   }
+
+  private int getCmpOpcode(NumericType type, Cmp.Bias bias) {
+    switch (type) {
+      case LONG:
+        return LirOpcodes.LCMP;
+      case FLOAT:
+        return bias == Cmp.Bias.LT ? LirOpcodes.FCMPL : LirOpcodes.FCMPG;
+      case DOUBLE:
+        return bias == Cmp.Bias.LT ? LirOpcodes.DCMPL : LirOpcodes.DCMPG;
+      default:
+        throw new Unreachable("Cmp has unknown type " + type);
+    }
+  }
+
+  public LirBuilder<V, EV> addCmp(NumericType type, Bias bias, V leftValue, V rightValue) {
+    return addTwoValueInstruction(getCmpOpcode(type, bias), leftValue, rightValue);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java b/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
index ed94301..a83524c 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
@@ -106,6 +106,18 @@
     onInvokeMethodInstruction(method, arguments);
   }
 
+  public void onInvokeStatic(DexMethod method, List<EV> arguments) {
+    onInvokeMethodInstruction(method, arguments);
+  }
+
+  public void onInvokeInterface(DexMethod method, List<EV> arguments) {
+    onInvokeMethodInstruction(method, arguments);
+  }
+
+  public void onNewInstance(DexType clazz) {
+    onInstruction();
+  }
+
   public void onFieldInstruction(DexField field) {
     onInstruction();
   }
@@ -114,6 +126,10 @@
     onFieldInstruction(field);
   }
 
+  public abstract void onInstanceGet(DexField field, EV object);
+
+  public abstract void onInstancePut(DexField field, EV object, EV value);
+
   public void onReturnVoid() {
     onInstruction();
   }
@@ -130,6 +146,11 @@
     onInstruction();
   }
 
+  public void onCmpInstruction(int opcode, EV leftValue, EV rightValue) {
+    assert LirOpcodes.LCMP <= opcode && opcode <= LirOpcodes.DCMPG;
+    onInstruction();
+  }
+
   private DexItem getConstantItem(int index) {
     return code.getConstantItem(index);
   }
@@ -204,12 +225,50 @@
           onInvokeVirtual(target, arguments);
           return;
         }
+      case LirOpcodes.INVOKESTATIC:
+        {
+          DexMethod target = getInvokeInstructionTarget(view);
+          List<EV> arguments = getInvokeInstructionArguments(view);
+          onInvokeStatic(target, arguments);
+          return;
+        }
+      case LirOpcodes.INVOKEINTERFACE:
+        {
+          DexMethod target = getInvokeInstructionTarget(view);
+          List<EV> arguments = getInvokeInstructionArguments(view);
+          onInvokeInterface(target, arguments);
+          return;
+        }
+      case LirOpcodes.NEW:
+        {
+          DexItem item = getConstantItem(view.getNextConstantOperand());
+          if (item instanceof DexType) {
+            onNewInstance((DexType) item);
+            return;
+          }
+          throw new Unimplemented();
+        }
       case LirOpcodes.GETSTATIC:
         {
           DexField field = (DexField) getConstantItem(view.getNextConstantOperand());
           onStaticGet(field);
           return;
         }
+      case LirOpcodes.GETFIELD:
+        {
+          DexField field = (DexField) getConstantItem(view.getNextConstantOperand());
+          EV object = getNextValueOperand(view);
+          onInstanceGet(field, object);
+          return;
+        }
+      case LirOpcodes.PUTFIELD:
+        {
+          DexField field = (DexField) getConstantItem(view.getNextConstantOperand());
+          EV object = getNextValueOperand(view);
+          EV value = getNextValueOperand(view);
+          onInstancePut(field, object, value);
+          return;
+        }
       case LirOpcodes.RETURN:
         {
           onReturnVoid();
@@ -252,6 +311,17 @@
           onDebugLocalWrite(srcIndex);
           return;
         }
+      case LirOpcodes.LCMP:
+      case LirOpcodes.FCMPL:
+      case LirOpcodes.FCMPG:
+      case LirOpcodes.DCMPL:
+      case LirOpcodes.DCMPG:
+        {
+          EV leftValue = getNextValueOperand(view);
+          EV rightValue = getNextValueOperand(view);
+          onCmpInstruction(opcode, leftValue, rightValue);
+          return;
+        }
       default:
         throw new Unimplemented("No dispatch for opcode " + LirOpcodes.toString(opcode));
     }
diff --git a/src/main/java/com/android/tools/r8/lightir/LirPrinter.java b/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
index f4a840b..33dedd6 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.ir.code.IfType;
 import com.android.tools.r8.ir.code.NumericType;
 import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
 import java.util.List;
 
 public class LirPrinter<EV> extends LirParsedInstructionCallback<EV> {
@@ -60,6 +61,11 @@
     return instructionIndex < 0 ? "--" : ("" + instructionIndex);
   }
 
+  @SafeVarargs
+  private void appendValueArguments(EV... arguments) {
+    appendValueArguments(Arrays.asList(arguments));
+  }
+
   private void appendValueArguments(List<EV> arguments) {
     for (int i = 0; i < arguments.size(); i++) {
       builder.append(fmtValueIndex(arguments.get(i))).append(' ');
@@ -183,6 +189,19 @@
   }
 
   @Override
+  public void onInstanceGet(DexField field, EV object) {
+    appendOutValue();
+    builder.append(field).append(' ');
+    appendValueArguments(object);
+  }
+
+  @Override
+  public void onInstancePut(DexField field, EV object, EV value) {
+    builder.append(field).append(' ');
+    appendValueArguments(object, value);
+  }
+
+  @Override
   public void onReturnVoid() {
     // Nothing to append.
   }
@@ -203,4 +222,10 @@
     appendValueArguments(operands);
     builder.append(type);
   }
+
+  @Override
+  public void onCmpInstruction(int opcode, EV leftValue, EV rightValue) {
+    appendOutValue();
+    appendValueArguments(leftValue, rightValue);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
index 14f7711..d607d71 100644
--- a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
@@ -457,7 +457,7 @@
           // Don't report errors, as the set of call sites is a conservative estimate, and can
           // refer to interfaces which has been removed.
           Set<DexEncodedMethod> implementedMethods =
-              appView.appInfo().lookupLambdaImplementedMethods(callSite);
+              appView.appInfo().lookupLambdaImplementedMethods(callSite, appView);
           for (DexEncodedMethod method : implementedMethods) {
             Wrapper<DexEncodedMethod> wrapped = definitionEquivalence.wrap(method);
             InterfaceMethodGroupState groupState = globalStateMap.get(wrapped);
@@ -470,8 +470,7 @@
           }
           // For intersection types, we have to iterate all the multiple interfaces to look for
           // methods with the same signature.
-          List<DexType> implementedInterfaces =
-              LambdaDescriptor.getInterfaces(callSite, appView.appInfo());
+          List<DexType> implementedInterfaces = LambdaDescriptor.getInterfaces(callSite, appView);
           if (implementedInterfaces != null) {
             for (int i = 1; i < implementedInterfaces.size(); i++) {
               // Add the merging state for all additional implemented interfaces into the state
diff --git a/src/main/java/com/android/tools/r8/naming/NamingLens.java b/src/main/java/com/android/tools/r8/naming/NamingLens.java
index 5b24eb9..ca69f67 100644
--- a/src/main/java/com/android/tools/r8/naming/NamingLens.java
+++ b/src/main/java/com/android/tools/r8/naming/NamingLens.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.InnerClassAttribute;
 import com.android.tools.r8.optimize.MemberRebindingAnalysis;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.Sets;
@@ -55,8 +56,9 @@
     if (!appView.appInfo().hasLiveness()) {
       return callSite.methodName;
     }
+    AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
     Set<DexEncodedMethod> lambdaImplementedMethods =
-        appView.appInfo().withLiveness().lookupLambdaImplementedMethods(callSite);
+        appViewWithLiveness.appInfo().lookupLambdaImplementedMethods(callSite, appViewWithLiveness);
     if (lambdaImplementedMethods.isEmpty()) {
       return callSite.methodName;
     }
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index 87af2b2..d860861 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -64,6 +64,10 @@
     this.lensBuilder = MemberRebindingLens.builder(appView);
   }
 
+  private AppView<AppInfoWithLiveness> appView() {
+    return appView;
+  }
+
   private DexMethod validMemberRebindingTargetForNonProgramMethod(
       DexClassAndMethod resolvedMethod,
       SingleResolutionResult<?> resolutionResult,
@@ -167,7 +171,7 @@
     }
     return Iterables.all(
         contexts,
-        context -> resolutionResult.isAccessibleFrom(context, appView.appInfo()).isTrue());
+        context -> resolutionResult.isAccessibleFrom(context, appView, appView.appInfo()).isTrue());
   }
 
   private boolean isInvokeSuperToInterfaceMethod(DexClassAndMethod method, InvokeType invokeType) {
@@ -578,7 +582,7 @@
                   }
 
                   private void registerFieldReference(DexField field) {
-                    appView
+                    appView()
                         .appInfo()
                         .resolveField(field)
                         .forEachSuccessfulFieldResolutionResult(
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingEventConsumer.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingEventConsumer.java
index dff6597..c6d1656 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingEventConsumer.java
@@ -7,7 +7,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingMemberRebindingEventConsumer;
+import com.android.tools.r8.profile.art.rewriting.ProfileRewritingMemberRebindingEventConsumer;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
 public interface MemberRebindingEventConsumer {
@@ -19,7 +19,7 @@
       AppView<AppInfoWithLiveness> appView, MemberRebindingLens memberRebindingLens) {}
 
   static MemberRebindingEventConsumer create(AppView<AppInfoWithLiveness> appView) {
-    return ArtProfileRewritingMemberRebindingEventConsumer.attach(appView, empty());
+    return ProfileRewritingMemberRebindingEventConsumer.attach(appView, empty());
   }
 
   static EmptyMemberRebindingEventConsumer empty() {
diff --git a/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
index 5cbb1b6..815a807 100644
--- a/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
+++ b/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
@@ -205,7 +205,7 @@
     // Only check for interfaces if resolving the method on super type causes NoSuchMethodError.
     FailedResolutionResult failedResolutionResult = superTypeResolution.asFailedResolution();
     if (failedResolutionResult == null
-        || !failedResolutionResult.isNoSuchMethodErrorResult(holder, appView.appInfo())
+        || !failedResolutionResult.isNoSuchMethodErrorResult(holder, appView, appView.appInfo())
         || holder.getInterfaces().isEmpty()) {
       return false;
     }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
index 9023c00..c42b054 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
@@ -205,7 +205,7 @@
       // Use the super target instead of the resolved method to ensure that we propagate the
       // argument information to the targeted method.
       DexClassAndMethod target =
-          resolutionResult.lookupInvokeSuperTarget(context.getHolder(), appView.appInfo());
+          resolutionResult.lookupInvokeSuperTarget(context.getHolder(), appView);
       if (target == null) {
         // Nothing to propagate; the invoke instruction fails.
         return;
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorSyntheticEventConsumer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorSyntheticEventConsumer.java
index 799cd92..397a23f 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorSyntheticEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorSyntheticEventConsumer.java
@@ -7,7 +7,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingArgumentPropagatorSyntheticEventConsumer;
+import com.android.tools.r8.profile.art.rewriting.ProfileRewritingArgumentPropagatorSyntheticEventConsumer;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
 public interface ArgumentPropagatorSyntheticEventConsumer {
@@ -17,7 +17,7 @@
   void finished(AppView<AppInfoWithLiveness> appView);
 
   static ArgumentPropagatorSyntheticEventConsumer create(AppView<AppInfoWithLiveness> appView) {
-    return ArtProfileRewritingArgumentPropagatorSyntheticEventConsumer.attach(appView, empty());
+    return ProfileRewritingArgumentPropagatorSyntheticEventConsumer.attach(appView, empty());
   }
 
   static ArgumentPropagatorSyntheticEventConsumer empty() {
diff --git a/src/main/java/com/android/tools/r8/profile/AbstractProfile.java b/src/main/java/com/android/tools/r8/profile/AbstractProfile.java
new file mode 100644
index 0000000..87b5656
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/AbstractProfile.java
@@ -0,0 +1,41 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.ThrowingConsumer;
+
+public interface AbstractProfile<
+    ClassRule extends AbstractProfileClassRule, MethodRule extends AbstractProfileMethodRule> {
+
+  boolean containsClassRule(DexType type);
+
+  boolean containsMethodRule(DexMethod method);
+
+  <E1 extends Exception, E2 extends Exception> void forEachRule(
+      ThrowingConsumer<ClassRule, E1> classRuleConsumer,
+      ThrowingConsumer<MethodRule, E2> methodRuleConsumer)
+      throws E1, E2;
+
+  ClassRule getClassRule(DexType type);
+
+  MethodRule getMethodRule(DexMethod method);
+
+  interface Builder<
+      ClassRule extends AbstractProfileClassRule,
+      MethodRule extends AbstractProfileMethodRule,
+      Profile extends AbstractProfile<ClassRule, MethodRule>,
+      ProfileBuilder extends Builder<ClassRule, MethodRule, Profile, ProfileBuilder>> {
+
+    ProfileBuilder addRule(AbstractProfileRule rule);
+
+    ProfileBuilder addClassRule(ClassRule classRule);
+
+    ProfileBuilder addMethodRule(MethodRule methodRule);
+
+    Profile build();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/AbstractProfileClassRule.java b/src/main/java/com/android/tools/r8/profile/AbstractProfileClassRule.java
new file mode 100644
index 0000000..d948afb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/AbstractProfileClassRule.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile;
+
+import com.android.tools.r8.graph.DexType;
+
+public interface AbstractProfileClassRule extends AbstractProfileRule {
+
+  DexType getReference();
+
+  interface Builder<ClassRule extends AbstractProfileClassRule> {
+
+    ClassRule build();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/AbstractProfileMethodRule.java b/src/main/java/com/android/tools/r8/profile/AbstractProfileMethodRule.java
new file mode 100644
index 0000000..ddb1225
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/AbstractProfileMethodRule.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile;
+
+import com.android.tools.r8.graph.DexMethod;
+
+public interface AbstractProfileMethodRule extends AbstractProfileRule {
+
+  DexMethod getReference();
+
+  interface Builder<
+      MethodRule extends AbstractProfileMethodRule,
+      MethodRuleBuilder extends Builder<MethodRule, MethodRuleBuilder>> {
+
+    boolean isGreaterThanOrEqualTo(MethodRuleBuilder methodRuleBuilder);
+
+    MethodRuleBuilder join(MethodRule methodRule);
+
+    MethodRuleBuilder join(MethodRuleBuilder methodRuleBuilder);
+
+    MethodRuleBuilder setMethod(DexMethod method);
+
+    MethodRule build();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/AbstractProfileRule.java b/src/main/java/com/android/tools/r8/profile/AbstractProfileRule.java
new file mode 100644
index 0000000..ad28bfd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/AbstractProfileRule.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile;
+
+import com.android.tools.r8.experimental.startup.profile.StartupProfileRule;
+import com.android.tools.r8.profile.art.ArtProfileRule;
+
+public interface AbstractProfileRule {
+
+  @SuppressWarnings("unchecked")
+  default ArtProfileRule asArtProfileRule() {
+    return (ArtProfileRule) this;
+  }
+
+  @SuppressWarnings("unchecked")
+  default StartupProfileRule asStartupProfileRule() {
+    return (StartupProfileRule) this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfile.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfile.java
index 8fb11ef..7ab727d 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfile.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfile.java
@@ -19,6 +19,8 @@
 import com.android.tools.r8.graph.PrunedItems;
 import com.android.tools.r8.ir.optimize.enums.EnumUnboxingLens;
 import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.profile.AbstractProfile;
+import com.android.tools.r8.profile.AbstractProfileRule;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.ThrowingConsumer;
@@ -33,7 +35,7 @@
 import java.util.function.Consumer;
 import java.util.function.Function;
 
-public class ArtProfile {
+public class ArtProfile implements AbstractProfile<ArtProfileClassRule, ArtProfileMethodRule> {
 
   private final Map<DexReference, ArtProfileRule> rules;
 
@@ -50,10 +52,12 @@
     return new Builder(artProfileProvider, options);
   }
 
+  @Override
   public boolean containsClassRule(DexType type) {
     return rules.containsKey(type);
   }
 
+  @Override
   public boolean containsMethodRule(DexMethod method) {
     return rules.containsKey(method);
   }
@@ -65,6 +69,7 @@
     }
   }
 
+  @Override
   public <E1 extends Exception, E2 extends Exception> void forEachRule(
       ThrowingConsumer<ArtProfileClassRule, E1> classRuleConsumer,
       ThrowingConsumer<ArtProfileMethodRule, E2> methodRuleConsumer)
@@ -74,10 +79,12 @@
     }
   }
 
+  @Override
   public ArtProfileClassRule getClassRule(DexType type) {
     return (ArtProfileClassRule) rules.get(type);
   }
 
+  @Override
   public ArtProfileMethodRule getMethodRule(DexMethod method) {
     return (ArtProfileMethodRule) rules.get(method);
   }
@@ -259,7 +266,9 @@
                 methodRule.getMethodReference(), methodRule.getMethodRuleInfo()));
   }
 
-  public static class Builder implements ArtProfileBuilder {
+  public static class Builder
+      implements ArtProfileBuilder,
+          AbstractProfile.Builder<ArtProfileClassRule, ArtProfileMethodRule, ArtProfile, Builder> {
 
     private final ArtProfileProvider artProfileProvider;
     private final DexItemFactory dexItemFactory;
@@ -281,19 +290,29 @@
       this.reporter = options.reporter;
     }
 
-    public Builder addRule(ArtProfileRule rule) {
-      assert !rules.containsKey(rule.getReference());
-      rule.accept(
-          classRule -> rules.put(classRule.getType(), classRule),
-          methodRule -> rules.put(methodRule.getMethod(), methodRule));
+    @Override
+    public Builder addRule(AbstractProfileRule rule) {
+      return addRule(rule.asArtProfileRule());
+    }
+
+    @Override
+    public Builder addClassRule(ArtProfileClassRule classRule) {
+      assert !rules.containsKey(classRule.getReference());
+      rules.put(classRule.getType(), classRule);
       return this;
     }
 
-    public Builder addRules(Collection<ArtProfileRule> rules) {
-      rules.forEach(this::addRule);
+    @Override
+    public Builder addMethodRule(ArtProfileMethodRule methodRule) {
+      assert !rules.containsKey(methodRule.getReference());
+      rules.put(methodRule.getMethod(), methodRule);
       return this;
     }
 
+    public Builder addRule(ArtProfileRule rule) {
+      return rule.apply(this::addClassRule, this::addMethodRule);
+    }
+
     public Builder addRuleBuilders(Collection<ArtProfileRule.Builder> ruleBuilders) {
       ruleBuilders.forEach(ruleBuilder -> addRule(ruleBuilder.build()));
       return this;
@@ -303,14 +322,14 @@
     public Builder addClassRule(Consumer<ArtProfileClassRuleBuilder> classRuleBuilderConsumer) {
       ArtProfileClassRule.Builder classRuleBuilder = ArtProfileClassRule.builder(dexItemFactory);
       classRuleBuilderConsumer.accept(classRuleBuilder);
-      return addRule(classRuleBuilder.build());
+      return addClassRule(classRuleBuilder.build());
     }
 
     @Override
     public Builder addMethodRule(Consumer<ArtProfileMethodRuleBuilder> methodRuleBuilderConsumer) {
       ArtProfileMethodRule.Builder methodRuleBuilder = ArtProfileMethodRule.builder(dexItemFactory);
       methodRuleBuilderConsumer.accept(methodRuleBuilder);
-      return addRule(methodRuleBuilder.build());
+      return addMethodRule(methodRuleBuilder.build());
     }
 
     @Override
@@ -328,6 +347,7 @@
       return this;
     }
 
+    @Override
     public ArtProfile build() {
       return new ArtProfile(rules);
     }
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileBuilderUtils.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileBuilderUtils.java
index 77d0005..bbfe13e 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileBuilderUtils.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileBuilderUtils.java
@@ -4,14 +4,11 @@
 
 package com.android.tools.r8.profile.art;
 
-import static com.android.tools.r8.synthesis.SyntheticNaming.COMPANION_CLASS_SUFFIX;
-import static com.android.tools.r8.synthesis.SyntheticNaming.EXTERNAL_SYNTHETIC_CLASS_SEPARATOR;
 
 import com.android.tools.r8.TextInputStream;
 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;
 import com.android.tools.r8.startup.StartupProfileBuilder;
 import com.android.tools.r8.utils.MethodReferenceUtils;
 import java.io.IOException;
@@ -20,60 +17,12 @@
 
 public class ArtProfileBuilderUtils {
 
-  public interface SyntheticToSyntheticContextGeneralization {
-
-    ClassReference getSyntheticContextReference(ClassReference classReference);
-
-    /**
-     * When a startup profile is given to D8, the program input should be dex and the startup
-     * profile should have been generated by launching the dex that is given on input. Therefore,
-     * synthetic items in the ART profile should be present in the program input to D8 with the
-     * exact same synthetic names as in the ART profile. This means that there is no need to
-     * generalize synthetic items to their synthetic context.
-     */
-    static SyntheticToSyntheticContextGeneralization createForD8() {
-      return classReference -> null;
-    }
-
-    /**
-     * When a startup profile is given to R8, the program input is class files and the startup
-     * profile should have been generated by dexing the program input (in release and intermediate
-     * mode) and then launching the resulting app. The synthetic items in the resulting ART profile
-     * do not exist in the program input to R8 (and the same synthetics may receive different names
-     * in the R8 compilation). Therefore, synthetic items in the ART profile are generalized into
-     * matching all synthetics from their synthetic context.
-     */
-    static SyntheticToSyntheticContextGeneralization createForR8() {
-      return classReference -> {
-        // TODO(b/243777722): Move this logic into synthetic items and extend the mapping from
-        //  synthetic classes to their synthetic context to all synthetic kinds.
-        String classDescriptor = classReference.getDescriptor();
-        for (int i = 1; i < classDescriptor.length() - 1; i++) {
-          if (classDescriptor.charAt(i) != '$') {
-            continue;
-          }
-          if (classDescriptor.regionMatches(
-                  i, COMPANION_CLASS_SUFFIX, 0, COMPANION_CLASS_SUFFIX.length())
-              || classDescriptor.regionMatches(
-                  i,
-                  EXTERNAL_SYNTHETIC_CLASS_SEPARATOR,
-                  0,
-                  EXTERNAL_SYNTHETIC_CLASS_SEPARATOR.length())) {
-            return Reference.classFromDescriptor(classDescriptor.substring(0, i) + ";");
-          }
-        }
-        return null;
-      };
-    }
-  }
-
   /**
    * Helper for creating an {@link ArtProfileBuilder} that performs callbacks on the given {@param
    * startupProfileBuilder}.
    */
   public static ArtProfileBuilder createBuilderForArtProfileToStartupProfileConversion(
-      StartupProfileBuilder startupProfileBuilder,
-      SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization) {
+      StartupProfileBuilder startupProfileBuilder) {
     return new ArtProfileBuilder() {
 
       @Override
@@ -81,19 +30,9 @@
           Consumer<ArtProfileClassRuleBuilder> classRuleBuilderConsumer) {
         MutableArtProfileClassRule classRule = new MutableArtProfileClassRule();
         classRuleBuilderConsumer.accept(classRule);
-        ClassReference syntheticContextReference =
-            syntheticToSyntheticContextGeneralization.getSyntheticContextReference(
-                classRule.getClassReference());
-        if (syntheticContextReference == null) {
-          startupProfileBuilder.addStartupClass(
-              startupClassBuilder ->
-                  startupClassBuilder.setClassReference(classRule.getClassReference()));
-        } else {
-          startupProfileBuilder.addSyntheticStartupMethod(
-              syntheticStartupMethodBuilder ->
-                  syntheticStartupMethodBuilder.setSyntheticContextReference(
-                      syntheticContextReference));
-        }
+        startupProfileBuilder.addStartupClass(
+            startupClassBuilder ->
+                startupClassBuilder.setClassReference(classRule.getClassReference()));
         return this;
       }
 
@@ -102,19 +41,9 @@
           Consumer<ArtProfileMethodRuleBuilder> methodRuleBuilderConsumer) {
         MutableArtProfileMethodRule methodRule = new MutableArtProfileMethodRule();
         methodRuleBuilderConsumer.accept(methodRule);
-        ClassReference syntheticContextReference =
-            syntheticToSyntheticContextGeneralization.getSyntheticContextReference(
-                methodRule.getMethodReference().getHolderClass());
-        if (syntheticContextReference == null) {
-          startupProfileBuilder.addStartupMethod(
-              startupMethodBuilder ->
-                  startupMethodBuilder.setMethodReference(methodRule.getMethodReference()));
-        } else {
-          startupProfileBuilder.addSyntheticStartupMethod(
-              syntheticStartupMethodBuilder ->
-                  syntheticStartupMethodBuilder.setSyntheticContextReference(
-                      syntheticContextReference));
-        }
+        startupProfileBuilder.addStartupMethod(
+            startupMethodBuilder ->
+                startupMethodBuilder.setMethodReference(methodRule.getMethodReference()));
         return this;
       }
 
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRule.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRule.java
index 283e22b..625d92f 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRule.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRule.java
@@ -5,15 +5,16 @@
 package com.android.tools.r8.profile.art;
 
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.profile.AbstractProfileClassRule;
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.utils.ThrowingConsumer;
+import com.android.tools.r8.utils.ThrowingFunction;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
 
-public class ArtProfileClassRule extends ArtProfileRule {
+public class ArtProfileClassRule extends ArtProfileRule implements AbstractProfileClassRule {
 
   private final DexType type;
 
@@ -37,6 +38,14 @@
     classRuleConsumer.accept(this);
   }
 
+  @Override
+  public <T, E1 extends Exception, E2 extends Exception> T apply(
+      ThrowingFunction<ArtProfileClassRule, T, E1> classRuleFunction,
+      ThrowingFunction<ArtProfileMethodRule, T, E2> methodRuleFunction)
+      throws E1 {
+    return classRuleFunction.apply(this);
+  }
+
   public ClassReference getClassReference() {
     return Reference.classFromDescriptor(type.toDescriptorString());
   }
@@ -46,7 +55,7 @@
   }
 
   @Override
-  public DexReference getReference() {
+  public DexType getReference() {
     return getType();
   }
 
@@ -55,16 +64,6 @@
   }
 
   @Override
-  public boolean isClassRule() {
-    return true;
-  }
-
-  @Override
-  public ArtProfileClassRule asClassRule() {
-    return this;
-  }
-
-  @Override
   public void writeHumanReadableRuleString(OutputStreamWriter writer) throws IOException {
     writer.write(type.toDescriptorString());
   }
@@ -91,7 +90,8 @@
     return type.toDescriptorString();
   }
 
-  public static class Builder extends ArtProfileRule.Builder implements ArtProfileClassRuleBuilder {
+  public static class Builder extends ArtProfileRule.Builder
+      implements ArtProfileClassRuleBuilder, AbstractProfileClassRule.Builder<ArtProfileClassRule> {
 
     private final DexItemFactory dexItemFactory;
     private DexType type;
@@ -105,11 +105,6 @@
     }
 
     @Override
-    public boolean isClassRuleBuilder() {
-      return true;
-    }
-
-    @Override
     Builder asClassRuleBuilder() {
       return this;
     }
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileCollection.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileCollection.java
index 9eb9a1c..a571922 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileCollection.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileCollection.java
@@ -48,10 +48,11 @@
   private static ArtProfile createCompleteArtProfile(AppInfo appInfo) {
     ArtProfile.Builder artProfileBuilder = ArtProfile.builder();
     for (DexProgramClass clazz : appInfo.classesWithDeterministicOrder()) {
-      artProfileBuilder.addRule(ArtProfileClassRule.builder().setType(clazz.getType()).build());
+      artProfileBuilder.addClassRule(
+          ArtProfileClassRule.builder().setType(clazz.getType()).build());
       clazz.forEachMethod(
           method ->
-              artProfileBuilder.addRule(
+              artProfileBuilder.addMethodRule(
                   ArtProfileMethodRule.builder().setMethod(method.getReference()).build()));
     }
     return artProfileBuilder.build();
@@ -61,6 +62,8 @@
     return EmptyArtProfileCollection.getInstance();
   }
 
+  public abstract boolean isEmpty();
+
   public abstract boolean isNonEmpty();
 
   public abstract NonEmptyArtProfileCollection asNonEmpty();
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRule.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRule.java
index fe7bd78..1440b8b 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRule.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRule.java
@@ -6,14 +6,16 @@
 
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.profile.AbstractProfileMethodRule;
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.utils.MethodReferenceUtils;
 import com.android.tools.r8.utils.ThrowingConsumer;
+import com.android.tools.r8.utils.ThrowingFunction;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
 import java.util.function.Consumer;
 
-public class ArtProfileMethodRule extends ArtProfileRule {
+public class ArtProfileMethodRule extends ArtProfileRule implements AbstractProfileMethodRule {
 
   private final DexMethod method;
   private final ArtProfileMethodRuleInfoImpl info;
@@ -39,6 +41,14 @@
     methodRuleConsumer.accept(this);
   }
 
+  @Override
+  public <T, E1 extends Exception, E2 extends Exception> T apply(
+      ThrowingFunction<ArtProfileClassRule, T, E1> classRuleFunction,
+      ThrowingFunction<ArtProfileMethodRule, T, E2> methodRuleFunction)
+      throws E2 {
+    return methodRuleFunction.apply(this);
+  }
+
   public DexMethod getMethod() {
     return method;
   }
@@ -57,16 +67,6 @@
   }
 
   @Override
-  public boolean isMethodRule() {
-    return true;
-  }
-
-  @Override
-  public ArtProfileMethodRule asMethodRule() {
-    return this;
-  }
-
-  @Override
   public void writeHumanReadableRuleString(OutputStreamWriter writer) throws IOException {
     info.writeHumanReadableFlags(writer);
     writer.write(method.toSmaliString());
@@ -97,7 +97,8 @@
   }
 
   public static class Builder extends ArtProfileRule.Builder
-      implements ArtProfileMethodRuleBuilder {
+      implements ArtProfileMethodRuleBuilder,
+          AbstractProfileMethodRule.Builder<ArtProfileMethodRule, Builder> {
 
     private final DexItemFactory dexItemFactory;
 
@@ -118,8 +119,8 @@
     }
 
     @Override
-    public boolean isMethodRuleBuilder() {
-      return true;
+    public boolean isGreaterThanOrEqualTo(Builder builder) {
+      return methodRuleInfoBuilder.isGreaterThanOrEqualTo(builder.methodRuleInfoBuilder);
     }
 
     @Override
@@ -128,11 +129,24 @@
     }
 
     @Override
+    public Builder join(Builder builder) {
+      methodRuleInfoBuilder.joinFlags(builder);
+      return this;
+    }
+
+    @Override
+    public Builder join(ArtProfileMethodRule methodRule) {
+      methodRuleInfoBuilder.joinFlags(methodRule.getMethodRuleInfo());
+      return this;
+    }
+
+    @Override
     public Builder setMethodReference(MethodReference methodReference) {
       assert dexItemFactory != null;
       return setMethod(MethodReferenceUtils.toDexMethod(methodReference, dexItemFactory));
     }
 
+    @Override
     public Builder setMethod(DexMethod method) {
       this.method = method;
       return this;
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleInfoImpl.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleInfoImpl.java
index cb482d8..d91553e 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleInfoImpl.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleInfoImpl.java
@@ -105,6 +105,10 @@
       return flags;
     }
 
+    public boolean isGreaterThanOrEqualTo(Builder builder) {
+      return flags == (flags | builder.flags);
+    }
+
     public Builder merge(ArtProfileMethodRuleInfo methodRuleInfo) {
       if (methodRuleInfo.isHot()) {
         setIsHot();
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileRule.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileRule.java
index 8d6eefd..bb0bcc1 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileRule.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileRule.java
@@ -5,17 +5,24 @@
 package com.android.tools.r8.profile.art;
 
 import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.profile.AbstractProfileRule;
 import com.android.tools.r8.utils.ThrowingConsumer;
+import com.android.tools.r8.utils.ThrowingFunction;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
 
-public abstract class ArtProfileRule implements Comparable<ArtProfileRule> {
+public abstract class ArtProfileRule implements Comparable<ArtProfileRule>, AbstractProfileRule {
 
   public abstract <E1 extends Exception, E2 extends Exception> void accept(
       ThrowingConsumer<ArtProfileClassRule, E1> classRuleConsumer,
       ThrowingConsumer<ArtProfileMethodRule, E2> methodRuleConsumer)
       throws E1, E2;
 
+  public abstract <T, E1 extends Exception, E2 extends Exception> T apply(
+      ThrowingFunction<ArtProfileClassRule, T, E1> classRuleFunction,
+      ThrowingFunction<ArtProfileMethodRule, T, E2> methodRuleFunction)
+      throws E1, E2;
+
   @Override
   public final int compareTo(ArtProfileRule rule) {
     return getReference().compareTo(rule.getReference());
@@ -23,38 +30,14 @@
 
   public abstract DexReference getReference();
 
-  public boolean isClassRule() {
-    return false;
-  }
-
-  public ArtProfileClassRule asClassRule() {
-    return null;
-  }
-
-  public boolean isMethodRule() {
-    return false;
-  }
-
-  public ArtProfileMethodRule asMethodRule() {
-    return null;
-  }
-
   public abstract void writeHumanReadableRuleString(OutputStreamWriter writer) throws IOException;
 
   public abstract static class Builder {
 
-    public boolean isClassRuleBuilder() {
-      return false;
-    }
-
     ArtProfileClassRule.Builder asClassRuleBuilder() {
       return null;
     }
 
-    public boolean isMethodRuleBuilder() {
-      return false;
-    }
-
     ArtProfileMethodRule.Builder asMethodRuleBuilder() {
       return null;
     }
diff --git a/src/main/java/com/android/tools/r8/profile/art/EmptyArtProfileCollection.java b/src/main/java/com/android/tools/r8/profile/art/EmptyArtProfileCollection.java
index b66f261..744ea4d 100644
--- a/src/main/java/com/android/tools/r8/profile/art/EmptyArtProfileCollection.java
+++ b/src/main/java/com/android/tools/r8/profile/art/EmptyArtProfileCollection.java
@@ -21,6 +21,11 @@
   }
 
   @Override
+  public boolean isEmpty() {
+    return true;
+  }
+
+  @Override
   public boolean isNonEmpty() {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/profile/art/NonEmptyArtProfileCollection.java b/src/main/java/com/android/tools/r8/profile/art/NonEmptyArtProfileCollection.java
index 1d4756f..0cb809e 100644
--- a/src/main/java/com/android/tools/r8/profile/art/NonEmptyArtProfileCollection.java
+++ b/src/main/java/com/android/tools/r8/profile/art/NonEmptyArtProfileCollection.java
@@ -27,6 +27,11 @@
   }
 
   @Override
+  public boolean isEmpty() {
+    return false;
+  }
+
+  @Override
   public boolean isNonEmpty() {
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileAdditions.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileAdditions.java
index f4400c7..6207388 100644
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileAdditions.java
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileAdditions.java
@@ -4,306 +4,57 @@
 
 package com.android.tools.r8.profile.art.rewriting;
 
-import static com.android.tools.r8.utils.MapUtils.ignoreKey;
-
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.ProgramDefinition;
-import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.profile.AbstractProfileRule;
 import com.android.tools.r8.profile.art.ArtProfile;
 import com.android.tools.r8.profile.art.ArtProfileClassRule;
 import com.android.tools.r8.profile.art.ArtProfileMethodRule;
-import com.android.tools.r8.profile.art.ArtProfileMethodRuleInfoImpl;
 import com.android.tools.r8.profile.art.ArtProfileRule;
-import com.android.tools.r8.utils.WorkList;
-import com.google.common.collect.Sets;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.stream.Collectors;
+import java.util.Comparator;
 
-/** Mutable extension of an existing ArtProfile. */
-public class ArtProfileAdditions {
+public class ArtProfileAdditions
+    extends ProfileAdditions<
+        ArtProfileAdditions,
+        ArtProfileClassRule,
+        ArtProfileClassRule.Builder,
+        ArtProfileMethodRule,
+        ArtProfileMethodRule.Builder,
+        ArtProfileRule,
+        ArtProfile,
+        ArtProfile.Builder> {
 
-  public interface ArtProfileAdditionsBuilder {
-
-    default ArtProfileAdditionsBuilder addRule(ProgramDefinition definition) {
-      return addRule(definition.getReference());
-    }
-
-    default ArtProfileAdditionsBuilder addRule(DexReference reference) {
-      if (reference.isDexType()) {
-        return addClassRule(reference.asDexType());
-      } else {
-        assert reference.isDexMethod();
-        return addMethodRule(reference.asDexMethod());
-      }
-    }
-
-    ArtProfileAdditionsBuilder addClassRule(DexType type);
-
-    ArtProfileAdditionsBuilder addMethodRule(DexMethod method);
-
-    default ArtProfileAdditionsBuilder removeMovedMethodRule(
-        ProgramMethod oldMethod, ProgramMethod newMethod) {
-      return removeMovedMethodRule(oldMethod.getReference(), newMethod);
-    }
-
-    ArtProfileAdditionsBuilder removeMovedMethodRule(DexMethod oldMethod, ProgramMethod newMethod);
+  ArtProfileAdditions(ArtProfile profile) {
+    super(profile);
   }
 
-  private ArtProfile artProfile;
-
-  private final Map<DexType, ArtProfileClassRule.Builder> classRuleAdditions =
-      new ConcurrentHashMap<>();
-  private final Map<DexMethod, ArtProfileMethodRule.Builder> methodRuleAdditions =
-      new ConcurrentHashMap<>();
-  private final Set<DexMethod> methodRuleRemovals = Sets.newConcurrentHashSet();
-
-  private final NestedMethodRuleAdditionsGraph nestedMethodRuleAdditionsGraph =
-      new NestedMethodRuleAdditionsGraph();
-
-  ArtProfileAdditions(ArtProfile artProfile) {
-    this.artProfile = artProfile;
+  @Override
+  public ArtProfileAdditions create() {
+    return new ArtProfileAdditions(profile);
   }
 
-  void applyIfContextIsInProfile(DexType context, Consumer<ArtProfileAdditions> fn) {
-    if (artProfile.containsClassRule(context) || classRuleAdditions.containsKey(context)) {
-      fn.accept(this);
-    }
+  @Override
+  public ArtProfileClassRule.Builder createClassRuleBuilder(DexType type) {
+    return ArtProfileClassRule.builder().setType(type);
   }
 
-  void applyIfContextIsInProfile(
-      DexMethod context, Consumer<ArtProfileAdditionsBuilder> builderConsumer) {
-    ArtProfileMethodRule contextMethodRule = artProfile.getMethodRule(context);
-    if (contextMethodRule != null) {
-      builderConsumer.accept(
-          new ArtProfileAdditionsBuilder() {
-
-            @Override
-            public ArtProfileAdditionsBuilder addClassRule(DexType type) {
-              ArtProfileAdditions.this.addClassRule(type);
-              return this;
-            }
-
-            @Override
-            public ArtProfileAdditionsBuilder addMethodRule(DexMethod method) {
-              ArtProfileAdditions.this.addMethodRuleFromContext(
-                  method,
-                  methodRuleInfoBuilder ->
-                      methodRuleInfoBuilder.joinFlags(contextMethodRule.getMethodRuleInfo()));
-              return this;
-            }
-
-            @Override
-            public ArtProfileAdditionsBuilder removeMovedMethodRule(
-                DexMethod oldMethod, ProgramMethod newMethod) {
-              ArtProfileAdditions.this.removeMovedMethodRule(oldMethod, newMethod);
-              return this;
-            }
-          });
-    } else if (methodRuleAdditions.containsKey(context)) {
-      builderConsumer.accept(
-          new ArtProfileAdditionsBuilder() {
-
-            @Override
-            public ArtProfileAdditionsBuilder addClassRule(DexType type) {
-              ArtProfileAdditions.this.addClassRule(type);
-              return this;
-            }
-
-            @Override
-            public ArtProfileAdditionsBuilder addMethodRule(DexMethod method) {
-              ArtProfileMethodRule.Builder contextRuleBuilder = methodRuleAdditions.get(context);
-              ArtProfileAdditions.this.addMethodRuleFromContext(
-                  method,
-                  methodRuleInfoBuilder -> methodRuleInfoBuilder.joinFlags(contextRuleBuilder));
-              nestedMethodRuleAdditionsGraph.recordMethodRuleInfoFlagsLargerThan(method, context);
-              return this;
-            }
-
-            @Override
-            public ArtProfileAdditionsBuilder removeMovedMethodRule(
-                DexMethod oldMethod, ProgramMethod newMethod) {
-              ArtProfileAdditions.this.removeMovedMethodRule(oldMethod, newMethod);
-              return this;
-            }
-          });
-    }
+  @Override
+  public ArtProfileMethodRule.Builder createMethodRuleBuilder(DexMethod method) {
+    return ArtProfileMethodRule.builder().setMethod(method);
   }
 
-  public ArtProfileAdditions addClassRule(DexClass clazz) {
-    addClassRule(clazz.getType());
+  @Override
+  public ArtProfile.Builder createProfileBuilder() {
+    return ArtProfile.builder();
+  }
+
+  @Override
+  public Comparator<AbstractProfileRule> getRuleComparator() {
+    return Comparator.comparing(AbstractProfileRule::asArtProfileRule);
+  }
+
+  @Override
+  public ArtProfileAdditions self() {
     return this;
   }
-
-  public void addClassRule(DexType type) {
-    if (artProfile.containsClassRule(type)) {
-      return;
-    }
-
-    // Create profile rule for class.
-    classRuleAdditions.computeIfAbsent(type, key -> ArtProfileClassRule.builder().setType(key));
-  }
-
-  private void addMethodRuleFromContext(
-      DexMethod method,
-      Consumer<ArtProfileMethodRuleInfoImpl.Builder> methodRuleInfoBuilderConsumer) {
-    addMethodRule(method, methodRuleInfoBuilderConsumer);
-  }
-
-  public ArtProfileAdditions addMethodRule(
-      DexClassAndMethod method,
-      Consumer<ArtProfileMethodRuleInfoImpl.Builder> methodRuleInfoBuilderConsumer) {
-    return addMethodRule(method.getReference(), methodRuleInfoBuilderConsumer);
-  }
-
-  public ArtProfileAdditions addMethodRule(
-      DexMethod method,
-      Consumer<ArtProfileMethodRuleInfoImpl.Builder> methodRuleInfoBuilderConsumer) {
-    // Create profile rule for method.
-    ArtProfileMethodRule.Builder methodRuleBuilder =
-        methodRuleAdditions.computeIfAbsent(
-            method, methodReference -> ArtProfileMethodRule.builder().setMethod(method));
-
-    // Setup the rule.
-    synchronized (methodRuleBuilder) {
-      methodRuleBuilder.acceptMethodRuleInfoBuilder(methodRuleInfoBuilderConsumer);
-    }
-
-    return this;
-  }
-
-  void removeMovedMethodRule(DexMethod oldMethod, ProgramMethod newMethod) {
-    assert artProfile.containsMethodRule(oldMethod) || methodRuleAdditions.containsKey(oldMethod);
-    assert methodRuleAdditions.containsKey(newMethod.getReference());
-    methodRuleRemovals.add(oldMethod);
-  }
-
-  ArtProfile createNewArtProfile() {
-    if (!hasAdditions()) {
-      assert !hasRemovals();
-      return artProfile;
-    }
-
-    nestedMethodRuleAdditionsGraph.propagateMethodRuleInfoFlags(methodRuleAdditions);
-
-    // Add existing rules to new profile.
-    ArtProfile.Builder artProfileBuilder = ArtProfile.builder();
-    artProfile.forEachRule(
-        artProfileBuilder::addRule,
-        methodRule -> {
-          if (methodRuleRemovals.contains(methodRule.getMethod())) {
-            return;
-          }
-          ArtProfileMethodRule.Builder methodRuleBuilder =
-              methodRuleAdditions.remove(methodRule.getReference());
-          if (methodRuleBuilder != null) {
-            ArtProfileMethodRule newMethodRule =
-                methodRuleBuilder
-                    .acceptMethodRuleInfoBuilder(
-                        methodRuleInfoBuilder ->
-                            methodRuleInfoBuilder.joinFlags(methodRule.getMethodRuleInfo()))
-                    .build();
-            artProfileBuilder.addRule(newMethodRule);
-          } else {
-            artProfileBuilder.addRule(methodRule);
-          }
-        });
-
-    // Sort and add additions to new profile. Sorting is needed since the additions to this
-    // collection may be concurrent.
-    List<ArtProfileRule> ruleAdditionsSorted =
-        new ArrayList<>(classRuleAdditions.size() + methodRuleAdditions.size());
-    classRuleAdditions
-        .values()
-        .forEach(classRuleBuilder -> ruleAdditionsSorted.add(classRuleBuilder.build()));
-    methodRuleAdditions
-        .values()
-        .forEach(methodRuleBuilder -> ruleAdditionsSorted.add(methodRuleBuilder.build()));
-    ruleAdditionsSorted.sort(ArtProfileRule::compareTo);
-    artProfileBuilder.addRules(ruleAdditionsSorted);
-
-    return artProfileBuilder.build();
-  }
-
-  boolean hasAdditions() {
-    return !classRuleAdditions.isEmpty() || !methodRuleAdditions.isEmpty();
-  }
-
-  private boolean hasRemovals() {
-    return !methodRuleRemovals.isEmpty();
-  }
-
-  ArtProfileAdditions rewriteMethodReferences(Function<DexMethod, DexMethod> methodFn) {
-    ArtProfileAdditions rewrittenAdditions = new ArtProfileAdditions(artProfile);
-    assert methodRuleRemovals.isEmpty();
-    rewrittenAdditions.classRuleAdditions.putAll(classRuleAdditions);
-    methodRuleAdditions.forEach(
-        (method, methodRuleBuilder) -> {
-          DexMethod newMethod = methodFn.apply(method);
-          ArtProfileMethodRule.Builder existingMethodRuleBuilder =
-              rewrittenAdditions.methodRuleAdditions.put(
-                  newMethod, methodRuleBuilder.setMethod(newMethod));
-          assert existingMethodRuleBuilder == null;
-        });
-    return rewrittenAdditions;
-  }
-
-  void setArtProfile(ArtProfile artProfile) {
-    this.artProfile = artProfile;
-  }
-
-  private static class NestedMethodRuleAdditionsGraph {
-
-    private final Map<DexMethod, Set<DexMethod>> successors = new ConcurrentHashMap<>();
-    private final Map<DexMethod, Set<DexMethod>> predecessors = new ConcurrentHashMap<>();
-
-    void recordMethodRuleInfoFlagsLargerThan(DexMethod largerFlags, DexMethod smallerFlags) {
-      predecessors
-          .computeIfAbsent(largerFlags, ignoreKey(Sets::newConcurrentHashSet))
-          .add(smallerFlags);
-      successors
-          .computeIfAbsent(smallerFlags, ignoreKey(Sets::newConcurrentHashSet))
-          .add(largerFlags);
-    }
-
-    void propagateMethodRuleInfoFlags(
-        Map<DexMethod, ArtProfileMethodRule.Builder> methodRuleAdditions) {
-      List<DexMethod> leaves =
-          successors.keySet().stream()
-              .filter(method -> predecessors.getOrDefault(method, Collections.emptySet()).isEmpty())
-              .collect(Collectors.toList());
-      WorkList<DexMethod> worklist = WorkList.newIdentityWorkList(leaves);
-      while (worklist.hasNext()) {
-        DexMethod method = worklist.next();
-        ArtProfileMethodRule.Builder methodRuleBuilder = methodRuleAdditions.get(method);
-        for (DexMethod successor : successors.getOrDefault(method, Collections.emptySet())) {
-          methodRuleAdditions
-              .get(successor)
-              .acceptMethodRuleInfoBuilder(
-                  methodRuleInfoBuilder -> {
-                    int oldFlags = methodRuleInfoBuilder.getFlags();
-                    methodRuleInfoBuilder.joinFlags(methodRuleBuilder);
-                    // If this assertion fails, that means we have synthetics with multiple
-                    // synthesizing contexts, which are not guaranteed to be processed before the
-                    // synthetic itself. In that case this assertion should simply be removed.
-                    assert methodRuleInfoBuilder.getFlags() == oldFlags;
-                  });
-          // Note: no need to addIgnoringSeenSet() since the graph will not have cycles. Indeed, it
-          // should never be the case that a method m2(), which is synthesized from method context
-          // m1(), would itself be a synthesizing context for m1().
-          worklist.addIfNotSeen(successor);
-        }
-      }
-    }
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileCollectionAdditions.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileCollectionAdditions.java
deleted file mode 100644
index 5198d05..0000000
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileCollectionAdditions.java
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.profile.art.rewriting;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.profile.art.ArtProfileCollection;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileAdditions.ArtProfileAdditionsBuilder;
-import java.util.function.Consumer;
-import java.util.function.Function;
-
-/**
- * Interface for adding (synthetic) items to an existing ArtProfileCollection.
- *
- * <p>The interface will be implemented by {@link NopArtProfileCollectionAdditions} when the
- * compilation does not contain any ART profiles, for minimal performance overhead.
- *
- * <p>When one or more ART profiles are present, this is implemented by {@link
- * ConcreteArtProfileCollectionAdditions}.
- */
-public abstract class ArtProfileCollectionAdditions {
-
-  public static ArtProfileCollectionAdditions create(AppView<?> appView) {
-    ArtProfileCollection artProfileCollection = appView.getArtProfileCollection();
-    if (artProfileCollection.isNonEmpty()) {
-      return new ConcreteArtProfileCollectionAdditions(artProfileCollection.asNonEmpty());
-    }
-    return nop();
-  }
-
-  public static NopArtProfileCollectionAdditions nop() {
-    return NopArtProfileCollectionAdditions.getInstance();
-  }
-
-  public abstract void addMethodIfContextIsInProfile(ProgramMethod method, ProgramMethod context);
-
-  public abstract void applyIfContextIsInProfile(
-      DexMethod context, Consumer<ArtProfileAdditionsBuilder> builderConsumer);
-
-  public abstract void commit(AppView<?> appView);
-
-  public boolean isNop() {
-    return false;
-  }
-
-  public ConcreteArtProfileCollectionAdditions asConcrete() {
-    return null;
-  }
-
-  public abstract ArtProfileCollectionAdditions rewriteMethodReferences(
-      Function<DexMethod, DexMethod> methodFn);
-
-  public abstract ArtProfileCollectionAdditions setArtProfileCollection(
-      ArtProfileCollection artProfileCollection);
-
-  public abstract boolean verifyIsCommitted();
-}
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ConcreteArtProfileCollectionAdditions.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ConcreteArtProfileCollectionAdditions.java
deleted file mode 100644
index 1d1a897..0000000
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ConcreteArtProfileCollectionAdditions.java
+++ /dev/null
@@ -1,164 +0,0 @@
-// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.profile.art.rewriting;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.ProgramDefinition;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.profile.art.ArtProfile;
-import com.android.tools.r8.profile.art.ArtProfileCollection;
-import com.android.tools.r8.profile.art.ArtProfileMethodRuleInfoImpl;
-import com.android.tools.r8.profile.art.NonEmptyArtProfileCollection;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileAdditions.ArtProfileAdditionsBuilder;
-import com.google.common.collect.Iterables;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.function.Consumer;
-import java.util.function.Function;
-
-public class ConcreteArtProfileCollectionAdditions extends ArtProfileCollectionAdditions {
-
-  private final List<ArtProfileAdditions> additionsCollection;
-
-  private boolean committed = false;
-
-  private ConcreteArtProfileCollectionAdditions(List<ArtProfileAdditions> additionsCollection) {
-    this.additionsCollection = additionsCollection;
-  }
-
-  ConcreteArtProfileCollectionAdditions(NonEmptyArtProfileCollection artProfileCollection) {
-    additionsCollection = new ArrayList<>();
-    for (ArtProfile artProfile : artProfileCollection) {
-      additionsCollection.add(new ArtProfileAdditions(artProfile));
-    }
-    assert !additionsCollection.isEmpty();
-  }
-
-  @Override
-  public void addMethodIfContextIsInProfile(ProgramMethod method, ProgramMethod context) {
-    applyIfContextIsInProfile(context, additionsBuilder -> additionsBuilder.addRule(method));
-  }
-
-  public void addMethodIfContextIsInProfile(
-      ProgramMethod method,
-      DexClassAndMethod context,
-      Consumer<ArtProfileMethodRuleInfoImpl.Builder> methodRuleInfoBuilderConsumer) {
-    if (context.isProgramMethod()) {
-      applyIfContextIsInProfile(
-          context.asProgramMethod(), additionsBuilder -> additionsBuilder.addRule(method));
-    } else {
-      apply(
-          artProfileAdditions ->
-              artProfileAdditions.addMethodRule(method, methodRuleInfoBuilderConsumer));
-    }
-  }
-
-  public void addMethodAndHolderIfContextIsInProfile(ProgramMethod method, ProgramMethod context) {
-    applyIfContextIsInProfile(
-        context, additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder()));
-  }
-
-  void apply(Consumer<ArtProfileAdditions> additionsConsumer) {
-    for (ArtProfileAdditions artProfileAdditions : additionsCollection) {
-      additionsConsumer.accept(artProfileAdditions);
-    }
-  }
-
-  void applyIfContextIsInProfile(
-      ProgramDefinition context,
-      Consumer<ArtProfileAdditions> additionsConsumer,
-      Consumer<ArtProfileAdditionsBuilder> additionsBuilderConsumer) {
-    if (context.isProgramClass()) {
-      applyIfContextIsInProfile(context.asProgramClass(), additionsConsumer);
-    } else {
-      assert context.isProgramMethod();
-      applyIfContextIsInProfile(context.asProgramMethod(), additionsBuilderConsumer);
-    }
-  }
-
-  void applyIfContextIsInProfile(
-      DexProgramClass context, Consumer<ArtProfileAdditions> additionsConsumer) {
-    applyIfContextIsInProfile(context.getType(), additionsConsumer);
-  }
-
-  void applyIfContextIsInProfile(DexType type, Consumer<ArtProfileAdditions> additionsConsumer) {
-    for (ArtProfileAdditions artProfileAdditions : additionsCollection) {
-      artProfileAdditions.applyIfContextIsInProfile(type, additionsConsumer);
-    }
-  }
-
-  public void applyIfContextIsInProfile(
-      ProgramMethod context, Consumer<ArtProfileAdditionsBuilder> builderConsumer) {
-    applyIfContextIsInProfile(context.getReference(), builderConsumer);
-  }
-
-  @Override
-  public void applyIfContextIsInProfile(
-      DexMethod context, Consumer<ArtProfileAdditionsBuilder> builderConsumer) {
-    for (ArtProfileAdditions artProfileAdditions : additionsCollection) {
-      artProfileAdditions.applyIfContextIsInProfile(context, builderConsumer);
-    }
-  }
-
-  @Override
-  public ConcreteArtProfileCollectionAdditions asConcrete() {
-    return this;
-  }
-
-  @Override
-  public void commit(AppView<?> appView) {
-    assert !committed;
-    if (hasAdditions()) {
-      appView.setArtProfileCollection(createNewArtProfileCollection());
-    }
-    committed = true;
-  }
-
-  private ArtProfileCollection createNewArtProfileCollection() {
-    assert hasAdditions();
-    List<ArtProfile> newArtProfiles = new ArrayList<>(additionsCollection.size());
-    for (ArtProfileAdditions additions : additionsCollection) {
-      newArtProfiles.add(additions.createNewArtProfile());
-    }
-    return new NonEmptyArtProfileCollection(newArtProfiles);
-  }
-
-  private boolean hasAdditions() {
-    return Iterables.any(additionsCollection, ArtProfileAdditions::hasAdditions);
-  }
-
-  @Override
-  public ConcreteArtProfileCollectionAdditions rewriteMethodReferences(
-      Function<DexMethod, DexMethod> methodFn) {
-    List<ArtProfileAdditions> rewrittenAdditionsCollection =
-        new ArrayList<>(additionsCollection.size());
-    for (ArtProfileAdditions additions : additionsCollection) {
-      rewrittenAdditionsCollection.add(additions.rewriteMethodReferences(methodFn));
-    }
-    return new ConcreteArtProfileCollectionAdditions(rewrittenAdditionsCollection);
-  }
-
-  @Override
-  public ConcreteArtProfileCollectionAdditions setArtProfileCollection(
-      ArtProfileCollection artProfileCollection) {
-    assert artProfileCollection.isNonEmpty();
-    Iterator<ArtProfile> artProfileIterator = artProfileCollection.asNonEmpty().iterator();
-    for (ArtProfileAdditions additions : additionsCollection) {
-      additions.setArtProfile(artProfileIterator.next());
-    }
-    return this;
-  }
-
-  @Override
-  public boolean verifyIsCommitted() {
-    assert committed;
-    return true;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ConcreteProfileCollectionAdditions.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ConcreteProfileCollectionAdditions.java
new file mode 100644
index 0000000..90cbd6b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ConcreteProfileCollectionAdditions.java
@@ -0,0 +1,191 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile.art.rewriting;
+
+import com.android.tools.r8.experimental.startup.StartupProfile;
+import com.android.tools.r8.experimental.startup.rewriting.StartupProfileAdditions;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramDefinition;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.profile.AbstractProfileMethodRule;
+import com.android.tools.r8.profile.art.ArtProfile;
+import com.android.tools.r8.profile.art.ArtProfileCollection;
+import com.android.tools.r8.profile.art.NonEmptyArtProfileCollection;
+import com.android.tools.r8.profile.art.rewriting.ProfileAdditions.ProfileAdditionsBuilder;
+import com.android.tools.r8.utils.Box;
+import com.google.common.collect.Iterables;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+public class ConcreteProfileCollectionAdditions extends ProfileCollectionAdditions {
+
+  private final List<ArtProfileAdditions> additionsCollection;
+  private final Box<StartupProfileAdditions> startupProfileAdditions;
+
+  private boolean committed = false;
+
+  private ConcreteProfileCollectionAdditions(
+      List<ArtProfileAdditions> additionsCollection,
+      Box<StartupProfileAdditions> startupProfileAdditions) {
+    this.additionsCollection = additionsCollection;
+    this.startupProfileAdditions = startupProfileAdditions;
+  }
+
+  ConcreteProfileCollectionAdditions(
+      ArtProfileCollection artProfileCollection, StartupProfile startupProfile) {
+    additionsCollection = new ArrayList<>();
+    if (artProfileCollection.isNonEmpty()) {
+      for (ArtProfile artProfile : artProfileCollection.asNonEmpty()) {
+        additionsCollection.add(new ArtProfileAdditions(artProfile));
+      }
+      assert !additionsCollection.isEmpty();
+    }
+    startupProfileAdditions =
+        new Box<>(startupProfile.isEmpty() ? null : new StartupProfileAdditions(startupProfile));
+  }
+
+  void accept(Consumer<ProfileAdditions<?, ?, ?, ?, ?, ?, ?, ?>> additionsConsumer) {
+    for (ArtProfileAdditions additions : additionsCollection) {
+      additionsConsumer.accept(additions);
+    }
+    startupProfileAdditions.accept(additionsConsumer);
+  }
+
+  @Override
+  public void addMethodIfContextIsInProfile(ProgramMethod method, ProgramMethod context) {
+    applyIfContextIsInProfile(context, additionsBuilder -> additionsBuilder.addRule(method));
+  }
+
+  public void addMethodIfContextIsInProfile(
+      ProgramMethod method,
+      DexClassAndMethod context,
+      Consumer<AbstractProfileMethodRule.Builder<?, ?>> methodRuleBuilderConsumer) {
+    if (context.isProgramMethod()) {
+      addMethodIfContextIsInProfile(method, context.asProgramMethod());
+    } else {
+      accept(additions -> additions.addMethodRule(method, methodRuleBuilderConsumer));
+    }
+  }
+
+  public void addMethodAndHolderIfContextIsInProfile(ProgramMethod method, ProgramMethod context) {
+    applyIfContextIsInProfile(
+        context, additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder()));
+  }
+
+  void applyIfContextIsInProfile(
+      ProgramDefinition context,
+      Consumer<ProfileAdditions<?, ?, ?, ?, ?, ?, ?, ?>> additionsConsumer,
+      Consumer<ProfileAdditionsBuilder> additionsBuilderConsumer) {
+    if (context.isProgramClass()) {
+      applyIfContextIsInProfile(context.asProgramClass(), additionsConsumer);
+    } else {
+      assert context.isProgramMethod();
+      applyIfContextIsInProfile(context.asProgramMethod(), additionsBuilderConsumer);
+    }
+  }
+
+  void applyIfContextIsInProfile(
+      DexProgramClass context,
+      Consumer<ProfileAdditions<?, ?, ?, ?, ?, ?, ?, ?>> additionsConsumer) {
+    accept(additions -> additions.applyIfContextIsInProfile(context.getType(), additionsConsumer));
+  }
+
+  public void applyIfContextIsInProfile(
+      ProgramMethod context, Consumer<ProfileAdditionsBuilder> builderConsumer) {
+    applyIfContextIsInProfile(context.getReference(), builderConsumer);
+  }
+
+  @Override
+  public void applyIfContextIsInProfile(
+      DexMethod context, Consumer<ProfileAdditionsBuilder> builderConsumer) {
+    accept(additions -> additions.applyIfContextIsInProfile(context, builderConsumer));
+  }
+
+  @Override
+  public ConcreteProfileCollectionAdditions asConcrete() {
+    return this;
+  }
+
+  @Override
+  public void commit(AppView<?> appView) {
+    assert !committed;
+    if (hasArtProfileAdditions()) {
+      appView.setArtProfileCollection(createNewArtProfileCollection());
+    }
+    if (hasStartupProfileAdditions()) {
+      appView.setStartupProfile(createNewStartupProfile());
+    }
+    committed = true;
+  }
+
+  private ArtProfileCollection createNewArtProfileCollection() {
+    assert hasArtProfileAdditions();
+    List<ArtProfile> newArtProfiles = new ArrayList<>(additionsCollection.size());
+    for (ArtProfileAdditions additions : additionsCollection) {
+      newArtProfiles.add(additions.createNewProfile());
+    }
+    return new NonEmptyArtProfileCollection(newArtProfiles);
+  }
+
+  private StartupProfile createNewStartupProfile() {
+    assert hasStartupProfileAdditions();
+    return startupProfileAdditions.get().createNewProfile();
+  }
+
+  private boolean hasArtProfileAdditions() {
+    return Iterables.any(additionsCollection, ProfileAdditions::hasAdditions);
+  }
+
+  private boolean hasStartupProfileAdditions() {
+    return startupProfileAdditions.test(ProfileAdditions::hasAdditions);
+  }
+
+  @Override
+  public ConcreteProfileCollectionAdditions rewriteMethodReferences(
+      Function<DexMethod, DexMethod> methodFn) {
+    List<ArtProfileAdditions> rewrittenAdditionsCollection =
+        new ArrayList<>(additionsCollection.size());
+    for (ArtProfileAdditions additions : additionsCollection) {
+      rewrittenAdditionsCollection.add(additions.rewriteMethodReferences(methodFn));
+    }
+    Box<StartupProfileAdditions> rewrittenStartupProfileAdditions =
+        startupProfileAdditions.rebuild(additions -> additions.rewriteMethodReferences(methodFn));
+    return new ConcreteProfileCollectionAdditions(
+        rewrittenAdditionsCollection, rewrittenStartupProfileAdditions);
+  }
+
+  @Override
+  public ConcreteProfileCollectionAdditions setArtProfileCollection(
+      ArtProfileCollection artProfileCollection) {
+    if (artProfileCollection.isNonEmpty()) {
+      Iterator<ArtProfile> artProfileIterator = artProfileCollection.asNonEmpty().iterator();
+      for (ArtProfileAdditions additions : additionsCollection) {
+        additions.setProfile(artProfileIterator.next());
+      }
+    } else {
+      assert additionsCollection.isEmpty();
+      assert startupProfileAdditions.isSet();
+    }
+    return this;
+  }
+
+  @Override
+  public ProfileCollectionAdditions setStartupProfile(StartupProfile startupProfile) {
+    startupProfileAdditions.accept(additions -> additions.setProfile(startupProfile));
+    return this;
+  }
+
+  @Override
+  public boolean verifyIsCommitted() {
+    assert committed;
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/NopArtProfileCollectionAdditions.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/NopProfileCollectionAdditions.java
similarity index 61%
rename from src/main/java/com/android/tools/r8/profile/art/rewriting/NopArtProfileCollectionAdditions.java
rename to src/main/java/com/android/tools/r8/profile/art/rewriting/NopProfileCollectionAdditions.java
index 8bfd4a6..d27a083 100644
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/NopArtProfileCollectionAdditions.java
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/NopProfileCollectionAdditions.java
@@ -4,22 +4,22 @@
 
 package com.android.tools.r8.profile.art.rewriting;
 
+import com.android.tools.r8.experimental.startup.StartupProfile;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.profile.art.ArtProfileCollection;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileAdditions.ArtProfileAdditionsBuilder;
+import com.android.tools.r8.profile.art.rewriting.ProfileAdditions.ProfileAdditionsBuilder;
 import java.util.function.Consumer;
 import java.util.function.Function;
 
-public class NopArtProfileCollectionAdditions extends ArtProfileCollectionAdditions {
+public class NopProfileCollectionAdditions extends ProfileCollectionAdditions {
 
-  private static final NopArtProfileCollectionAdditions INSTANCE =
-      new NopArtProfileCollectionAdditions();
+  private static final NopProfileCollectionAdditions INSTANCE = new NopProfileCollectionAdditions();
 
-  private NopArtProfileCollectionAdditions() {}
+  private NopProfileCollectionAdditions() {}
 
-  public static NopArtProfileCollectionAdditions getInstance() {
+  public static NopProfileCollectionAdditions getInstance() {
     return INSTANCE;
   }
 
@@ -30,7 +30,7 @@
 
   @Override
   public void applyIfContextIsInProfile(
-      DexMethod context, Consumer<ArtProfileAdditionsBuilder> builderConsumer) {
+      DexMethod context, Consumer<ProfileAdditionsBuilder> builderConsumer) {
     // Intentionally empty.
   }
 
@@ -45,20 +45,26 @@
   }
 
   @Override
-  public NopArtProfileCollectionAdditions rewriteMethodReferences(
+  public NopProfileCollectionAdditions rewriteMethodReferences(
       Function<DexMethod, DexMethod> methodFn) {
     // Intentionally empty.
     return this;
   }
 
   @Override
-  public NopArtProfileCollectionAdditions setArtProfileCollection(
+  public NopProfileCollectionAdditions setArtProfileCollection(
       ArtProfileCollection artProfileCollection) {
     // Intentionally empty.
     return this;
   }
 
   @Override
+  public NopProfileCollectionAdditions setStartupProfile(StartupProfile startupProfile) {
+    // Intentionally empty.
+    return this;
+  }
+
+  @Override
   public boolean verifyIsCommitted() {
     // Nothing to commit.
     return true;
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileAdditions.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileAdditions.java
new file mode 100644
index 0000000..3df9866
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileAdditions.java
@@ -0,0 +1,309 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile.art.rewriting;
+
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramDefinition;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.profile.AbstractProfile;
+import com.android.tools.r8.profile.AbstractProfileClassRule;
+import com.android.tools.r8.profile.AbstractProfileMethodRule;
+import com.android.tools.r8.profile.AbstractProfileRule;
+import com.android.tools.r8.utils.WorkList;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/** Mutable extension of an existing profile. */
+public abstract class ProfileAdditions<
+    Additions extends
+        ProfileAdditions<
+                Additions,
+                ClassRule,
+                ClassRuleBuilder,
+                MethodRule,
+                MethodRuleBuilder,
+                ProfileRule,
+                Profile,
+                ProfileBuilder>,
+    ClassRule extends AbstractProfileClassRule,
+    ClassRuleBuilder extends AbstractProfileClassRule.Builder<ClassRule>,
+    MethodRule extends AbstractProfileMethodRule,
+    MethodRuleBuilder extends AbstractProfileMethodRule.Builder<MethodRule, MethodRuleBuilder>,
+    ProfileRule extends AbstractProfileRule,
+    Profile extends AbstractProfile<ClassRule, MethodRule>,
+    ProfileBuilder extends
+        AbstractProfile.Builder<ClassRule, MethodRule, Profile, ProfileBuilder>> {
+
+  public interface ProfileAdditionsBuilder {
+
+    default ProfileAdditionsBuilder addRule(ProgramDefinition definition) {
+      return addRule(definition.getReference());
+    }
+
+    default ProfileAdditionsBuilder addRule(DexReference reference) {
+      if (reference.isDexType()) {
+        return addClassRule(reference.asDexType());
+      } else {
+        assert reference.isDexMethod();
+        return addMethodRule(reference.asDexMethod());
+      }
+    }
+
+    ProfileAdditionsBuilder addClassRule(DexType type);
+
+    ProfileAdditionsBuilder addMethodRule(DexMethod method);
+
+    default void removeMovedMethodRule(ProgramMethod oldMethod, ProgramMethod newMethod) {
+      removeMovedMethodRule(oldMethod.getReference(), newMethod);
+    }
+
+    void removeMovedMethodRule(DexMethod oldMethod, ProgramMethod newMethod);
+  }
+
+  protected Profile profile;
+
+  final Map<DexType, ClassRuleBuilder> classRuleAdditions = new ConcurrentHashMap<>();
+  final Map<DexMethod, MethodRuleBuilder> methodRuleAdditions = new ConcurrentHashMap<>();
+  private final Set<DexMethod> methodRuleRemovals = Sets.newConcurrentHashSet();
+
+  private final NestedMethodRuleAdditionsGraph nestedMethodRuleAdditionsGraph =
+      new NestedMethodRuleAdditionsGraph();
+
+  protected ProfileAdditions(Profile profile) {
+    this.profile = profile;
+  }
+
+  public void applyIfContextIsInProfile(DexType context, Consumer<? super Additions> fn) {
+    if (profile.containsClassRule(context) || classRuleAdditions.containsKey(context)) {
+      fn.accept(self());
+    }
+  }
+
+  public void applyIfContextIsInProfile(
+      DexMethod context, Consumer<ProfileAdditionsBuilder> builderConsumer) {
+    MethodRule contextMethodRule = profile.getMethodRule(context);
+    if (contextMethodRule != null) {
+      builderConsumer.accept(
+          new ProfileAdditionsBuilder() {
+
+            @Override
+            public ProfileAdditionsBuilder addClassRule(DexType type) {
+              ProfileAdditions.this.addClassRule(type);
+              return this;
+            }
+
+            @Override
+            public ProfileAdditionsBuilder addMethodRule(DexMethod method) {
+              ProfileAdditions.this.addMethodRule(
+                  method, methodRuleBuilder -> methodRuleBuilder.join(contextMethodRule));
+              return this;
+            }
+
+            @Override
+            public void removeMovedMethodRule(DexMethod oldMethod, ProgramMethod newMethod) {
+              ProfileAdditions.this.removeMovedMethodRule(oldMethod, newMethod);
+            }
+          });
+    } else if (methodRuleAdditions.containsKey(context)) {
+      builderConsumer.accept(
+          new ProfileAdditionsBuilder() {
+
+            @Override
+            public ProfileAdditionsBuilder addClassRule(DexType type) {
+              ProfileAdditions.this.addClassRule(type);
+              return this;
+            }
+
+            @Override
+            public ProfileAdditionsBuilder addMethodRule(DexMethod method) {
+              MethodRuleBuilder contextRuleBuilder = methodRuleAdditions.get(context);
+              ProfileAdditions.this.addMethodRule(
+                  method, methodRuleBuilder -> methodRuleBuilder.join(contextRuleBuilder));
+              nestedMethodRuleAdditionsGraph.recordMethodRuleInfoFlagsLargerThan(method, context);
+              return this;
+            }
+
+            @Override
+            public void removeMovedMethodRule(DexMethod oldMethod, ProgramMethod newMethod) {
+              ProfileAdditions.this.removeMovedMethodRule(oldMethod, newMethod);
+            }
+          });
+    }
+  }
+
+  public Additions addClassRule(DexClass clazz) {
+    addClassRule(clazz.getType());
+    return self();
+  }
+
+  public void addClassRule(DexType type) {
+    if (profile.containsClassRule(type)) {
+      return;
+    }
+
+    // Create profile rule for class.
+    classRuleAdditions.computeIfAbsent(type, this::createClassRuleBuilder);
+  }
+
+  public Additions addMethodRule(
+      DexClassAndMethod method, Consumer<? super MethodRuleBuilder> methodRuleBuilderConsumer) {
+    return addMethodRule(method.getReference(), methodRuleBuilderConsumer);
+  }
+
+  public Additions addMethodRule(
+      DexMethod method, Consumer<? super MethodRuleBuilder> methodRuleBuilderConsumer) {
+    // Create profile rule for method.
+    MethodRuleBuilder methodRuleBuilder =
+        methodRuleAdditions.computeIfAbsent(method, this::createMethodRuleBuilder);
+
+    // Setup the rule.
+    synchronized (methodRuleBuilder) {
+      methodRuleBuilderConsumer.accept(methodRuleBuilder);
+    }
+
+    return self();
+  }
+
+  void removeMovedMethodRule(DexMethod oldMethod, ProgramMethod newMethod) {
+    assert profile.containsMethodRule(oldMethod) || methodRuleAdditions.containsKey(oldMethod);
+    assert methodRuleAdditions.containsKey(newMethod.getReference());
+    methodRuleRemovals.add(oldMethod);
+  }
+
+  public Profile createNewProfile() {
+    if (!hasAdditions()) {
+      assert !hasRemovals();
+      return profile;
+    }
+
+    nestedMethodRuleAdditionsGraph.propagateMethodRuleInfoFlags(methodRuleAdditions);
+
+    // Add existing rules to new profile.
+    ProfileBuilder profileBuilder = createProfileBuilder();
+    profile.forEachRule(
+        profileBuilder::addClassRule,
+        methodRule -> {
+          if (methodRuleRemovals.contains(methodRule.getReference())) {
+            return;
+          }
+          MethodRuleBuilder methodRuleBuilder =
+              methodRuleAdditions.remove(methodRule.getReference());
+          if (methodRuleBuilder != null) {
+            MethodRule newMethodRule = methodRuleBuilder.join(methodRule).build();
+            profileBuilder.addMethodRule(newMethodRule);
+          } else {
+            profileBuilder.addMethodRule(methodRule);
+          }
+        });
+
+    // Sort and add additions to new profile. Sorting is needed since the additions to this
+    // collection may be concurrent.
+    List<AbstractProfileRule> ruleAdditionsSorted =
+        new ArrayList<>(classRuleAdditions.size() + methodRuleAdditions.size());
+    classRuleAdditions
+        .values()
+        .forEach(classRuleBuilder -> ruleAdditionsSorted.add(classRuleBuilder.build()));
+    methodRuleAdditions
+        .values()
+        .forEach(methodRuleBuilder -> ruleAdditionsSorted.add(methodRuleBuilder.build()));
+    ruleAdditionsSorted.sort(getRuleComparator());
+    ruleAdditionsSorted.forEach(profileBuilder::addRule);
+
+    return profileBuilder.build();
+  }
+
+  public boolean hasAdditions() {
+    return !classRuleAdditions.isEmpty() || !methodRuleAdditions.isEmpty();
+  }
+
+  private boolean hasRemovals() {
+    return !methodRuleRemovals.isEmpty();
+  }
+
+  public Additions rewriteMethodReferences(Function<DexMethod, DexMethod> methodFn) {
+    Additions rewrittenAdditions = create();
+    assert methodRuleRemovals.isEmpty();
+    rewrittenAdditions.classRuleAdditions.putAll(classRuleAdditions);
+    methodRuleAdditions.forEach(
+        (method, methodRuleBuilder) -> {
+          DexMethod newMethod = methodFn.apply(method);
+          MethodRuleBuilder existingMethodRuleBuilder =
+              rewrittenAdditions.methodRuleAdditions.put(
+                  newMethod, methodRuleBuilder.setMethod(newMethod));
+          assert existingMethodRuleBuilder == null;
+        });
+    return rewrittenAdditions;
+  }
+
+  public abstract Additions create();
+
+  public abstract ClassRuleBuilder createClassRuleBuilder(DexType type);
+
+  public abstract MethodRuleBuilder createMethodRuleBuilder(DexMethod method);
+
+  public abstract ProfileBuilder createProfileBuilder();
+
+  public abstract Comparator<AbstractProfileRule> getRuleComparator();
+
+  public abstract Additions self();
+
+  public void setProfile(Profile profile) {
+    this.profile = profile;
+  }
+
+  private class NestedMethodRuleAdditionsGraph {
+
+    private final Map<DexMethod, Set<DexMethod>> successors = new ConcurrentHashMap<>();
+    private final Map<DexMethod, Set<DexMethod>> predecessors = new ConcurrentHashMap<>();
+
+    void recordMethodRuleInfoFlagsLargerThan(DexMethod largerFlags, DexMethod smallerFlags) {
+      predecessors
+          .computeIfAbsent(largerFlags, ignoreKey(Sets::newConcurrentHashSet))
+          .add(smallerFlags);
+      successors
+          .computeIfAbsent(smallerFlags, ignoreKey(Sets::newConcurrentHashSet))
+          .add(largerFlags);
+    }
+
+    void propagateMethodRuleInfoFlags(Map<DexMethod, MethodRuleBuilder> methodRuleAdditions) {
+      List<DexMethod> leaves =
+          successors.keySet().stream()
+              .filter(method -> predecessors.getOrDefault(method, Collections.emptySet()).isEmpty())
+              .collect(Collectors.toList());
+      WorkList<DexMethod> worklist = WorkList.newIdentityWorkList(leaves);
+      while (worklist.hasNext()) {
+        DexMethod method = worklist.next();
+        MethodRuleBuilder methodRuleBuilder = methodRuleAdditions.get(method);
+        for (DexMethod successor : successors.getOrDefault(method, Collections.emptySet())) {
+          MethodRuleBuilder successorMethodRuleBuilder = methodRuleAdditions.get(successor);
+          // If this assertion fails, that means we have synthetics with multiple
+          // synthesizing contexts, which are not guaranteed to be processed before the
+          // synthetic itself. In that case this assertion should simply be removed.
+          assert successorMethodRuleBuilder.isGreaterThanOrEqualTo(methodRuleBuilder);
+          successorMethodRuleBuilder.join(methodRuleBuilder);
+          // Note: no need to addIgnoringSeenSet() since the graph will not have cycles. Indeed, it
+          // should never be the case that a method m2(), which is synthesized from method context
+          // m1(), would itself be a synthesizing context for m1().
+          worklist.addIfNotSeen(successor);
+        }
+      }
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileCollectionAdditions.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileCollectionAdditions.java
new file mode 100644
index 0000000..93be945
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileCollectionAdditions.java
@@ -0,0 +1,65 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile.art.rewriting;
+
+import com.android.tools.r8.experimental.startup.StartupProfile;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.profile.art.ArtProfileCollection;
+import com.android.tools.r8.profile.art.rewriting.ProfileAdditions.ProfileAdditionsBuilder;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * Interface for adding (synthetic) items to existing instances of {@link
+ * com.android.tools.r8.profile.AbstractProfile}.
+ *
+ * <p>The interface will be implemented by {@link NopProfileCollectionAdditions} when the
+ * compilation does not contain any ART profiles, for minimal performance overhead.
+ *
+ * <p>When one or more ART profiles are present, or a startup profile is, then this class is
+ * implemented by {@link ConcreteProfileCollectionAdditions}.
+ */
+public abstract class ProfileCollectionAdditions {
+
+  public static ProfileCollectionAdditions create(AppView<?> appView) {
+    ArtProfileCollection artProfileCollection = appView.getArtProfileCollection();
+    StartupProfile startupProfile = appView.getStartupProfile();
+    if (artProfileCollection.isEmpty() && startupProfile.isEmpty()) {
+      return nop();
+    }
+    return new ConcreteProfileCollectionAdditions(artProfileCollection, startupProfile);
+  }
+
+  public static NopProfileCollectionAdditions nop() {
+    return NopProfileCollectionAdditions.getInstance();
+  }
+
+  public abstract void addMethodIfContextIsInProfile(ProgramMethod method, ProgramMethod context);
+
+  public abstract void applyIfContextIsInProfile(
+      DexMethod context, Consumer<ProfileAdditionsBuilder> builderConsumer);
+
+  public abstract void commit(AppView<?> appView);
+
+  public boolean isNop() {
+    return false;
+  }
+
+  public ConcreteProfileCollectionAdditions asConcrete() {
+    return null;
+  }
+
+  public abstract ProfileCollectionAdditions rewriteMethodReferences(
+      Function<DexMethod, DexMethod> methodFn);
+
+  public abstract ProfileCollectionAdditions setArtProfileCollection(
+      ArtProfileCollection artProfileCollection);
+
+  public abstract ProfileCollectionAdditions setStartupProfile(StartupProfile startupProfile);
+
+  public abstract boolean verifyIsCommitted();
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingApiReferenceStubberEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingApiReferenceStubberEventConsumer.java
similarity index 76%
rename from src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingApiReferenceStubberEventConsumer.java
rename to src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingApiReferenceStubberEventConsumer.java
index 0b4dfde..4f7f76c 100644
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingApiReferenceStubberEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingApiReferenceStubberEventConsumer.java
@@ -11,14 +11,14 @@
 import com.android.tools.r8.graph.DexLibraryClass;
 import com.android.tools.r8.graph.DexProgramClass;
 
-public class ArtProfileRewritingApiReferenceStubberEventConsumer
+public class ProfileRewritingApiReferenceStubberEventConsumer
     implements ApiReferenceStubberEventConsumer {
 
-  private final ConcreteArtProfileCollectionAdditions collectionAdditions;
+  private final ConcreteProfileCollectionAdditions collectionAdditions;
   private final ApiReferenceStubberEventConsumer parent;
 
-  private ArtProfileRewritingApiReferenceStubberEventConsumer(
-      ConcreteArtProfileCollectionAdditions collectionAdditions,
+  private ProfileRewritingApiReferenceStubberEventConsumer(
+      ConcreteProfileCollectionAdditions collectionAdditions,
       ApiReferenceStubberEventConsumer parent) {
     this.collectionAdditions = collectionAdditions;
     this.parent = parent;
@@ -27,11 +27,11 @@
   public static ApiReferenceStubberEventConsumer attach(
       AppView<?> appView, ApiReferenceStubberEventConsumer eventConsumer) {
     if (appView.options().getArtProfileOptions().isIncludingApiReferenceStubs()) {
-      ArtProfileCollectionAdditions artProfileCollectionAdditions =
-          ArtProfileCollectionAdditions.create(appView);
-      if (!artProfileCollectionAdditions.isNop()) {
-        return new ArtProfileRewritingApiReferenceStubberEventConsumer(
-            artProfileCollectionAdditions.asConcrete(), eventConsumer);
+      ProfileCollectionAdditions profileCollectionAdditions =
+          ProfileCollectionAdditions.create(appView);
+      if (!profileCollectionAdditions.isNop()) {
+        return new ProfileRewritingApiReferenceStubberEventConsumer(
+            profileCollectionAdditions.asConcrete(), eventConsumer);
       }
     }
     return eventConsumer;
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingArgumentPropagatorSyntheticEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingArgumentPropagatorSyntheticEventConsumer.java
similarity index 77%
rename from src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingArgumentPropagatorSyntheticEventConsumer.java
rename to src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingArgumentPropagatorSyntheticEventConsumer.java
index 1084d3a..dece00f 100644
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingArgumentPropagatorSyntheticEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingArgumentPropagatorSyntheticEventConsumer.java
@@ -10,14 +10,14 @@
 import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorSyntheticEventConsumer;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
-public class ArtProfileRewritingArgumentPropagatorSyntheticEventConsumer
+public class ProfileRewritingArgumentPropagatorSyntheticEventConsumer
     implements ArgumentPropagatorSyntheticEventConsumer {
 
-  private final ConcreteArtProfileCollectionAdditions additionsCollection;
+  private final ConcreteProfileCollectionAdditions additionsCollection;
   private final ArgumentPropagatorSyntheticEventConsumer parent;
 
-  private ArtProfileRewritingArgumentPropagatorSyntheticEventConsumer(
-      ConcreteArtProfileCollectionAdditions additionsCollection,
+  private ProfileRewritingArgumentPropagatorSyntheticEventConsumer(
+      ConcreteProfileCollectionAdditions additionsCollection,
       ArgumentPropagatorSyntheticEventConsumer parent) {
     this.additionsCollection = additionsCollection;
     this.parent = parent;
@@ -26,12 +26,11 @@
   public static ArgumentPropagatorSyntheticEventConsumer attach(
       AppView<AppInfoWithLiveness> appView,
       ArgumentPropagatorSyntheticEventConsumer eventConsumer) {
-    ArtProfileCollectionAdditions additionsCollection =
-        ArtProfileCollectionAdditions.create(appView);
+    ProfileCollectionAdditions additionsCollection = ProfileCollectionAdditions.create(appView);
     if (additionsCollection.isNop()) {
       return eventConsumer;
     }
-    return new ArtProfileRewritingArgumentPropagatorSyntheticEventConsumer(
+    return new ProfileRewritingArgumentPropagatorSyntheticEventConsumer(
         additionsCollection.asConcrete(), eventConsumer);
   }
 
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingCfClassSynthesizerDesugaringEventConsumer.java
similarity index 83%
rename from src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer.java
rename to src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingCfClassSynthesizerDesugaringEventConsumer.java
index ebc4799..2d0f51c 100644
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingCfClassSynthesizerDesugaringEventConsumer.java
@@ -4,7 +4,7 @@
 
 package com.android.tools.r8.profile.art.rewriting;
 
-import static com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingVarHandleDesugaringEventConsumerUtils.handleVarHandleDesugaringClassContext;
+import static com.android.tools.r8.profile.art.rewriting.ProfileRewritingVarHandleDesugaringEventConsumerUtils.handleVarHandleDesugaringClassContext;
 
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
@@ -15,15 +15,15 @@
 import com.android.tools.r8.profile.art.ArtProfileOptions;
 import java.util.Set;
 
-public class ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer
+public class ProfileRewritingCfClassSynthesizerDesugaringEventConsumer
     extends CfClassSynthesizerDesugaringEventConsumer {
 
-  private final ConcreteArtProfileCollectionAdditions additionsCollection;
+  private final ConcreteProfileCollectionAdditions additionsCollection;
   private final ArtProfileOptions options;
   private final CfClassSynthesizerDesugaringEventConsumer parent;
 
-  private ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer(
-      ConcreteArtProfileCollectionAdditions additionsCollection,
+  private ProfileRewritingCfClassSynthesizerDesugaringEventConsumer(
+      ConcreteProfileCollectionAdditions additionsCollection,
       ArtProfileOptions options,
       CfClassSynthesizerDesugaringEventConsumer parent) {
     this.additionsCollection = additionsCollection;
@@ -33,18 +33,18 @@
 
   public static CfClassSynthesizerDesugaringEventConsumer attach(
       AppView<?> appView, CfClassSynthesizerDesugaringEventConsumer eventConsumer) {
-    return attach(appView, eventConsumer, ArtProfileCollectionAdditions.create(appView));
+    return attach(appView, eventConsumer, ProfileCollectionAdditions.create(appView));
   }
 
   public static CfClassSynthesizerDesugaringEventConsumer attach(
       AppView<?> appView,
       CfClassSynthesizerDesugaringEventConsumer eventConsumer,
-      ArtProfileCollectionAdditions artProfileCollectionAdditions) {
-    if (artProfileCollectionAdditions.isNop()) {
+      ProfileCollectionAdditions profileCollectionAdditions) {
+    if (profileCollectionAdditions.isNop()) {
       return eventConsumer;
     }
-    return new ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer(
-        artProfileCollectionAdditions.asConcrete(),
+    return new ProfileRewritingCfClassSynthesizerDesugaringEventConsumer(
+        profileCollectionAdditions.asConcrete(),
         appView.options().getArtProfileOptions(),
         eventConsumer);
   }
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingCfInstructionDesugaringEventConsumer.java
similarity index 95%
rename from src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfInstructionDesugaringEventConsumer.java
rename to src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingCfInstructionDesugaringEventConsumer.java
index 3ac8a23..60a9e96 100644
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingCfInstructionDesugaringEventConsumer.java
@@ -4,7 +4,7 @@
 
 package com.android.tools.r8.profile.art.rewriting;
 
-import static com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingVarHandleDesugaringEventConsumerUtils.handleVarHandleDesugaringClassContext;
+import static com.android.tools.r8.profile.art.rewriting.ProfileRewritingVarHandleDesugaringEventConsumerUtils.handleVarHandleDesugaringClassContext;
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClassAndMethod;
@@ -22,36 +22,36 @@
 import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaringEventConsumer;
 import java.util.List;
 
-public class ArtProfileRewritingCfInstructionDesugaringEventConsumer
+public class ProfileRewritingCfInstructionDesugaringEventConsumer
     extends CfInstructionDesugaringEventConsumer {
 
   private final AppView<?> appView;
-  private final ConcreteArtProfileCollectionAdditions additionsCollection;
+  private final ConcreteProfileCollectionAdditions additionsCollection;
   private final CfInstructionDesugaringEventConsumer parent;
 
   private final NestBasedAccessDesugaringEventConsumer nestBasedAccessDesugaringEventConsumer;
 
-  private ArtProfileRewritingCfInstructionDesugaringEventConsumer(
+  private ProfileRewritingCfInstructionDesugaringEventConsumer(
       AppView<?> appView,
-      ConcreteArtProfileCollectionAdditions additionsCollection,
+      ConcreteProfileCollectionAdditions additionsCollection,
       CfInstructionDesugaringEventConsumer parent) {
     this.appView = appView;
     this.additionsCollection = additionsCollection;
     this.parent = parent;
     this.nestBasedAccessDesugaringEventConsumer =
-        ArtProfileRewritingNestBasedAccessDesugaringEventConsumer.attach(
+        ProfileRewritingNestBasedAccessDesugaringEventConsumer.attach(
             additionsCollection, NestBasedAccessDesugaringEventConsumer.empty());
   }
 
   public static CfInstructionDesugaringEventConsumer attach(
       AppView<?> appView,
-      ArtProfileCollectionAdditions artProfileCollectionAdditions,
+      ProfileCollectionAdditions profileCollectionAdditions,
       CfInstructionDesugaringEventConsumer eventConsumer) {
-    if (artProfileCollectionAdditions.isNop()) {
+    if (profileCollectionAdditions.isNop()) {
       return eventConsumer;
     }
-    return new ArtProfileRewritingCfInstructionDesugaringEventConsumer(
-        appView, artProfileCollectionAdditions.asConcrete(), eventConsumer);
+    return new ProfileRewritingCfInstructionDesugaringEventConsumer(
+        appView, profileCollectionAdditions.asConcrete(), eventConsumer);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfPostProcessingDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingCfPostProcessingDesugaringEventConsumer.java
similarity index 90%
rename from src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfPostProcessingDesugaringEventConsumer.java
rename to src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingCfPostProcessingDesugaringEventConsumer.java
index 568efcc..5402a78 100644
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfPostProcessingDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingCfPostProcessingDesugaringEventConsumer.java
@@ -23,15 +23,15 @@
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 
-public class ArtProfileRewritingCfPostProcessingDesugaringEventConsumer
+public class ProfileRewritingCfPostProcessingDesugaringEventConsumer
     extends CfPostProcessingDesugaringEventConsumer {
 
-  private final ConcreteArtProfileCollectionAdditions additionsCollection;
+  private final ConcreteProfileCollectionAdditions additionsCollection;
   private final ArtProfileOptions options;
   private final CfPostProcessingDesugaringEventConsumer parent;
 
-  private ArtProfileRewritingCfPostProcessingDesugaringEventConsumer(
-      ConcreteArtProfileCollectionAdditions additionsCollection,
+  private ProfileRewritingCfPostProcessingDesugaringEventConsumer(
+      ConcreteProfileCollectionAdditions additionsCollection,
       ArtProfileOptions options,
       CfPostProcessingDesugaringEventConsumer parent) {
     this.additionsCollection = additionsCollection;
@@ -41,13 +41,13 @@
 
   public static CfPostProcessingDesugaringEventConsumer attach(
       AppView<?> appView,
-      ArtProfileCollectionAdditions artProfileCollectionAdditions,
+      ProfileCollectionAdditions profileCollectionAdditions,
       CfPostProcessingDesugaringEventConsumer eventConsumer) {
-    if (artProfileCollectionAdditions.isNop()) {
+    if (profileCollectionAdditions.isNop()) {
       return eventConsumer;
     }
-    return new ArtProfileRewritingCfPostProcessingDesugaringEventConsumer(
-        artProfileCollectionAdditions.asConcrete(),
+    return new ProfileRewritingCfPostProcessingDesugaringEventConsumer(
+        profileCollectionAdditions.asConcrete(),
         appView.options().getArtProfileOptions(),
         eventConsumer);
   }
@@ -80,7 +80,7 @@
   public void acceptDesugaredLibraryRetargeterForwardingMethod(
       ProgramMethod method, EmulatedDispatchMethodDescriptor descriptor) {
     if (options.isIncludingDesugaredLibraryRetargeterForwardingMethodsUnconditionally()) {
-      additionsCollection.apply(additions -> additions.addMethodRule(method, emptyConsumer()));
+      additionsCollection.accept(additions -> additions.addMethodRule(method, emptyConsumer()));
     }
     parent.acceptDesugaredLibraryRetargeterForwardingMethod(method, descriptor);
   }
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCovariantReturnTypeAnnotationTransformerEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingCovariantReturnTypeAnnotationTransformerEventConsumer.java
similarity index 73%
rename from src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCovariantReturnTypeAnnotationTransformerEventConsumer.java
rename to src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingCovariantReturnTypeAnnotationTransformerEventConsumer.java
index 9722924..32c6d73 100644
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCovariantReturnTypeAnnotationTransformerEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingCovariantReturnTypeAnnotationTransformerEventConsumer.java
@@ -7,26 +7,26 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.CovariantReturnTypeAnnotationTransformerEventConsumer;
 
-public class ArtProfileRewritingCovariantReturnTypeAnnotationTransformerEventConsumer
+public class ProfileRewritingCovariantReturnTypeAnnotationTransformerEventConsumer
     implements CovariantReturnTypeAnnotationTransformerEventConsumer {
 
-  private final ConcreteArtProfileCollectionAdditions additionsCollection;
+  private final ConcreteProfileCollectionAdditions additionsCollection;
   private final CovariantReturnTypeAnnotationTransformerEventConsumer parent;
 
-  private ArtProfileRewritingCovariantReturnTypeAnnotationTransformerEventConsumer(
-      ConcreteArtProfileCollectionAdditions additionsCollection,
+  private ProfileRewritingCovariantReturnTypeAnnotationTransformerEventConsumer(
+      ConcreteProfileCollectionAdditions additionsCollection,
       CovariantReturnTypeAnnotationTransformerEventConsumer parent) {
     this.additionsCollection = additionsCollection;
     this.parent = parent;
   }
 
   public static CovariantReturnTypeAnnotationTransformerEventConsumer attach(
-      ArtProfileCollectionAdditions additionsCollection,
+      ProfileCollectionAdditions additionsCollection,
       CovariantReturnTypeAnnotationTransformerEventConsumer eventConsumer) {
     if (additionsCollection.isNop()) {
       return eventConsumer;
     }
-    return new ArtProfileRewritingCovariantReturnTypeAnnotationTransformerEventConsumer(
+    return new ProfileRewritingCovariantReturnTypeAnnotationTransformerEventConsumer(
         additionsCollection.asConcrete(), eventConsumer);
   }
 
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingMemberRebindingEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingMemberRebindingEventConsumer.java
similarity index 75%
rename from src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingMemberRebindingEventConsumer.java
rename to src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingMemberRebindingEventConsumer.java
index df87170..1c9b0a5 100644
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingMemberRebindingEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingMemberRebindingEventConsumer.java
@@ -13,27 +13,24 @@
 import com.android.tools.r8.optimize.MemberRebindingLens;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
-public class ArtProfileRewritingMemberRebindingEventConsumer
-    implements MemberRebindingEventConsumer {
+public class ProfileRewritingMemberRebindingEventConsumer implements MemberRebindingEventConsumer {
 
-  private final ConcreteArtProfileCollectionAdditions additionsCollection;
+  private final ConcreteProfileCollectionAdditions additionsCollection;
   private final MemberRebindingEventConsumer parent;
 
-  private ArtProfileRewritingMemberRebindingEventConsumer(
-      ConcreteArtProfileCollectionAdditions additionsCollection,
-      MemberRebindingEventConsumer parent) {
+  private ProfileRewritingMemberRebindingEventConsumer(
+      ConcreteProfileCollectionAdditions additionsCollection, MemberRebindingEventConsumer parent) {
     this.additionsCollection = additionsCollection;
     this.parent = parent;
   }
 
   public static MemberRebindingEventConsumer attach(
       AppView<AppInfoWithLiveness> appView, MemberRebindingEventConsumer eventConsumer) {
-    ArtProfileCollectionAdditions additionsCollection =
-        ArtProfileCollectionAdditions.create(appView);
+    ProfileCollectionAdditions additionsCollection = ProfileCollectionAdditions.create(appView);
     if (additionsCollection.isNop()) {
       return eventConsumer;
     }
-    return new ArtProfileRewritingMemberRebindingEventConsumer(
+    return new ProfileRewritingMemberRebindingEventConsumer(
         additionsCollection.asConcrete(), eventConsumer);
   }
 
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingMethodProcessorEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingMethodProcessorEventConsumer.java
similarity index 87%
rename from src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingMethodProcessorEventConsumer.java
rename to src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingMethodProcessorEventConsumer.java
index dea3841..c5193da 100644
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingMethodProcessorEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingMethodProcessorEventConsumer.java
@@ -9,37 +9,35 @@
 import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
-public class ArtProfileRewritingMethodProcessorEventConsumer extends MethodProcessorEventConsumer {
+public class ProfileRewritingMethodProcessorEventConsumer extends MethodProcessorEventConsumer {
 
-  private final ConcreteArtProfileCollectionAdditions additionsCollection;
+  private final ConcreteProfileCollectionAdditions additionsCollection;
   private final MethodProcessorEventConsumer parent;
 
-  private ArtProfileRewritingMethodProcessorEventConsumer(
-      ConcreteArtProfileCollectionAdditions additionsCollection,
-      MethodProcessorEventConsumer parent) {
+  private ProfileRewritingMethodProcessorEventConsumer(
+      ConcreteProfileCollectionAdditions additionsCollection, MethodProcessorEventConsumer parent) {
     this.additionsCollection = additionsCollection;
     this.parent = parent;
   }
 
   public static MethodProcessorEventConsumer attach(
       AppView<?> appView, MethodProcessorEventConsumer eventConsumer) {
-    ArtProfileCollectionAdditions additionsCollection =
-        ArtProfileCollectionAdditions.create(appView);
+    ProfileCollectionAdditions additionsCollection = ProfileCollectionAdditions.create(appView);
     if (additionsCollection.isNop()) {
       return eventConsumer;
     }
-    return new ArtProfileRewritingMethodProcessorEventConsumer(
+    return new ProfileRewritingMethodProcessorEventConsumer(
         additionsCollection.asConcrete(), eventConsumer);
   }
 
   public static MethodProcessorEventConsumer attach(
-      ArtProfileCollectionAdditions artProfileCollectionAdditions,
+      ProfileCollectionAdditions profileCollectionAdditions,
       MethodProcessorEventConsumer eventConsumer) {
-    if (artProfileCollectionAdditions.isNop()) {
+    if (profileCollectionAdditions.isNop()) {
       return eventConsumer;
     }
-    return new ArtProfileRewritingMethodProcessorEventConsumer(
-        artProfileCollectionAdditions.asConcrete(), eventConsumer);
+    return new ProfileRewritingMethodProcessorEventConsumer(
+        profileCollectionAdditions.asConcrete(), eventConsumer);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingNestBasedAccessDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingNestBasedAccessDesugaringEventConsumer.java
similarity index 85%
rename from src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingNestBasedAccessDesugaringEventConsumer.java
rename to src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingNestBasedAccessDesugaringEventConsumer.java
index 2988ce4..28552b5 100644
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingNestBasedAccessDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingNestBasedAccessDesugaringEventConsumer.java
@@ -12,26 +12,26 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaringEventConsumer;
 
-public class ArtProfileRewritingNestBasedAccessDesugaringEventConsumer
+public class ProfileRewritingNestBasedAccessDesugaringEventConsumer
     implements NestBasedAccessDesugaringEventConsumer {
 
-  private final ConcreteArtProfileCollectionAdditions additionsCollection;
+  private final ConcreteProfileCollectionAdditions additionsCollection;
   private final NestBasedAccessDesugaringEventConsumer parent;
 
-  private ArtProfileRewritingNestBasedAccessDesugaringEventConsumer(
-      ConcreteArtProfileCollectionAdditions additionsCollection,
+  private ProfileRewritingNestBasedAccessDesugaringEventConsumer(
+      ConcreteProfileCollectionAdditions additionsCollection,
       NestBasedAccessDesugaringEventConsumer parent) {
     this.additionsCollection = additionsCollection;
     this.parent = parent;
   }
 
   public static NestBasedAccessDesugaringEventConsumer attach(
-      ArtProfileCollectionAdditions additionsCollection,
+      ProfileCollectionAdditions additionsCollection,
       NestBasedAccessDesugaringEventConsumer eventConsumer) {
     if (additionsCollection.isNop()) {
       return eventConsumer;
     }
-    return new ArtProfileRewritingNestBasedAccessDesugaringEventConsumer(
+    return new ProfileRewritingNestBasedAccessDesugaringEventConsumer(
         additionsCollection.asConcrete(), eventConsumer);
   }
 
@@ -46,7 +46,7 @@
           context.asProgramMethod(),
           additionsBuilder -> additionsBuilder.addRule(argumentClass).addRule(bridge));
     } else {
-      additionsCollection.apply(
+      additionsCollection.accept(
           additions ->
               additions.addClassRule(argumentClass).addMethodRule(bridge, emptyConsumer()));
     }
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingOutlineOptimizationEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingOutlineOptimizationEventConsumer.java
similarity index 78%
rename from src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingOutlineOptimizationEventConsumer.java
rename to src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingOutlineOptimizationEventConsumer.java
index 2184a87..4343c25 100644
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingOutlineOptimizationEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingOutlineOptimizationEventConsumer.java
@@ -10,14 +10,14 @@
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.Collection;
 
-public class ArtProfileRewritingOutlineOptimizationEventConsumer
+public class ProfileRewritingOutlineOptimizationEventConsumer
     implements OutlineOptimizationEventConsumer {
 
-  private final ConcreteArtProfileCollectionAdditions additionsCollection;
+  private final ConcreteProfileCollectionAdditions additionsCollection;
   private final OutlineOptimizationEventConsumer parent;
 
-  private ArtProfileRewritingOutlineOptimizationEventConsumer(
-      ConcreteArtProfileCollectionAdditions additionsCollection,
+  private ProfileRewritingOutlineOptimizationEventConsumer(
+      ConcreteProfileCollectionAdditions additionsCollection,
       OutlineOptimizationEventConsumer parent) {
     this.additionsCollection = additionsCollection;
     this.parent = parent;
@@ -25,12 +25,11 @@
 
   public static OutlineOptimizationEventConsumer attach(
       AppView<AppInfoWithLiveness> appView, OutlineOptimizationEventConsumer eventConsumer) {
-    ArtProfileCollectionAdditions additionsCollection =
-        ArtProfileCollectionAdditions.create(appView);
+    ProfileCollectionAdditions additionsCollection = ProfileCollectionAdditions.create(appView);
     if (additionsCollection.isNop()) {
       return eventConsumer;
     }
-    return new ArtProfileRewritingOutlineOptimizationEventConsumer(
+    return new ProfileRewritingOutlineOptimizationEventConsumer(
         additionsCollection.asConcrete(), eventConsumer);
   }
 
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingRootSetBuilderEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingRootSetBuilderEventConsumer.java
similarity index 80%
rename from src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingRootSetBuilderEventConsumer.java
rename to src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingRootSetBuilderEventConsumer.java
index 005c33c..9b9d6a5 100644
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingRootSetBuilderEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingRootSetBuilderEventConsumer.java
@@ -7,25 +7,23 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.shaking.RootSetBuilderEventConsumer;
 
-public class ArtProfileRewritingRootSetBuilderEventConsumer implements RootSetBuilderEventConsumer {
+public class ProfileRewritingRootSetBuilderEventConsumer implements RootSetBuilderEventConsumer {
 
-  private final ConcreteArtProfileCollectionAdditions additionsCollection;
+  private final ConcreteProfileCollectionAdditions additionsCollection;
   private final RootSetBuilderEventConsumer parent;
 
-  private ArtProfileRewritingRootSetBuilderEventConsumer(
-      ConcreteArtProfileCollectionAdditions additionsCollection,
-      RootSetBuilderEventConsumer parent) {
+  private ProfileRewritingRootSetBuilderEventConsumer(
+      ConcreteProfileCollectionAdditions additionsCollection, RootSetBuilderEventConsumer parent) {
     this.additionsCollection = additionsCollection;
     this.parent = parent;
   }
 
   public static RootSetBuilderEventConsumer attach(
-      ArtProfileCollectionAdditions additionsCollection,
-      RootSetBuilderEventConsumer eventConsumer) {
+      ProfileCollectionAdditions additionsCollection, RootSetBuilderEventConsumer eventConsumer) {
     if (additionsCollection.isNop()) {
       return eventConsumer;
     }
-    return new ArtProfileRewritingRootSetBuilderEventConsumer(
+    return new ProfileRewritingRootSetBuilderEventConsumer(
         additionsCollection.asConcrete(), eventConsumer);
   }
 
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingVarHandleDesugaringEventConsumerUtils.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingVarHandleDesugaringEventConsumerUtils.java
similarity index 89%
rename from src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingVarHandleDesugaringEventConsumerUtils.java
rename to src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingVarHandleDesugaringEventConsumerUtils.java
index f53e9bc..8792769 100644
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingVarHandleDesugaringEventConsumerUtils.java
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ProfileRewritingVarHandleDesugaringEventConsumerUtils.java
@@ -10,12 +10,12 @@
 import com.android.tools.r8.graph.ProgramDefinition;
 import com.android.tools.r8.profile.art.ArtProfileOptions;
 
-public class ArtProfileRewritingVarHandleDesugaringEventConsumerUtils {
+public class ProfileRewritingVarHandleDesugaringEventConsumerUtils {
 
   static void handleVarHandleDesugaringClassContext(
       DexProgramClass varHandleClass,
       ProgramDefinition context,
-      ConcreteArtProfileCollectionAdditions additionsCollection,
+      ConcreteProfileCollectionAdditions additionsCollection,
       ArtProfileOptions options) {
     if (options.isIncludingVarHandleClasses()) {
       additionsCollection.applyIfContextIsInProfile(
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
index 40ee16e..16bef88 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
@@ -146,7 +146,8 @@
       MethodResolutionResult methodResult = resolutionResult.asMethodResolutionResult();
       if (methodResult.isClassNotFoundResult()
           || methodResult.isArrayCloneMethodResult()
-          || methodResult.isNoSuchMethodErrorResult(getContext().getContextClass(), appInfo)) {
+          || methodResult.isNoSuchMethodErrorResult(
+              getContext().getContextClass(), appView, appInfo)) {
         return;
       }
       node.addNeighbor(missingTypeNode);
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java
index f82327a..2deef6f 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java
@@ -228,24 +228,28 @@
     OptionalInt originalPosition = mappedRangeForFrame.position;
     if (!isAmbiguous()
         && (mappedRange.minifiedRange == null || obfuscatedPosition.orElse(-1) == -1)) {
-      int originalLineNumber = mappedRange.getFirstPositionOfOriginalRange(0);
-      if (originalLineNumber > 0) {
-        return RetracedMethodReferenceImpl.create(
-            methodReference, OptionalUtils.orElse(originalPosition, originalLineNumber));
-      } else {
-        return RetracedMethodReferenceImpl.create(methodReference, originalPosition);
-      }
+      return RetracedMethodReferenceImpl.create(
+          methodReference,
+          OptionalUtils.map(
+              originalPosition,
+              () -> {
+                int originalLineNumber = mappedRange.getFirstPositionOfOriginalRange(0);
+                return originalLineNumber > 0
+                    ? OptionalInt.of(originalLineNumber)
+                    : OptionalInt.empty();
+              }));
     }
-    if (!obfuscatedPosition.isPresent()
+    if (obfuscatedPosition.isEmpty()
         || mappedRange.minifiedRange == null
         || !mappedRange.minifiedRange.contains(obfuscatedPosition.getAsInt())) {
       return RetracedMethodReferenceImpl.create(methodReference, originalPosition);
     }
     return RetracedMethodReferenceImpl.create(
         methodReference,
-        OptionalUtils.orElseGet(
+        OptionalUtils.map(
             originalPosition,
-            () -> mappedRange.getOriginalLineNumber(obfuscatedPosition.getAsInt())));
+            () ->
+                OptionalInt.of(mappedRange.getOriginalLineNumber(obfuscatedPosition.getAsInt()))));
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java
index 761a124..b88958e 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java
@@ -115,6 +115,9 @@
     }
     try {
       String lineNumberString = getEntryInLine(lineNumber);
+      if (lineNumberString.startsWith(":")) {
+        lineNumberString = lineNumberString.substring(1);
+      }
       if (lineNumberString.isEmpty()) {
         return -1;
       }
@@ -238,14 +241,15 @@
               startIndex,
               endIndex,
               (retraced, original, verbose) -> {
-                boolean printLineNumber =
-                    retraced.hasLineNumber()
-                        && ((original.hasLineNumber() && original.getLineNumber() > -1)
-                            || !retraced.isAmbiguous()
-                            || verbose);
-                return printLineNumber
-                    ? ((insertSeparatorForRetraced ? ":" : "") + retraced.getLineNumber())
-                    : original.lineNumberAsString();
+                if (retraced.hasLineNumber()
+                    && ((original.hasLineNumber() && original.getLineNumber() > -1)
+                        || !retraced.isAmbiguous()
+                        || verbose)) {
+                  return retraced.getLineNumber() <= 0
+                      ? ""
+                      : ((insertSeparatorForRetraced ? ":" : "") + retraced.getLineNumber());
+                }
+                return original.lineNumberAsString();
               });
       orderedIndices.add(lineNumber);
       return this;
@@ -326,6 +330,10 @@
       }
       lastSeenStartIndex = newStartIndex;
     }
+
+    public String getLine() {
+      return line;
+    }
   }
 
   static class StringIndex {
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java
index bf9123e..b73c9bc 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java
@@ -297,7 +297,15 @@
         if (startOfGroup == NO_MATCH) {
           return false;
         }
-        builder.registerLineNumber(startOfGroup, matcher.end(captureGroup), false);
+        boolean insertSeparatorForRetraced = false;
+        // We need to include ':' in the group since we may want to rewrite '(SourceFile:0)` into
+        // (SourceFile) and not (SourceFile:)
+        if (startOfGroup > 0 && builder.getLine().charAt(startOfGroup + -1) == ':') {
+          startOfGroup = startOfGroup - 1;
+          insertSeparatorForRetraced = true;
+        }
+        int end = matcher.end(captureGroup);
+        builder.registerLineNumber(startOfGroup, end, insertSeparatorForRetraced);
         return true;
       };
     }
@@ -321,9 +329,10 @@
         int sourceFileEnd = startOfGroup + endOfSourceFileInGroup;
         builder.registerSourceFile(startOfGroup, sourceFileEnd);
         int endOfMatch = matcher.end(captureGroup);
-        int lineNumberStart = sourceFileEnd + 1;
-        builder.registerLineNumber(
-            Integer.min(lineNumberStart, endOfMatch), endOfMatch, lineNumberStart > endOfMatch);
+        // We need to include ':' in the group since we may want to rewrite '(SourceFile:0)` into
+        // (SourceFile) and not (SourceFile:). We fix this by setting the start of the linenumber
+        // group to the end of the SourceFile group and then force inserting ':'.
+        builder.registerLineNumber(Integer.min(sourceFileEnd, endOfMatch), endOfMatch, true);
         return true;
       };
     }
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
index f6959c6..9321484 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -129,10 +129,12 @@
           if (!options.isKeepRuntimeVisibleParameterAnnotationsEnabled()) {
             return false;
           }
-        } else {
-          if (!options.isKeepRuntimeVisibleAnnotationsEnabled()) {
-            return false;
-          }
+        } else if (!annotation.isTypeAnnotation()
+            && !options.isKeepRuntimeVisibleAnnotationsEnabled()) {
+          return false;
+        } else if (annotation.isTypeAnnotation()
+            && !options.isKeepRuntimeVisibleTypeAnnotationsEnabled()) {
+          return false;
         }
         return isAnnotationTypeLive;
 
@@ -147,13 +149,14 @@
           if (!options.isKeepRuntimeInvisibleParameterAnnotationsEnabled()) {
             return false;
           }
-        } else {
-          if (!options.isKeepRuntimeInvisibleAnnotationsEnabled()) {
-            return false;
-          }
+        } else if (!annotation.isTypeAnnotation()
+            && !options.isKeepRuntimeInvisibleAnnotationsEnabled()) {
+          return false;
+        } else if (annotation.isTypeAnnotation()
+            && !options.isKeepRuntimeInvisibleTypeAnnotationsEnabled()) {
+          return false;
         }
         return isAnnotationTypeLive;
-
       default:
         throw new Unreachable("Unexpected annotation visibility.");
     }
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 811ddc9..bd42c6a 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -9,7 +9,6 @@
 import static com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult.isOverriding;
 
 import com.android.tools.r8.cf.CfVersion;
-import com.android.tools.r8.experimental.startup.StartupOrder;
 import com.android.tools.r8.features.ClassToFeatureSplitMap;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
@@ -202,7 +201,6 @@
       ClassToFeatureSplitMap classToFeatureSplitMap,
       MainDexInfo mainDexInfo,
       MissingClasses missingClasses,
-      StartupOrder startupOrder,
       Set<DexType> deadProtoTypes,
       Set<DexType> liveTypes,
       Set<DexMethod> targetedMethods,
@@ -234,12 +232,7 @@
       Set<DexType> lockCandidates,
       Map<DexType, Visibility> initClassReferences,
       Set<DexMethod> recordFieldValuesReferences) {
-    super(
-        committedItems,
-        classToFeatureSplitMap,
-        mainDexInfo,
-        missingClasses,
-        startupOrder);
+    super(committedItems, classToFeatureSplitMap, mainDexInfo, missingClasses);
     this.deadProtoTypes = deadProtoTypes;
     this.liveTypes = liveTypes;
     this.targetedMethods = targetedMethods;
@@ -280,7 +273,6 @@
         previous.getClassToFeatureSplitMap(),
         previous.getMainDexInfo(),
         previous.getMissingClasses(),
-        previous.getStartupOrder(),
         previous.deadProtoTypes,
         CollectionUtils.addAll(previous.liveTypes, committedItems.getCommittedProgramTypes()),
         previous.targetedMethods,
@@ -324,7 +316,6 @@
         previous.getClassToFeatureSplitMap().withoutPrunedItems(prunedItems),
         previous.getMainDexInfo().withoutPrunedItems(prunedItems),
         previous.getMissingClasses(),
-        previous.getStartupOrder().withoutPrunedItems(prunedItems, previous.getSyntheticItems()),
         previous.deadProtoTypes,
         pruneClasses(previous.liveTypes, prunedItems, executorService, futures),
         pruneMethods(previous.targetedMethods, prunedItems, executorService, futures),
@@ -489,7 +480,6 @@
         getClassToFeatureSplitMap(),
         mainDexInfo,
         getMissingClasses(),
-        getStartupOrder(),
         deadProtoTypes,
         liveTypes,
         targetedMethods,
@@ -566,8 +556,7 @@
         previous.getSyntheticItems().commit(previous.app()),
         previous.getClassToFeatureSplitMap(),
         previous.getMainDexInfo(),
-        previous.getMissingClasses(),
-        previous.getStartupOrder());
+        previous.getMissingClasses());
     this.deadProtoTypes = previous.deadProtoTypes;
     this.liveTypes = previous.liveTypes;
     this.targetedMethods = previous.targetedMethods;
@@ -767,9 +756,10 @@
    * @param callSite Call site to resolve.
    * @return Methods implemented by the lambda expression that created the {@code callSite}.
    */
-  public Set<DexEncodedMethod> lookupLambdaImplementedMethods(DexCallSite callSite) {
+  public Set<DexEncodedMethod> lookupLambdaImplementedMethods(
+      DexCallSite callSite, AppView<AppInfoWithLiveness> appView) {
     assert checkIfObsolete();
-    List<DexType> callSiteInterfaces = LambdaDescriptor.getInterfaces(callSite, this);
+    List<DexType> callSiteInterfaces = LambdaDescriptor.getInterfaces(callSite, appView);
     if (callSiteInterfaces == null || callSiteInterfaces.isEmpty()) {
       return Collections.emptySet();
     }
@@ -1159,7 +1149,6 @@
         getClassToFeatureSplitMap().rewrittenWithLens(lens),
         getMainDexInfo().rewrittenWithLens(getSyntheticItems(), lens),
         getMissingClasses(),
-        getStartupOrder().rewrittenWithLens(lens),
         deadProtoTypes,
         lens.rewriteReferences(liveTypes),
         lens.rewriteReferences(targetedMethods),
@@ -1229,7 +1218,7 @@
   }
 
   public DexEncodedMethod lookupSingleTarget(
-      AppView<? extends AppInfoWithClassHierarchy> appView,
+      AppView<AppInfoWithLiveness> appView,
       InvokeType type,
       DexMethod target,
       ProgramMethod context,
@@ -1245,18 +1234,18 @@
       case INTERFACE:
         return lookupSingleVirtualTarget(appView, target, context, true, modeledPredicate);
       case DIRECT:
-        return lookupDirectTarget(target, context);
+        return lookupDirectTarget(target, context, appView);
       case STATIC:
-        return lookupStaticTarget(target, context);
+        return lookupStaticTarget(target, context, appView);
       case SUPER:
-        return toMethodDefinitionOrNull(lookupSuperTarget(target, context));
+        return toMethodDefinitionOrNull(lookupSuperTarget(target, context, appView));
       default:
         return null;
     }
   }
 
   public ProgramMethod lookupSingleProgramTarget(
-      AppView<? extends AppInfoWithClassHierarchy> appView,
+      AppView<AppInfoWithLiveness> appView,
       InvokeType type,
       DexMethod target,
       ProgramMethod context,
@@ -1267,7 +1256,7 @@
 
   /** For mapping invoke virtual instruction to single target method. */
   public DexEncodedMethod lookupSingleVirtualTarget(
-      AppView<? extends AppInfoWithClassHierarchy> appView,
+      AppView<AppInfoWithLiveness> appView,
       DexMethod method,
       ProgramMethod context,
       boolean isInterface) {
@@ -1277,7 +1266,7 @@
 
   /** For mapping invoke virtual instruction to single target method. */
   public DexEncodedMethod lookupSingleVirtualTarget(
-      AppView<? extends AppInfoWithClassHierarchy> appView,
+      AppView<AppInfoWithLiveness> appView,
       DexMethod method,
       ProgramMethod context,
       boolean isInterface,
@@ -1288,7 +1277,7 @@
   }
 
   public DexEncodedMethod lookupSingleVirtualTarget(
-      AppView<? extends AppInfoWithClassHierarchy> appView,
+      AppView<AppInfoWithLiveness> appView,
       DexMethod method,
       ProgramMethod context,
       boolean isInterface,
@@ -1325,7 +1314,7 @@
     SingleResolutionResult<?> resolution =
         resolveMethodOnLegacy(initialResolutionHolder, method).asSingleResolution();
     if (resolution == null
-        || resolution.isAccessibleForVirtualDispatchFrom(context.getHolder(), this).isFalse()) {
+        || resolution.isAccessibleForVirtualDispatchFrom(context.getHolder(), appView).isFalse()) {
       return null;
     }
     // If the method is modeled, return the resolution.
@@ -1378,7 +1367,10 @@
     LookupResultSuccess lookupResult =
         resolution
             .lookupVirtualDispatchTargets(
-                context.getHolder(), this, refinedReceiverClass.asProgramClass(), refinedLowerBound)
+                context.getHolder(),
+                appView,
+                refinedReceiverClass.asProgramClass(),
+                refinedLowerBound)
             .asLookupResultSuccess();
     if (lookupResult != null && !lookupResult.isIncomplete()) {
       LookupTarget singleTarget = lookupResult.getSingleLookupTarget();
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 a30bcf4..f45bf9b 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -122,7 +122,7 @@
 import com.android.tools.r8.naming.identifiernamestring.IdentifierNameStringLookupResult;
 import com.android.tools.r8.naming.identifiernamestring.IdentifierNameStringTypeLookupResult;
 import com.android.tools.r8.position.Position;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ProfileCollectionAdditions;
 import com.android.tools.r8.shaking.AnnotationMatchResult.MatchedAnnotation;
 import com.android.tools.r8.shaking.DelayedRootSetActionItem.InterfaceMethodSyntheticBridgeAction;
 import com.android.tools.r8.shaking.EnqueuerEvent.ClassEnqueuerEvent;
@@ -465,11 +465,11 @@
 
   private final Thread mainThreadForTesting = Thread.currentThread();
 
-  private final ArtProfileCollectionAdditions artProfileCollectionAdditions;
+  private final ProfileCollectionAdditions profileCollectionAdditions;
 
   Enqueuer(
       AppView<? extends AppInfoWithClassHierarchy> appView,
-      ArtProfileCollectionAdditions artProfileCollectionAdditions,
+      ProfileCollectionAdditions profileCollectionAdditions,
       ExecutorService executorService,
       SubtypingInfo subtypingInfo,
       GraphConsumer keptGraphConsumer,
@@ -478,7 +478,7 @@
     InternalOptions options = appView.options();
     this.appInfo = appView.appInfo();
     this.appView = appView.withClassHierarchy();
-    this.artProfileCollectionAdditions = artProfileCollectionAdditions;
+    this.profileCollectionAdditions = profileCollectionAdditions;
     this.deferredTracing = EnqueuerDeferredTracing.create(appView, this, mode);
     this.executorService = executorService;
     this.subtypingInfo = subtypingInfo;
@@ -528,8 +528,8 @@
     return appView.appInfo();
   }
 
-  public ArtProfileCollectionAdditions getArtProfileCollectionAdditions() {
-    return artProfileCollectionAdditions;
+  public ProfileCollectionAdditions getProfileCollectionAdditions() {
+    return profileCollectionAdditions;
   }
 
   public Mode getMode() {
@@ -1155,7 +1155,7 @@
       }
     }
 
-    LambdaDescriptor descriptor = LambdaDescriptor.tryInfer(callSite, appInfo(), context);
+    LambdaDescriptor descriptor = LambdaDescriptor.tryInfer(callSite, appView, appInfo(), context);
     if (descriptor == null) {
       for (DexValue bootstrapArgument : callSite.getBootstrapArgs()) {
         if (bootstrapArgument.isDexValueMethodHandle()) {
@@ -2877,7 +2877,7 @@
                               LookupResult lookupResult =
                                   singleResolution.lookupVirtualDispatchTargets(
                                       contextHolder,
-                                      appInfo,
+                                      appView,
                                       (type, subTypeConsumer, lambdaConsumer) -> {
                                         assert appInfo.isSubtype(currentClass.type, type);
                                         instantiation.apply(subTypeConsumer, lambdaConsumer);
@@ -3364,7 +3364,7 @@
               DexProgramClass resolvedHolder = resolution.getResolvedHolder().asProgramClass();
               DexEncodedMethod resolvedMethod = resolution.getResolvedMethod();
               markMethodAsTargeted(new ProgramMethod(resolvedHolder, resolvedMethod), reason);
-              if (resolution.isAccessibleForVirtualDispatchFrom(contextHolder, appInfo).isFalse()) {
+              if (resolution.isAccessibleForVirtualDispatchFrom(contextHolder, appView).isFalse()) {
                 // Not accessible from this context, so this call will cause a runtime exception.
                 return;
               }
@@ -3378,7 +3378,7 @@
               resolution
                   .lookupVirtualDispatchTargets(
                       contextHolder,
-                      appInfo,
+                      appView,
                       (type, subTypeConsumer, lambdaConsumer) ->
                           objectAllocationInfoCollection.forEachInstantiatedSubType(
                               type, subTypeConsumer, lambdaConsumer, appInfo),
@@ -3500,7 +3500,7 @@
               // If invoke target is invalid (inaccessible or not an instance-method) record it and
               // stop.
               DexClassAndMethod target =
-                  resolution.lookupInvokeSuperTarget(from.getHolder(), appInfo);
+                  resolution.lookupInvokeSuperTarget(from.getHolder(), appView);
               if (target == null) {
                 failedMethodResolutionTargets.add(resolution.getResolvedMethod().getReference());
                 analyses.forEach(
@@ -3628,7 +3628,7 @@
     }
     timing.begin("Create result");
     EnqueuerResult result = createEnqueuerResult(appInfo, timing);
-    artProfileCollectionAdditions.commit(appView);
+    profileCollectionAdditions.commit(appView);
     timing.end();
     return result;
   }
@@ -4024,7 +4024,7 @@
     CfInstructionDesugaringEventConsumer eventConsumer =
         CfInstructionDesugaringEventConsumer.createForR8(
             appView,
-            artProfileCollectionAdditions,
+            profileCollectionAdditions,
             lambdaCallback,
             this::recordConstantDynamicSynthesizingContext,
             this::recordTwrCloseResourceMethodSynthesizingContext,
@@ -4100,7 +4100,7 @@
       DexProgramClass holder = bridge.getHolder();
       DexEncodedMethod method = bridge.getDefinition();
       holder.addVirtualMethod(method);
-      artProfileCollectionAdditions.addMethodIfContextIsInProfile(bridge, action.getSingleTarget());
+      profileCollectionAdditions.addMethodIfContextIsInProfile(bridge, action.getSingleTarget());
     }
     syntheticInterfaceMethodBridges.clear();
   }
@@ -4216,7 +4216,6 @@
                 ? missingClassesBuilder.reportMissingClasses(
                     appView, lambdaSynthesizingContextOracle)
                 : missingClassesBuilder.assertNoMissingClasses(appView),
-            appInfo.getStartupOrder(),
             deadProtoTypes,
             SetUtils.mapIdentityHashSet(liveTypes.getItems(), DexProgramClass::getType),
             Enqueuer.toDescriptorSet(targetedMethods.getItems()),
@@ -4490,7 +4489,7 @@
         CfPostProcessingDesugaringEventConsumer.createForR8(
             appView,
             syntheticAdditions,
-            artProfileCollectionAdditions,
+            profileCollectionAdditions,
             desugaring,
             (context, missing) ->
                 missingClassesBuilder.addNewMissingClassWithDesugarDiagnostic(
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java
index e1d0124..e09b9be 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java
@@ -9,7 +9,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.SubtypingInfo;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ProfileCollectionAdditions;
 import com.android.tools.r8.shaking.Enqueuer.Mode;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
@@ -18,12 +18,12 @@
 
   public static Enqueuer createForInitialTreeShaking(
       AppView<? extends AppInfoWithClassHierarchy> appView,
-      ArtProfileCollectionAdditions artProfileCollectionAdditions,
+      ProfileCollectionAdditions profileCollectionAdditions,
       ExecutorService executorService,
       SubtypingInfo subtypingInfo) {
     return new Enqueuer(
         appView,
-        artProfileCollectionAdditions,
+        profileCollectionAdditions,
         executorService,
         subtypingInfo,
         null,
@@ -36,12 +36,12 @@
       SubtypingInfo subtypingInfo,
       GraphConsumer keptGraphConsumer,
       Set<DexType> initialPrunedTypes) {
-    ArtProfileCollectionAdditions artProfileCollectionAdditions =
-        ArtProfileCollectionAdditions.create(appView);
+    ProfileCollectionAdditions profileCollectionAdditions =
+        ProfileCollectionAdditions.create(appView);
     Enqueuer enqueuer =
         new Enqueuer(
             appView,
-            artProfileCollectionAdditions,
+            profileCollectionAdditions,
             executorService,
             subtypingInfo,
             keptGraphConsumer,
@@ -56,11 +56,11 @@
       AppView<? extends AppInfoWithClassHierarchy> appView,
       ExecutorService executorService,
       SubtypingInfo subtypingInfo) {
-    ArtProfileCollectionAdditions artProfileCollectionAdditions =
-        ArtProfileCollectionAdditions.create(appView);
+    ProfileCollectionAdditions profileCollectionAdditions =
+        ProfileCollectionAdditions.create(appView);
     return new Enqueuer(
         appView,
-        artProfileCollectionAdditions,
+        profileCollectionAdditions,
         executorService,
         subtypingInfo,
         null,
@@ -72,11 +72,11 @@
       ExecutorService executorService,
       SubtypingInfo subtypingInfo,
       GraphConsumer keptGraphConsumer) {
-    ArtProfileCollectionAdditions artProfileCollectionAdditions =
-        ArtProfileCollectionAdditions.create(appView);
+    ProfileCollectionAdditions profileCollectionAdditions =
+        ProfileCollectionAdditions.create(appView);
     return new Enqueuer(
         appView,
-        artProfileCollectionAdditions,
+        profileCollectionAdditions,
         executorService,
         subtypingInfo,
         keptGraphConsumer,
@@ -88,11 +88,11 @@
       ExecutorService executorService,
       SubtypingInfo subtypingInfo,
       GraphConsumer keptGraphConsumer) {
-    ArtProfileCollectionAdditions artProfileCollectionAdditions =
-        ArtProfileCollectionAdditions.create(appView);
+    ProfileCollectionAdditions profileCollectionAdditions =
+        ProfileCollectionAdditions.create(appView);
     return new Enqueuer(
         appView,
-        artProfileCollectionAdditions,
+        profileCollectionAdditions,
         executorService,
         subtypingInfo,
         keptGraphConsumer,
@@ -104,11 +104,11 @@
       ExecutorService executorService,
       SubtypingInfo subtypingInfo,
       GraphConsumer keptGraphConsumer) {
-    ArtProfileCollectionAdditions artProfileCollectionAdditions =
-        ArtProfileCollectionAdditions.create(appView);
+    ProfileCollectionAdditions profileCollectionAdditions =
+        ProfileCollectionAdditions.create(appView);
     return new Enqueuer(
         appView,
-        artProfileCollectionAdditions,
+        profileCollectionAdditions,
         executorService,
         subtypingInfo,
         keptGraphConsumer,
diff --git a/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java b/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
index d10b35c..7ff0803 100644
--- a/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
@@ -35,4 +35,8 @@
   boolean isKeepRuntimeVisibleAnnotationsEnabled();
 
   boolean isKeepRuntimeVisibleParameterAnnotationsEnabled();
+
+  boolean isKeepRuntimeVisibleTypeAnnotationsEnabled();
+
+  boolean isKeepRuntimeInvisibleTypeAnnotationsEnabled();
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java b/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java
index 8baf037..89c25ae 100644
--- a/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java
+++ b/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java
@@ -43,6 +43,10 @@
     this.consumer = consumer;
   }
 
+  private AppView<? extends AppInfoWithClassHierarchy> appView() {
+    return appView;
+  }
+
   public void run(Set<DexType> roots) {
     SyntheticItems syntheticItems = appView.getSyntheticItems();
     DexItemFactory factory = appView.dexItemFactory();
@@ -113,7 +117,7 @@
   private class DirectReferencesCollector extends UseRegistry<ProgramMethod> {
 
     private DirectReferencesCollector(ProgramMethod context) {
-      super(appView, context);
+      super(appView(), context);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilderEventConsumer.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilderEventConsumer.java
index 9378b76..4102d80 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilderEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilderEventConsumer.java
@@ -6,15 +6,13 @@
 
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.itf.InterfaceMethodDesugaringBaseEventConsumer;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingRootSetBuilderEventConsumer;
+import com.android.tools.r8.profile.art.rewriting.ProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ProfileRewritingRootSetBuilderEventConsumer;
 
 public interface RootSetBuilderEventConsumer extends InterfaceMethodDesugaringBaseEventConsumer {
 
-  static RootSetBuilderEventConsumer create(
-      ArtProfileCollectionAdditions artProfileCollectionAdditions) {
-    return ArtProfileRewritingRootSetBuilderEventConsumer.attach(
-        artProfileCollectionAdditions, empty());
+  static RootSetBuilderEventConsumer create(ProfileCollectionAdditions profileCollectionAdditions) {
+    return ProfileRewritingRootSetBuilderEventConsumer.attach(profileCollectionAdditions, empty());
   }
 
   static EmptyRootSetBuilderEventConsumer empty() {
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index 352facd..bbb8726 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -52,7 +52,7 @@
 import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
 import com.android.tools.r8.ir.optimize.membervaluepropagation.assume.AssumeInfo;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ProfileCollectionAdditions;
 import com.android.tools.r8.repackaging.RepackagingUtils;
 import com.android.tools.r8.shaking.AnnotationMatchResult.AnnotationsIgnoredMatchResult;
 import com.android.tools.r8.shaking.AnnotationMatchResult.ConcreteAnnotationMatchResult;
@@ -176,7 +176,7 @@
         SubtypingInfo subtypingInfo) {
       this(
           appView,
-          RootSetBuilderEventConsumer.create(enqueuer.getArtProfileCollectionAdditions()),
+          RootSetBuilderEventConsumer.create(enqueuer.getProfileCollectionAdditions()),
           subtypingInfo,
           null);
     }
@@ -2151,12 +2151,12 @@
 
     public static RootSetBuilder builder(
         AppView<? extends AppInfoWithClassHierarchy> appView,
-        ArtProfileCollectionAdditions artProfileCollectionAdditions,
+        ProfileCollectionAdditions profileCollectionAdditions,
         SubtypingInfo subtypingInfo,
         Iterable<? extends ProguardConfigurationRule> rules) {
       return new RootSetBuilder(
           appView,
-          RootSetBuilderEventConsumer.create(artProfileCollectionAdditions),
+          RootSetBuilderEventConsumer.create(profileCollectionAdditions),
           subtypingInfo,
           rules);
     }
@@ -2172,7 +2172,7 @@
         SubtypingInfo subtypingInfo) {
       super(
           appView,
-          RootSetBuilderEventConsumer.create(enqueuer.getArtProfileCollectionAdditions()),
+          RootSetBuilderEventConsumer.create(enqueuer.getProfileCollectionAdditions()),
           subtypingInfo,
           null);
       this.enqueuer = enqueuer;
@@ -2220,12 +2220,12 @@
 
     private MainDexRootSetBuilder(
         AppView<? extends AppInfoWithClassHierarchy> appView,
-        ArtProfileCollectionAdditions artProfileCollectionAdditions,
+        ProfileCollectionAdditions profileCollectionAdditions,
         SubtypingInfo subtypingInfo,
         Iterable<? extends ProguardConfigurationRule> rules) {
       super(
           appView,
-          RootSetBuilderEventConsumer.create(artProfileCollectionAdditions),
+          RootSetBuilderEventConsumer.create(profileCollectionAdditions),
           subtypingInfo,
           rules);
     }
@@ -2279,11 +2279,10 @@
 
     public static MainDexRootSetBuilder builder(
         AppView<? extends AppInfoWithClassHierarchy> appView,
-        ArtProfileCollectionAdditions artProfileCollectionAdditions,
+        ProfileCollectionAdditions profileCollectionAdditions,
         SubtypingInfo subtypingInfo,
         Iterable<? extends ProguardConfigurationRule> rules) {
-      return new MainDexRootSetBuilder(
-          appView, artProfileCollectionAdditions, subtypingInfo, rules);
+      return new MainDexRootSetBuilder(appView, profileCollectionAdditions, subtypingInfo, rules);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 1049259..c0e541b 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -73,7 +73,7 @@
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
 import com.android.tools.r8.ir.synthetic.AbstractSynthesizedCode;
 import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ProfileCollectionAdditions;
 import com.android.tools.r8.utils.Box;
 import com.android.tools.r8.utils.CollectionUtils;
 import com.android.tools.r8.utils.FieldSignatureEquivalence;
@@ -627,16 +627,16 @@
     assert verifyGraphLens(lens);
 
     // Include bridges in art profiles.
-    ArtProfileCollectionAdditions artProfileCollectionAdditions =
-        ArtProfileCollectionAdditions.create(appView);
-    if (!artProfileCollectionAdditions.isNop()) {
+    ProfileCollectionAdditions profileCollectionAdditions =
+        ProfileCollectionAdditions.create(appView);
+    if (!profileCollectionAdditions.isNop()) {
       for (SynthesizedBridgeCode synthesizedBridge : synthesizedBridges) {
-        artProfileCollectionAdditions.applyIfContextIsInProfile(
+        profileCollectionAdditions.applyIfContextIsInProfile(
             synthesizedBridge.originalMethod,
             additionsBuilder -> additionsBuilder.addRule(synthesizedBridge.method));
       }
     }
-    artProfileCollectionAdditions.commit(appView);
+    profileCollectionAdditions.commit(appView);
 
     // Rewrite collections using the lens.
     appView.rewriteWithLens(lens);
@@ -761,7 +761,7 @@
         LookupResultSuccess lookupResult =
             appInfo
                 .resolveMethodOnInterfaceLegacy(method.getHolderType(), method.getReference())
-                .lookupVirtualDispatchTargets(target, appInfo)
+                .lookupVirtualDispatchTargets(target, appView)
                 .asLookupResultSuccess();
         assert lookupResult != null;
         if (lookupResult == null) {
@@ -1356,7 +1356,7 @@
           // Only rewrite the invoke-super call if it does not lead to a NoSuchMethodError.
           boolean resolutionSucceeds =
               holder.lookupVirtualMethod(signatureInHolder) != null
-                  || appInfo.lookupSuperTarget(signatureInHolder, holder) != null;
+                  || appInfo.lookupSuperTarget(signatureInHolder, holder, appView) != null;
           if (resolutionSucceeds) {
             deferredRenamings.mapVirtualMethodToDirectInType(
                 signatureInHolder,
@@ -1381,7 +1381,7 @@
             // its super classes declared the method.
             boolean resolutionSucceededBeforeMerge =
                 lensBuilder.hasMappingForSignatureInContext(holder, signatureInType)
-                    || appInfo.lookupSuperTarget(signatureInHolder, holder) != null;
+                    || appInfo.lookupSuperTarget(signatureInHolder, holder, appView) != null;
             if (resolutionSucceededBeforeMerge) {
               deferredRenamings.mapVirtualMethodToDirectInType(
                   signatureInType,
diff --git a/src/main/java/com/android/tools/r8/startup/StartupProfileBuilder.java b/src/main/java/com/android/tools/r8/startup/StartupProfileBuilder.java
index 47916c1..3ee53c7 100644
--- a/src/main/java/com/android/tools/r8/startup/StartupProfileBuilder.java
+++ b/src/main/java/com/android/tools/r8/startup/StartupProfileBuilder.java
@@ -21,29 +21,6 @@
       Consumer<StartupMethodBuilder> startupMethodBuilderConsumer);
 
   /**
-   * API for adding information about a synthetic startup method to the compiler.
-   *
-   * <p>When shrinking an app using R8, the names of synthetic classes may differ from the synthetic
-   * names that arise from dexing the app using D8. Therefore, synthetic classes and methods should
-   * not be added to the startup profile using {@link #addStartupClass(Consumer)} and {@link
-   * #addStartupMethod(Consumer)}.
-   *
-   * <p>Instead, synthetic items should be added to the startup profile using this method, which
-   * takes the name of the synthetic context instead of the synthetic name. The addition of the
-   * synthetic context will be interpreted as the presence of any method on any synthetic class that
-   * has been synthesized from the synthetic context.
-   *
-   * <p>Example: Instead of adding "Lcom/example/MainActivity$ExternalSynthetic0;->m()V" as a
-   * (non-synthetic) startup method, a synthetic startup method should be added with synthetic
-   * context "Lcom/example/MainActivity;".
-   *
-   * <p>NOTE: This should only be used when supplying a startup profile that is generated from an
-   * unobfuscated build of the app to R8.
-   */
-  StartupProfileBuilder addSyntheticStartupMethod(
-      Consumer<SyntheticStartupMethodBuilder> syntheticStartupMethodBuilderConsumer);
-
-  /**
    * Adds the rules from the given human-readable ART profile to the startup profile and then closes
    * the stream.
    *
diff --git a/src/main/java/com/android/tools/r8/startup/SyntheticStartupMethodBuilder.java b/src/main/java/com/android/tools/r8/startup/SyntheticStartupMethodBuilder.java
deleted file mode 100644
index 281bac7..0000000
--- a/src/main/java/com/android/tools/r8/startup/SyntheticStartupMethodBuilder.java
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (c) 2022, 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.startup;
-
-import com.android.tools.r8.Keep;
-import com.android.tools.r8.references.ClassReference;
-
-/** Interface for providing information about a synthetic startup method to the compiler. */
-@Keep
-public interface SyntheticStartupMethodBuilder {
-
-  SyntheticStartupMethodBuilder setSyntheticContextReference(ClassReference classReference);
-}
diff --git a/src/main/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnostic.java b/src/main/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnostic.java
index 6fa28ea..0c33a74 100644
--- a/src/main/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnostic.java
+++ b/src/main/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnostic.java
@@ -6,9 +6,8 @@
 
 import com.android.tools.r8.Diagnostic;
 import com.android.tools.r8.Keep;
-import com.android.tools.r8.experimental.startup.profile.StartupClass;
-import com.android.tools.r8.experimental.startup.profile.StartupMethod;
-import com.android.tools.r8.experimental.startup.profile.SyntheticStartupMethod;
+import com.android.tools.r8.experimental.startup.profile.StartupProfileClassRule;
+import com.android.tools.r8.experimental.startup.profile.StartupProfileMethodRule;
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexString;
@@ -86,7 +85,7 @@
       return !missingStartupItems.isEmpty();
     }
 
-    public boolean registerStartupClass(StartupClass startupClass) {
+    public boolean registerStartupClass(StartupProfileClassRule startupClass) {
       if (definitions != null && !definitions.hasDefinitionFor(startupClass.getReference())) {
         addMissingStartupItem(startupClass.getReference());
         return true;
@@ -94,7 +93,7 @@
       return false;
     }
 
-    public boolean registerStartupMethod(StartupMethod startupMethod) {
+    public boolean registerStartupMethod(StartupProfileMethodRule startupMethod) {
       if (definitions != null && !definitions.hasDefinitionFor(startupMethod.getReference())) {
         addMissingStartupItem(startupMethod.getReference());
         return true;
@@ -102,15 +101,6 @@
       return false;
     }
 
-    public boolean registerSyntheticStartupMethod(SyntheticStartupMethod syntheticStartupMethod) {
-      if (definitions != null
-          && !definitions.hasDefinitionFor(syntheticStartupMethod.getSyntheticContextType())) {
-        addMissingStartupItem(syntheticStartupMethod.getSyntheticContextType());
-        return true;
-      }
-      return false;
-    }
-
     private void addMissingStartupItem(DexReference reference) {
       DexString jDollarDescriptorPrefix = definitions.dexItemFactory().jDollarDescriptorPrefix;
       if (!reference.getContextType().getDescriptor().startsWith(jDollarDescriptorPrefix)) {
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index 3bbb9ae..066c144 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -11,7 +11,7 @@
 import com.android.tools.r8.contexts.CompilationContext.UniqueContext;
 import com.android.tools.r8.errors.MissingGlobalSyntheticsConsumerDiagnostic;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.experimental.startup.StartupOrder;
+import com.android.tools.r8.experimental.startup.StartupProfile;
 import com.android.tools.r8.features.ClassToFeatureSplitMap;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -539,22 +539,6 @@
     return oracle.getSynthesizingContexts(clazz);
   }
 
-  public Map<DexType, List<DexProgramClass>> computeSyntheticContextsToSyntheticClasses(
-      AppView<?> appView) {
-    Map<DexType, List<DexProgramClass>> syntheticContextsToSyntheticClasses =
-        new IdentityHashMap<>();
-    for (DexProgramClass clazz : appView.appInfo().classes()) {
-      if (isSyntheticClass(clazz)) {
-        for (DexType synthesizingContextType : getSynthesizingContextTypes(clazz.getType())) {
-          syntheticContextsToSyntheticClasses
-              .computeIfAbsent(synthesizingContextType, ignoreKey -> new ArrayList<>())
-              .add(clazz);
-        }
-      }
-    }
-    return syntheticContextsToSyntheticClasses;
-  }
-
   public Collection<Origin> getSynthesizingOrigin(DexType type) {
     if (!isSynthetic(type)) {
       return Collections.emptyList();
@@ -626,13 +610,13 @@
     if (appView.hasClassHierarchy()) {
       AppInfoWithClassHierarchy appInfo = appView.appInfoWithClassHierarchy();
       return getSynthesizingContext(
-          context, appInfo.getClassToFeatureSplitMap(), options, appInfo.getStartupOrder());
+          context, appInfo.getClassToFeatureSplitMap(), options, appView.getStartupProfile());
     }
     return getSynthesizingContext(
         context,
         ClassToFeatureSplitMap.createEmptyClassToFeatureSplitMap(),
         options,
-        StartupOrder.empty());
+        StartupProfile.empty());
   }
 
   /** Used to find the synthesizing context for a new synthetic that is about to be created. */
@@ -640,7 +624,7 @@
       ProgramDefinition context,
       ClassToFeatureSplitMap featureSplits,
       InternalOptions options,
-      StartupOrder startupOrder) {
+      StartupProfile startupProfile) {
     DexType contextType = context.getContextType();
     SyntheticDefinition<?, ?, ?> existingDefinition = pending.definitions.get(contextType);
     if (existingDefinition != null) {
@@ -656,7 +640,8 @@
           .getContext();
     }
     // This context is not nested in an existing synthetic context so create a new "leaf" context.
-    FeatureSplit featureSplit = featureSplits.getFeatureSplit(context, options, startupOrder, this);
+    FeatureSplit featureSplit =
+        featureSplits.getFeatureSplit(context, options, startupProfile, this);
     return SynthesizingContext.fromNonSyntheticInputContext(context, featureSplit);
   }
 
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
index e1000f6..ad30e62 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
@@ -13,7 +13,6 @@
 import com.android.tools.r8.ResourceException;
 import com.android.tools.r8.Version;
 import com.android.tools.r8.dex.ApplicationReader;
-import com.android.tools.r8.experimental.startup.StartupOrder;
 import com.android.tools.r8.features.ClassToFeatureSplitMap;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
@@ -87,8 +86,7 @@
                 new ApplicationReader(builder.build(), options, Timing.empty()).read().toDirect(),
                 ClassToFeatureSplitMap.createEmptyClassToFeatureSplitMap(),
                 MainDexInfo.none(),
-                GlobalSyntheticsStrategy.forSingleOutputMode(),
-                StartupOrder.empty()));
+                GlobalSyntheticsStrategy.forSingleOutputMode()));
     modelLibraryMethodsWithCovariantReturnTypes(appView);
     Tracer tracer =
         new Tracer(
diff --git a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
index f182a0b..b430854 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
@@ -109,6 +109,10 @@
       this.targetPredicate = targetPredicate;
     }
 
+    AppView<? extends AppInfoWithClassHierarchy> appView() {
+      return appView;
+    }
+
     AppInfoWithClassHierarchy appInfo() {
       return appView.appInfo();
     }
@@ -228,7 +232,7 @@
               ? appInfo().resolveMethodOnInterface(method.getHolder(), method.getReference())
               : appInfo().resolveMethodOnClass(method.getHolder(), method.getReference());
       DexClassAndMethod superTarget =
-          methodResolutionResult.lookupInvokeSpecialTarget(method.getHolder(), appInfo());
+          methodResolutionResult.lookupInvokeSpecialTarget(method.getHolder(), appView);
       if (superTarget != null
           && !superTarget.isProgramMethod()
           && isTargetType(superTarget.getHolderType())) {
@@ -263,7 +267,7 @@
       private final DefinitionContext referencedFrom;
 
       public MethodUseCollector(ProgramMethod context) {
-        super(appView, context);
+        super(appView(), context);
         this.referencedFrom = DefinitionContextUtils.create(context);
       }
 
@@ -316,7 +320,7 @@
         handleRewrittenMethodResolution(
             method,
             appInfo().unsafeResolveMethodDueToDexFormat(rewrittenMethod),
-            result -> result.lookupInvokeSuperTarget(getContext().getHolder(), appInfo()));
+            result -> result.lookupInvokeSuperTarget(getContext().getHolder(), appView, appInfo()));
       }
 
       @Override
@@ -486,7 +490,8 @@
 
         // For lambdas that implement an interface, also keep the interface method by simulating an
         // invoke to it from the current context.
-        LambdaDescriptor descriptor = LambdaDescriptor.tryInfer(callSite, appInfo(), getContext());
+        LambdaDescriptor descriptor =
+            LambdaDescriptor.tryInfer(callSite, appView(), appInfo(), getContext());
         if (descriptor != null) {
           for (DexType interfaceType : descriptor.interfaces) {
             ClassResolutionResult classResolutionResult =
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index cf3047d..ee770f8 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -34,7 +34,7 @@
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.InternalCompilerError;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.experimental.startup.StartupOrder;
+import com.android.tools.r8.experimental.startup.StartupProfile;
 import com.android.tools.r8.experimental.startup.StartupProfileProviderUtils;
 import com.android.tools.r8.features.ClassToFeatureSplitMap;
 import com.android.tools.r8.features.FeatureSplitConfiguration;
@@ -679,7 +679,7 @@
                           SyntheticItems syntheticItems = null;
                           FeatureSplit featureSplit =
                               classToFeatureSplitMap.getFeatureSplit(
-                                  type, options, StartupOrder.empty(), syntheticItems);
+                                  type, options, StartupProfile.empty(), syntheticItems);
                           if (featureSplit != null && !featureSplit.isBase()) {
                             return featureSplitArchiveOutputStreams.get(featureSplit);
                           }
diff --git a/src/main/java/com/android/tools/r8/utils/Box.java b/src/main/java/com/android/tools/r8/utils/Box.java
index 1134718..00fde15 100644
--- a/src/main/java/com/android/tools/r8/utils/Box.java
+++ b/src/main/java/com/android/tools/r8/utils/Box.java
@@ -5,6 +5,8 @@
 package com.android.tools.r8.utils;
 
 import java.util.Comparator;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
 import java.util.function.Supplier;
 
 public class Box<T> extends BoxBase<T> {
@@ -16,6 +18,11 @@
   }
 
   @Override
+  public void accept(Consumer<? super T> consumer) {
+    super.accept(consumer);
+  }
+
+  @Override
   public void clear() {
     super.clear();
   }
@@ -35,6 +42,13 @@
     return super.getAndSet(newValue);
   }
 
+  public <E extends Exception> Box<T> rebuild(ThrowingFunction<T, T, E> fn) throws E {
+    if (isSet()) {
+      return new Box<>(fn.apply(get()));
+    }
+    return new Box<>();
+  }
+
   @Override
   public void set(T value) {
     super.set(value);
@@ -44,4 +58,9 @@
   public void setMin(T value, Comparator<T> comparator) {
     super.setMin(value, comparator);
   }
+
+  @Override
+  public boolean test(Predicate<T> predicate) {
+    return super.test(predicate);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/BoxBase.java b/src/main/java/com/android/tools/r8/utils/BoxBase.java
index 93f70da..c2b4492 100644
--- a/src/main/java/com/android/tools/r8/utils/BoxBase.java
+++ b/src/main/java/com/android/tools/r8/utils/BoxBase.java
@@ -6,7 +6,9 @@
 
 import java.util.Comparator;
 import java.util.Objects;
+import java.util.function.Consumer;
 import java.util.function.Function;
+import java.util.function.Predicate;
 import java.util.function.Supplier;
 
 public abstract class BoxBase<T> {
@@ -19,6 +21,12 @@
     set(initialValue);
   }
 
+  void accept(Consumer<? super T> consumer) {
+    if (isSet()) {
+      consumer.accept(get());
+    }
+  }
+
   void clear() {
     set(null);
   }
@@ -56,6 +64,10 @@
     }
   }
 
+  boolean test(Predicate<T> predicate) {
+    return isSet() && predicate.test(get());
+  }
+
   public boolean isSet() {
     return value != null;
   }
diff --git a/src/main/java/com/android/tools/r8/utils/CompileDumpUtils.java b/src/main/java/com/android/tools/r8/utils/CompileDumpUtils.java
index f0275f5..4356671 100644
--- a/src/main/java/com/android/tools/r8/utils/CompileDumpUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/CompileDumpUtils.java
@@ -43,26 +43,16 @@
           try (BufferedReader bufferedReader = Files.newBufferedReader(path)) {
             while (bufferedReader.ready()) {
               String rule = bufferedReader.readLine();
-              if (rule.charAt(0) == 'S') {
-                String classDescriptor = rule.substring(1);
-                assert DescriptorUtils.isClassDescriptor(classDescriptor);
-                startupProfileBuilder.addSyntheticStartupMethod(
-                    syntheticStartupMethodBuilder ->
-                        syntheticStartupMethodBuilder.setSyntheticContextReference(
-                            Reference.classFromDescriptor(classDescriptor)));
+              MethodReference methodReference = MethodReferenceUtils.parseSmaliString(rule);
+              if (methodReference != null) {
+                startupProfileBuilder.addStartupMethod(
+                    startupMethodBuilder ->
+                        startupMethodBuilder.setMethodReference(methodReference));
               } else {
-                MethodReference methodReference = MethodReferenceUtils.parseSmaliString(rule);
-                if (methodReference != null) {
-                  startupProfileBuilder.addStartupMethod(
-                      startupMethodBuilder ->
-                          startupMethodBuilder.setMethodReference(methodReference));
-                } else {
-                  assert DescriptorUtils.isClassDescriptor(rule);
-                  startupProfileBuilder.addStartupClass(
-                      startupClassBuilder ->
-                          startupClassBuilder.setClassReference(
-                              Reference.classFromDescriptor(rule)));
-                }
+                assert DescriptorUtils.isClassDescriptor(rule);
+                startupProfileBuilder.addStartupClass(
+                    startupClassBuilder ->
+                        startupClassBuilder.setClassReference(Reference.classFromDescriptor(rule)));
               }
             }
           }
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 0d7a71c..8275b55 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -785,6 +785,16 @@
     return proguardConfiguration.getKeepAttributes().runtimeVisibleParameterAnnotations;
   }
 
+  @Override
+  public boolean isKeepRuntimeVisibleTypeAnnotationsEnabled() {
+    return proguardConfiguration.getKeepAttributes().runtimeVisibleTypeAnnotations;
+  }
+
+  @Override
+  public boolean isKeepRuntimeInvisibleTypeAnnotationsEnabled() {
+    return proguardConfiguration.getKeepAttributes().runtimeInvisibleTypeAnnotations;
+  }
+
   /**
    * If any non-static class merging is enabled, information about types referred to by instanceOf
    * and check cast instructions needs to be collected.
diff --git a/src/main/java/com/android/tools/r8/utils/OptionalUtils.java b/src/main/java/com/android/tools/r8/utils/OptionalUtils.java
index 974bf0c..925ca07 100644
--- a/src/main/java/com/android/tools/r8/utils/OptionalUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/OptionalUtils.java
@@ -9,11 +9,8 @@
 
 public class OptionalUtils {
 
-  public static OptionalInt orElse(OptionalInt optional, int orElse) {
-    return optional.isPresent() ? optional : OptionalInt.of(orElse);
+  public static OptionalInt map(OptionalInt optional, Supplier<OptionalInt> orElse) {
+    return optional.isPresent() ? optional : orElse.get();
   }
 
-  public static OptionalInt orElseGet(OptionalInt optional, Supplier<Integer> orElse) {
-    return optional.isPresent() ? optional : OptionalInt.of(orElse.get());
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/TypeReferenceUtils.java b/src/main/java/com/android/tools/r8/utils/TypeReferenceUtils.java
index a5145f4..bb1bbc9 100644
--- a/src/main/java/com/android/tools/r8/utils/TypeReferenceUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/TypeReferenceUtils.java
@@ -37,6 +37,10 @@
     return COMPARATOR;
   }
 
+  public static TypeReference getVoidType() {
+    return null;
+  }
+
   public static DexProto toDexProto(
       List<TypeReference> formalTypes, TypeReference returnType, DexItemFactory dexItemFactory) {
     return toDexProto(
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index 487451f..f22ce21 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -21,12 +21,10 @@
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.dex.Marker.Tool;
-import com.android.tools.r8.experimental.startup.profile.StartupItem;
-import com.android.tools.r8.experimental.startup.profile.StartupMethod;
-import com.android.tools.r8.experimental.startup.profile.StartupProfile;
+import com.android.tools.r8.experimental.startup.StartupProfile;
+import com.android.tools.r8.experimental.startup.profile.StartupProfileRule;
 import com.android.tools.r8.origin.EmbeddedOrigin;
 import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.profile.art.ArtProfileBuilderUtils.SyntheticToSyntheticContextGeneralization;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.startup.StartupProfileProvider;
 import com.android.tools.r8.startup.diagnostic.MissingStartupProfileItemsDiagnostic;
@@ -807,24 +805,19 @@
     MissingStartupProfileItemsDiagnostic.Builder missingStartupProfileItemsDiagnosticBuilder =
         MissingStartupProfileItemsDiagnostic.Builder.nop();
     StartupProfileProvider startupProfileProvider = startupProfileProviders.get(0);
-    SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization =
-        SyntheticToSyntheticContextGeneralization.createForD8();
     StartupProfile.Builder startupProfileBuilder =
         StartupProfile.builder(
-            options,
-            missingStartupProfileItemsDiagnosticBuilder,
-            startupProfileProvider,
-            syntheticToSyntheticContextGeneralization);
+            options, missingStartupProfileItemsDiagnosticBuilder, startupProfileProvider);
     startupProfileProvider.getStartupProfile(startupProfileBuilder);
 
     // Verify we found the same rule.
     StartupProfile startupProfile = startupProfileBuilder.build();
-    Collection<StartupItem> startupItems = startupProfile.getStartupItems();
+    Collection<StartupProfileRule> startupItems = startupProfile.getRules();
     assertEquals(1, startupItems.size());
-    StartupItem startupItem = startupItems.iterator().next();
-    assertTrue(startupItem.isStartupMethod());
-    StartupMethod startupMethod = startupItem.asStartupMethod();
-    assertEquals(profileRule, startupMethod.getReference().toSmaliString());
+    StartupProfileRule startupItem = startupItems.iterator().next();
+    startupItem.accept(
+        startupClass -> fail(),
+        startupMethod -> assertEquals(profileRule, startupMethod.getReference().toSmaliString()));
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index f3f7d40..22f879d 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -93,6 +93,10 @@
               Version.V13_0_0,
               // TODO(120402963) Triage.
               ImmutableList.of("invokecustom-with-shrinking", "invokecustom2-with-shrinking"))
+          .put(
+              Version.V14_0_0,
+              // TODO(120402963) Triage.
+              ImmutableList.of("invokecustom-with-shrinking", "invokecustom2-with-shrinking"))
           .put(Version.DEFAULT, ImmutableList.of())
           .build();
 
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
index de13777..59e0d13 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -306,6 +306,11 @@
             ImmutableList.of(
                 // TODO(b/120402963): Triage.
                 "invokecustom", "invokecustom2"))
+        .put(
+            Version.V14_0_0,
+            ImmutableList.of(
+                // TODO(b/120402963): Triage.
+                "invokecustom", "invokecustom2"))
         .put(DexVm.Version.DEFAULT, ImmutableList.of());
     failsOn = builder.build();
   }
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index a2f3ebc..45322ff 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -26,7 +26,6 @@
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.dex.code.DexInstruction;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.experimental.startup.StartupOrder;
 import com.android.tools.r8.features.ClassToFeatureSplitMap;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -46,7 +45,7 @@
 import com.android.tools.r8.jasmin.JasminBuilder;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.PathOrigin;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ProfileCollectionAdditions;
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.FieldReference;
 import com.android.tools.r8.references.MethodReference;
@@ -777,8 +776,7 @@
         readApplicationForDexOutput(app, new InternalOptions()),
         ClassToFeatureSplitMap.createEmptyClassToFeatureSplitMap(),
         MainDexInfo.none(),
-        GlobalSyntheticsStrategy.forSingleOutputMode(),
-        StartupOrder.empty());
+        GlobalSyntheticsStrategy.forSingleOutputMode());
   }
 
   protected static AppView<AppInfoWithClassHierarchy> computeAppViewWithClassHierarchy(
@@ -847,20 +845,19 @@
         computeAppViewWithClassHierarchy(app, keepConfig, optionsConsumer);
     // Run the tree shaker to compute an instance of AppInfoWithLiveness.
     ExecutorService executor = Executors.newSingleThreadExecutor();
-    ArtProfileCollectionAdditions artProfileCollectionAdditions =
-        ArtProfileCollectionAdditions.nop();
+    ProfileCollectionAdditions profileCollectionAdditions = ProfileCollectionAdditions.nop();
     SubtypingInfo subtypingInfo = SubtypingInfo.create(appView);
     RootSet rootSet =
         RootSet.builder(
                 appView,
-                artProfileCollectionAdditions,
+                profileCollectionAdditions,
                 subtypingInfo,
                 appView.options().getProguardConfiguration().getRules())
             .build(executor);
     appView.setRootSet(rootSet);
     EnqueuerResult enqueuerResult =
         EnqueuerFactory.createForInitialTreeShaking(
-                appView, artProfileCollectionAdditions, executor, subtypingInfo)
+                appView, profileCollectionAdditions, executor, subtypingInfo)
             .traceApplication(rootSet, executor, Timing.empty());
     executor.shutdown();
     // We do not run the tree pruner to ensure that the hierarchy is as designed and not modified
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 1523915..704ce16 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -438,6 +438,14 @@
     return addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS);
   }
 
+  public T addKeepRuntimeVisibleTypeAnnotations() {
+    return addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_TYPE_ANNOTATIONS);
+  }
+
+  public T addKeepRuntimeInvisibleTypeAnnotations() {
+    return addKeepAttributes(ProguardKeepAttributes.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS);
+  }
+
   public T addKeepAllAttributes() {
     return addKeepAttributes("*");
   }
diff --git a/src/test/java/com/android/tools/r8/annotations/TypeUseAnnotationPruneTest.java b/src/test/java/com/android/tools/r8/annotations/TypeUseAnnotationPruneTest.java
new file mode 100644
index 0000000..3a72818
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/annotations/TypeUseAnnotationPruneTest.java
@@ -0,0 +1,99 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.annotations;
+
+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.ClassFileConsumer;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.annotations.testclasses.MainWithTypeAndGeneric;
+import com.android.tools.r8.annotations.testclasses.NotNullTestClass;
+import com.android.tools.r8.annotations.testclasses.NotNullTestRuntime;
+import com.android.tools.r8.annotations.testclasses.SuperInterface;
+import com.android.tools.r8.annotations.testclasses.TestClassWithTypeAndGenericAnnotations;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FieldSubject;
+import com.android.tools.r8.utils.codeinspector.FoundAnnotationSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.lang.reflect.AnnotatedType;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TypeUseAnnotationPruneTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  private String getExpected(String notNullTestRuntimeTypeName) {
+    return StringUtils.joinLines(
+        "printAnnotation - Class: " + notNullTestRuntimeTypeName,
+        "printAnnotation - Class: NULL",
+        "printAnnotation - Field: NULL",
+        "printAnnotation - Field: NULL",
+        "printAnnotation - Method: NULL",
+        "printAnnotation - Method: NULL",
+        "Hello World!");
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(
+            MainWithTypeAndGeneric.class,
+            NotNullTestClass.class,
+            NotNullTestRuntime.class,
+            TestClassWithTypeAndGenericAnnotations.class,
+            SuperInterface.class)
+        .setMinApi(parameters)
+        .addKeepClassRules(NotNullTestClass.class, NotNullTestRuntime.class, SuperInterface.class)
+        .addKeepRuntimeVisibleAnnotations()
+        .addKeepRuntimeInvisibleAnnotations()
+        .addKeepRuntimeVisibleParameterAnnotations()
+        .addKeepRuntimeInvisibleParameterAnnotations()
+        .addKeepAttributeSignature()
+        .addKeepAttributeExceptions()
+        .addKeepMainRule(MainWithTypeAndGeneric.class)
+        .addKeepClassAndMembersRules(TestClassWithTypeAndGenericAnnotations.class)
+        .applyIf(parameters.isDexRuntime(), b -> b.addDontWarn(AnnotatedType.class))
+        .compile()
+        .inspectWithOptions(
+            inspector -> {
+              ClassSubject notNullTestClass = inspector.clazz(NotNullTestClass.class);
+              assertThat(notNullTestClass, isPresent());
+              ClassSubject notNullTestRuntime = inspector.clazz(NotNullTestRuntime.class);
+              assertThat(notNullTestRuntime, isPresent());
+              ClassSubject clazz = inspector.clazz(TestClassWithTypeAndGenericAnnotations.class);
+              assertThat(clazz, isPresent());
+              assertTrue(
+                  clazz.annotations().stream().noneMatch(FoundAnnotationSubject::isTypeAnnotation));
+              FieldSubject field = clazz.uniqueFieldWithOriginalName("field");
+              assertThat(field, isPresent());
+              assertEquals(0, field.annotations().size());
+              MethodSubject method = clazz.uniqueMethodWithOriginalName("method");
+              assertThat(method, isPresent());
+              // We create a dex annotation for the checked exception.
+              assertEquals(1, method.annotations().size());
+            },
+            options -> options.programConsumer = ClassFileConsumer.emptyConsumer())
+        .run(parameters.getRuntime(), MainWithTypeAndGeneric.class)
+        .assertFailureWithErrorThatThrowsIf(parameters.isDexRuntime(), NoSuchMethodError.class)
+        .assertSuccessWithOutputLinesIf(
+            parameters.isCfRuntime(), getExpected(typeName(NotNullTestRuntime.class)));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/annotations/TypeUseAnnotationWithGenericsTest.java b/src/test/java/com/android/tools/r8/annotations/TypeUseAnnotationWithGenericsTest.java
new file mode 100644
index 0000000..307e052
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/annotations/TypeUseAnnotationWithGenericsTest.java
@@ -0,0 +1,239 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.annotations;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ThrowableConsumer;
+import com.android.tools.r8.annotations.testclasses.MainWithTypeAndGeneric;
+import com.android.tools.r8.annotations.testclasses.NotNullTestClass;
+import com.android.tools.r8.annotations.testclasses.NotNullTestRuntime;
+import com.android.tools.r8.annotations.testclasses.SuperInterface;
+import com.android.tools.r8.annotations.testclasses.TestClassWithTypeAndGenericAnnotations;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FieldSubject;
+import com.android.tools.r8.utils.codeinspector.FoundAnnotationSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.lang.reflect.AnnotatedType;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TypeUseAnnotationWithGenericsTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void testJvm() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    testForJvm(parameters)
+        .addProgramClasses(
+            MainWithTypeAndGeneric.class,
+            NotNullTestClass.class,
+            NotNullTestRuntime.class,
+            TestClassWithTypeAndGenericAnnotations.class,
+            SuperInterface.class)
+        .run(parameters.getRuntime(), MainWithTypeAndGeneric.class)
+        .assertSuccessWithOutputLines(getExpected(typeName(NotNullTestRuntime.class)));
+  }
+
+  private String getExpected(String notNullTestRuntimeTypeName) {
+    return StringUtils.joinLines(
+        "printAnnotation - Class: " + notNullTestRuntimeTypeName,
+        "printAnnotation - Class: NULL",
+        "printAnnotation - Extends(0): " + notNullTestRuntimeTypeName,
+        "printAnnotation - Implements(0): " + notNullTestRuntimeTypeName,
+        "printAnnotation - Field: NULL",
+        "printAnnotation - Field: NULL",
+        "printAnnotation - Field(0): " + notNullTestRuntimeTypeName,
+        "printAnnotation - Method: NULL",
+        "printAnnotation - Method: NULL",
+        "printAnnotation - MethodReturnType(0): " + notNullTestRuntimeTypeName,
+        "printAnnotation - MethodParameter at 0(0): " + notNullTestRuntimeTypeName,
+        "printAnnotation - MethodParameter at 1(0): " + notNullTestRuntimeTypeName,
+        "printAnnotation - MethodException at 0(0): " + notNullTestRuntimeTypeName,
+        "printAnnotation - MethodException at 1(0): " + notNullTestRuntimeTypeName,
+        "Hello World!");
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    testForD8(parameters.getBackend())
+        .addProgramClasses(
+            MainWithTypeAndGeneric.class,
+            NotNullTestClass.class,
+            NotNullTestRuntime.class,
+            TestClassWithTypeAndGenericAnnotations.class,
+            SuperInterface.class)
+        .setMinApi(parameters)
+        .run(parameters.getRuntime(), MainWithTypeAndGeneric.class)
+        .assertFailureWithErrorThatThrows(NoSuchMethodError.class);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    setupR8Test(
+        builder ->
+            builder.addKeepClassRules(
+                NotNullTestClass.class, NotNullTestRuntime.class, SuperInterface.class));
+  }
+
+  @Test
+  public void testR8WithRenaming() throws Exception {
+    setupR8Test(
+        builder ->
+            builder.addKeepClassRulesWithAllowObfuscation(
+                NotNullTestClass.class, NotNullTestRuntime.class, SuperInterface.class));
+  }
+
+  private void setupR8Test(ThrowableConsumer<R8FullTestBuilder> modifier) throws Exception {
+    Box<String> finalNotNullTestRuntimeName = new Box<>();
+    testForR8(parameters.getBackend())
+        .addProgramClasses(
+            MainWithTypeAndGeneric.class,
+            NotNullTestClass.class,
+            NotNullTestRuntime.class,
+            TestClassWithTypeAndGenericAnnotations.class,
+            SuperInterface.class)
+        .setMinApi(parameters)
+        .apply(modifier)
+        .addKeepRuntimeVisibleAnnotations()
+        .addKeepRuntimeInvisibleAnnotations()
+        .addKeepRuntimeVisibleTypeAnnotations()
+        .addKeepRuntimeInvisibleTypeAnnotations()
+        .addKeepAttributeSignature()
+        .addKeepAttributeExceptions()
+        .addKeepMainRule(MainWithTypeAndGeneric.class)
+        .addKeepClassAndMembersRules(TestClassWithTypeAndGenericAnnotations.class)
+        .applyIf(parameters.isDexRuntime(), b -> b.addDontWarn(AnnotatedType.class))
+        .compile()
+        .inspectWithOptions(
+            inspector -> {
+              ClassSubject notNullTestClass = inspector.clazz(NotNullTestClass.class);
+              assertThat(notNullTestClass, isPresent());
+              ClassSubject notNullTestRuntime = inspector.clazz(NotNullTestRuntime.class);
+              assertThat(notNullTestRuntime, isPresent());
+              finalNotNullTestRuntimeName.set(notNullTestRuntime.getFinalName());
+              ClassSubject clazz = inspector.clazz(TestClassWithTypeAndGenericAnnotations.class);
+              assertThat(clazz, isPresent());
+              if (parameters.isDexRuntime()) {
+                inspectAnnotations(
+                    clazz.annotations(),
+                    notNullTestRuntime.getFinalReference(),
+                    notNullTestClass.getFinalReference(),
+                    2,
+                    0,
+                    1,
+                    1);
+              } else {
+                inspectAnnotations(
+                    clazz.annotations(),
+                    notNullTestRuntime.getFinalReference(),
+                    notNullTestClass.getFinalReference(),
+                    10,
+                    8,
+                    5,
+                    5);
+              }
+              FieldSubject field = clazz.uniqueFieldWithOriginalName("field");
+              assertThat(field, isPresent());
+              if (parameters.isDexRuntime()) {
+                inspectAnnotations(
+                    field.annotations(),
+                    notNullTestRuntime.getFinalReference(),
+                    notNullTestClass.getFinalReference(),
+                    0,
+                    0,
+                    0,
+                    0);
+              } else {
+                inspectAnnotations(
+                    field.annotations(),
+                    notNullTestRuntime.getFinalReference(),
+                    notNullTestClass.getFinalReference(),
+                    4,
+                    4,
+                    2,
+                    2);
+              }
+              MethodSubject method = clazz.uniqueMethodWithOriginalName("method");
+              assertThat(method, isPresent());
+              // We create a dex annotation for the checked exception.
+              if (parameters.isDexRuntime()) {
+                inspectAnnotations(
+                    method.annotations(),
+                    notNullTestRuntime.getFinalReference(),
+                    notNullTestClass.getFinalReference(),
+                    1,
+                    0,
+                    0,
+                    0);
+              } else {
+                inspectAnnotations(
+                    method.annotations(),
+                    notNullTestRuntime.getFinalReference(),
+                    notNullTestClass.getFinalReference(),
+                    17,
+                    16,
+                    8,
+                    8);
+              }
+            },
+            options -> options.programConsumer = ClassFileConsumer.emptyConsumer())
+        .run(parameters.getRuntime(), MainWithTypeAndGeneric.class)
+        .assertFailureWithErrorThatThrowsIf(parameters.isDexRuntime(), NoSuchMethodError.class)
+        .assertSuccessWithOutputLinesIf(
+            parameters.isCfRuntime(), getExpected(finalNotNullTestRuntimeName.get()));
+  }
+
+  private void inspectAnnotations(
+      List<FoundAnnotationSubject> annotations,
+      ClassReference notNullRuntime,
+      ClassReference notNullClass,
+      int expectedCount,
+      int expectedTypeAnnotationCount,
+      int expectedNotNullTestRuntimeCount,
+      int expectedNotNullTestClassCount) {
+    assertEquals(expectedCount, annotations.size());
+    assertEquals(
+        expectedTypeAnnotationCount,
+        annotations.stream().filter(FoundAnnotationSubject::isTypeAnnotation).count());
+    assertEquals(
+        expectedNotNullTestRuntimeCount,
+        annotations.stream()
+            .filter(
+                annotation ->
+                    annotation.getAnnotation().type.asClassReference().equals(notNullRuntime))
+            .count());
+    assertEquals(
+        expectedNotNullTestClassCount,
+        annotations.stream()
+            .filter(
+                annotation ->
+                    annotation.getAnnotation().type.asClassReference().equals(notNullClass))
+            .count());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/annotations/testclasses/MainWithTypeAndGeneric.java b/src/test/java/com/android/tools/r8/annotations/testclasses/MainWithTypeAndGeneric.java
new file mode 100644
index 0000000..b1ac103
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/annotations/testclasses/MainWithTypeAndGeneric.java
@@ -0,0 +1,59 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.annotations.testclasses;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedType;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.List;
+
+public class MainWithTypeAndGeneric {
+
+  public static void main(String[] args) throws Exception {
+    Class<TestClassWithTypeAndGenericAnnotations> testClass =
+        TestClassWithTypeAndGenericAnnotations.class;
+    printAnnotation("Class", testClass.getAnnotation(NotNullTestRuntime.class));
+    printAnnotation("Class", testClass.getAnnotation(NotNullTestClass.class));
+    printAnnotatedType("Extends", testClass.getAnnotatedSuperclass());
+    for (AnnotatedType annotatedInterface : testClass.getAnnotatedInterfaces()) {
+      printAnnotatedType("Implements", annotatedInterface);
+    }
+    Field field = testClass.getDeclaredField("field");
+    printAnnotation("Field", field.getAnnotation(NotNullTestRuntime.class));
+    printAnnotation("Field", field.getAnnotation(NotNullTestClass.class));
+    printAnnotatedType("Field", field.getAnnotatedType());
+    Method method = testClass.getDeclaredMethod("method", int.class, List.class, Object.class);
+    printAnnotation("Method", method.getAnnotation(NotNullTestRuntime.class));
+    printAnnotation("Method", method.getAnnotation(NotNullTestClass.class));
+    printAnnotatedType("MethodReturnType", method.getAnnotatedReturnType());
+    for (Annotation[] parameterAnnotation : method.getParameterAnnotations()) {
+      for (Annotation annotation : parameterAnnotation) {
+        printAnnotation("MethodParameter", annotation);
+      }
+    }
+    for (int i = 0; i < method.getAnnotatedParameterTypes().length; i++) {
+      printAnnotatedType("MethodParameter at " + i, method.getAnnotatedParameterTypes()[i]);
+    }
+    for (int i = 0; i < method.getAnnotatedExceptionTypes().length; i++) {
+      printAnnotatedType("MethodException at " + i, method.getAnnotatedExceptionTypes()[i]);
+    }
+    System.out.println("Hello World!");
+  }
+
+  public static void printAnnotation(String name, Annotation annotation) {
+    System.out.println(
+        "printAnnotation - "
+            + name
+            + ": "
+            + (annotation == null ? "NULL" : annotation.annotationType().getName()));
+  }
+
+  public static void printAnnotatedType(String name, AnnotatedType annotatedType) {
+    for (int i = 0; i < annotatedType.getAnnotations().length; i++) {
+      printAnnotation(name + "(" + i + ")", annotatedType.getAnnotations()[i]);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/annotations/testclasses/NotNullTestClass.java b/src/test/java/com/android/tools/r8/annotations/testclasses/NotNullTestClass.java
new file mode 100644
index 0000000..aa5a53d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/annotations/testclasses/NotNullTestClass.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.annotations.testclasses;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.TYPE_USE)
+@Retention(RetentionPolicy.CLASS)
+public @interface NotNullTestClass {}
diff --git a/src/test/java/com/android/tools/r8/annotations/testclasses/NotNullTestRuntime.java b/src/test/java/com/android/tools/r8/annotations/testclasses/NotNullTestRuntime.java
new file mode 100644
index 0000000..52c5ced
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/annotations/testclasses/NotNullTestRuntime.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.annotations.testclasses;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.TYPE_USE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface NotNullTestRuntime {}
diff --git a/src/test/java/com/android/tools/r8/annotations/testclasses/SuperInterface.java b/src/test/java/com/android/tools/r8/annotations/testclasses/SuperInterface.java
new file mode 100644
index 0000000..61e6ae6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/annotations/testclasses/SuperInterface.java
@@ -0,0 +1,7 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.annotations.testclasses;
+
+public interface SuperInterface<T> {}
diff --git a/src/test/java/com/android/tools/r8/annotations/testclasses/TestClassWithTypeAndGenericAnnotations.java b/src/test/java/com/android/tools/r8/annotations/testclasses/TestClassWithTypeAndGenericAnnotations.java
new file mode 100644
index 0000000..fe1853e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/annotations/testclasses/TestClassWithTypeAndGenericAnnotations.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.annotations.testclasses;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+@NotNullTestRuntime
+@NotNullTestClass
+public class TestClassWithTypeAndGenericAnnotations<@NotNullTestRuntime @NotNullTestClass T>
+    extends @NotNullTestRuntime @NotNullTestClass Object
+    implements @NotNullTestRuntime @NotNullTestClass SuperInterface<
+        @NotNullTestRuntime @NotNullTestClass T> {
+
+  @NotNullTestRuntime @NotNullTestClass
+  List<@NotNullTestRuntime @NotNullTestClass Object> field = null;
+
+  @NotNullTestRuntime
+  @NotNullTestClass
+  <@NotNullTestRuntime @NotNullTestClass S>
+      List<@NotNullTestRuntime @NotNullTestClass Object> method(
+          @NotNullTestRuntime @NotNullTestClass int foo,
+          @NotNullTestRuntime @NotNullTestClass
+              List<@NotNullTestRuntime @NotNullTestClass String> bar,
+          S s)
+          throws @NotNullTestClass @NotNullTestRuntime RuntimeException,
+              @NotNullTestClass @NotNullTestRuntime IOException {
+    @NotNullTestRuntime
+    @NotNullTestClass
+    Object local = System.currentTimeMillis() > 0 ? new Object() : foo;
+    ArrayList<@NotNullTestRuntime @NotNullTestClass Object> objects = new ArrayList<>();
+    objects.add(foo);
+    this.field = objects;
+    return objects;
+  }
+}
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 57aaddc..cc9170b 100644
--- a/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java
@@ -87,7 +87,7 @@
     DexProgramClass classL = appView.definitionForProgramType(typeL);
     DexProgramClass classA = appView.definitionForProgramType(typeA);
     LookupResult lookupResult =
-        resolutionResult.lookupVirtualDispatchTargets(classI, appView.appInfo(), classL, classA);
+        resolutionResult.lookupVirtualDispatchTargets(classI, appView, classL, classA);
     assertTrue(lookupResult.isLookupResultSuccess());
     LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
     Set<String> targets = new HashSet<>();
@@ -128,7 +128,7 @@
     DexProgramClass classI = appView.definitionForProgramType(typeI);
     DexProgramClass classA = appView.definitionForProgramType(typeA);
     LookupResult lookupResult =
-        resolutionResult.lookupVirtualDispatchTargets(classI, appView.appInfo(), classI, classA);
+        resolutionResult.lookupVirtualDispatchTargets(classI, appView, classI, classA);
     assertTrue(lookupResult.isLookupResultSuccess());
     LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
     Set<String> targets = new HashSet<>();
@@ -168,7 +168,7 @@
     DexProgramClass classI = appView.definitionForProgramType(typeI);
     DexProgramClass classA = appView.definitionForProgramType(typeB);
     LookupResult lookupResult =
-        resolutionResult.lookupVirtualDispatchTargets(classI, appView.appInfo(), classA, classA);
+        resolutionResult.lookupVirtualDispatchTargets(classI, appView, classA, classA);
     assertTrue(lookupResult.isLookupResultSuccess());
     LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
     Set<String> targets = new HashSet<>();
@@ -206,7 +206,7 @@
     DexProgramClass classI = appView.definitionForProgramType(typeI);
     DexProgramClass classA = appView.definitionForProgramType(typeB);
     LookupResult lookupResult =
-        resolutionResult.lookupVirtualDispatchTargets(classI, appView.appInfo(), classA, classA);
+        resolutionResult.lookupVirtualDispatchTargets(classI, appView, classA, classA);
     assertTrue(lookupResult.isLookupResultSuccess());
     LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
     Set<String> targets = new HashSet<>();
@@ -246,7 +246,7 @@
     DexProgramClass classI = appView.definitionForProgramType(typeI);
     DexProgramClass classA = appView.definitionForProgramType(typeB);
     LookupResult lookupResult =
-        resolutionResult.lookupVirtualDispatchTargets(classI, appView.appInfo(), classA, classA);
+        resolutionResult.lookupVirtualDispatchTargets(classI, appView, classA, classA);
     assertTrue(lookupResult.isLookupResultSuccess());
     LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
     Set<String> targets = new HashSet<>();
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorWithEquivalenceStackTraceTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorWithEquivalenceStackTraceTest.java
index c39d3ef..655e6a8 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorWithEquivalenceStackTraceTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorWithEquivalenceStackTraceTest.java
@@ -60,7 +60,8 @@
                       // TODO(b/124483578): Stack trace lines from the merging of equivalent
                       //  constructors should retrace to the set of lines from each of the
                       //  individual source constructors.
-                      .map(1, stackTraceLine -> stackTraceLine.builderOf().setLineNumber(0).build())
+                      .map(
+                          1, stackTraceLine -> stackTraceLine.builderOf().setLineNumber(-1).build())
                       .build();
               assertThat(stackTrace, isSame(expectedStackTraceWithMergedConstructor));
               assertThat(codeInspector.clazz(B.class), not(isPresent()));
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/SyntheticBridgeSignaturesTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/SyntheticBridgeSignaturesTest.java
index b7bb268..e8d7572 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/SyntheticBridgeSignaturesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/SyntheticBridgeSignaturesTest.java
@@ -13,12 +13,14 @@
 import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.ir.optimize.Inliner.Reason;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
 import com.google.common.collect.ImmutableSet;
 import java.util.List;
+import org.junit.Assume;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -47,6 +49,9 @@
 
   @Test
   public void test() throws Throwable {
+    Assume.assumeTrue(
+        "b/273921056",
+        parameters.isCfRuntime() || !parameters.getDexRuntimeVersion().isEqualTo(Version.V14_0_0));
     R8TestCompileResult compileResult =
         testForR8(parameters.getBackend())
             .addInnerClasses(getClass())
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerDebugTestRunner.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerDebugTestRunner.java
index 5177efb..5ed531b 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerDebugTestRunner.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerDebugTestRunner.java
@@ -10,10 +10,11 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.TestRuntime;
 import com.android.tools.r8.debug.DebugTestBase;
 import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.Command;
 import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.DebuggeeState;
-import com.android.tools.r8.debug.DexDebugTestConfig;
+import com.android.tools.r8.debug.DebugTestConfig;
 import com.android.tools.r8.utils.AndroidApp;
 import java.io.File;
 import java.nio.file.Path;
@@ -31,11 +32,11 @@
     this.temp = temp;
   }
 
-  public void run(AndroidApp app, Path proguardMapPath) throws Throwable {
+  public void run(TestRuntime runtime, AndroidApp app, Path proguardMapPath) throws Throwable {
     Path appPath = File.createTempFile("app", ".zip", temp.getRoot()).toPath();
     app.writeToZipForTesting(appPath, OutputMode.DexIndexed);
 
-    DexDebugTestConfig config = new DexDebugTestConfig(appPath);
+    DebugTestConfig config = DebugTestConfig.create(runtime, appPath);
     config.allowUnprocessedCommands();
     config.setProguardMap(proguardMapPath);
 
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
index d8b923b..22529e5 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
@@ -223,6 +223,9 @@
   // This test has a cycle in the call graph consisting of the methods A.<init> and B.<init>.
   @Test
   public void testCallGraphCycle() throws Throwable {
+    Assume.assumeTrue(
+        "b/273921056",
+        parameters.isCfRuntime() || !parameters.getDexRuntimeVersion().isEqualTo(Version.V14_0_0));
     String main = "classmerging.CallGraphCycleTest";
     Path[] programFiles =
         new Path[] {
@@ -316,6 +319,9 @@
 
   @Test
   public void testFieldCollision() throws Throwable {
+    Assume.assumeTrue(
+        "b/273921056",
+        parameters.isCfRuntime() || !parameters.getDexRuntimeVersion().isEqualTo(Version.V14_0_0));
     String main = "classmerging.FieldCollisionTest";
     Path[] programFiles =
         new Path[] {
@@ -423,6 +429,9 @@
 
   @Test
   public void testPinnedParameterTypes() throws Throwable {
+    Assume.assumeTrue(
+        "b/273921056",
+        parameters.isCfRuntime() || !parameters.getDexRuntimeVersion().isEqualTo(Version.V14_0_0));
     String main = "classmerging.PinnedParameterTypesTest";
     Path[] programFiles =
         new Path[] {
@@ -448,6 +457,9 @@
 
   @Test
   public void testPinnedArrayParameterTypes() throws Throwable {
+    Assume.assumeTrue(
+        "b/273921056",
+        parameters.isCfRuntime() || !parameters.getDexRuntimeVersion().isEqualTo(Version.V14_0_0));
     String main = "classmerging.PinnedArrayParameterTypesTest";
     Path[] programFiles =
         new Path[] {
@@ -841,6 +853,9 @@
   //   }
   @Test
   public void testSuperCallToMergedClassIsRewritten() throws Throwable {
+    Assume.assumeTrue(
+        "b/273921056",
+        parameters.isCfRuntime() || !parameters.getDexRuntimeVersion().isEqualTo(Version.V14_0_0));
     assumeTrue(parameters.isDexRuntime()); // Due to smali input.
     assumeFalse(parameters.getRuntime().asDex().getVm().getVersion() == Version.V5_1_1);
     assumeFalse(parameters.getRuntime().asDex().getVm().getVersion() == Version.V6_0_1);
@@ -1217,7 +1232,7 @@
     // Check that we never come across a method that has a name with "$classmerging$" in it during
     // debugging.
     if (debugTestRunner != null && parameters.isDexRuntime()) {
-      debugTestRunner.run(compileResult.app, proguardMapPath);
+      debugTestRunner.run(parameters.getRuntime(), compileResult.app, proguardMapPath);
     }
     return compileResult;
   }
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTestBase.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTestBase.java
index e512b2b..b631cc8 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTestBase.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTestBase.java
@@ -21,6 +21,6 @@
   public void runDebugTest(Class<?> mainClass, R8TestCompileResult compileResult) throws Throwable {
     assertTrue(parameters.isDexRuntime());
     new VerticalClassMergerDebugTestRunner(mainClass.getTypeName(), temp)
-        .run(compileResult.app, compileResult.writeProguardMap());
+        .run(parameters.getRuntime(), compileResult.app, compileResult.writeProguardMap());
   }
 }
diff --git a/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java b/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
index 265bb19..6676e2c 100644
--- a/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
+++ b/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
@@ -36,6 +36,7 @@
 import java.util.jar.JarEntry;
 import java.util.jar.JarInputStream;
 import java.util.stream.Collectors;
+import org.junit.Assume;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
@@ -142,6 +143,10 @@
   public void testContinuousSingleStep() throws Throwable {
     DebugTestConfig config = compiledJars.apply(jarPath);
     assert config != null;
+    Assume.assumeTrue(
+        "b/273921056",
+        config.isCfRuntime()
+            || !config.getRuntime().asDex().getVersion().isEqualTo(Version.V14_0_0));
     runContinuousTest(mainClass, config, MAIN_METHOD_NAME);
   }
 
diff --git a/src/test/java/com/android/tools/r8/debug/LocalsTest.java b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
index 35178f8..a2254be 100644
--- a/src/test/java/com/android/tools/r8/debug/LocalsTest.java
+++ b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.debug;
 
+import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.Command;
 import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.FrameInspector;
 import java.util.ArrayList;
@@ -14,6 +15,7 @@
 import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants.Tag;
 import org.apache.harmony.jpda.tests.framework.jdwp.Value;
 import org.junit.Assert;
+import org.junit.Assume;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -277,6 +279,10 @@
 
   @Test
   public void testLocals_MoreThan16() throws Throwable {
+    Assume.assumeTrue(
+        "b/273921056",
+        config.isCfRuntime()
+            || !config.getRuntime().asDex().getVersion().isEqualTo(Version.V14_0_0));
     final int minIndex = 1;
     final int maxIndex = 16;
     Map<String, Value> arrayLocals = new HashMap<>();
diff --git a/src/test/java/com/android/tools/r8/debuginfo/InliningWithoutPositionsTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/InliningWithoutPositionsTestRunner.java
index c164fa7..ba0dba3 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/InliningWithoutPositionsTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/InliningWithoutPositionsTestRunner.java
@@ -150,7 +150,7 @@
         .setClassName(TEST_CLASS)
         .setFileName(TEST_FILE)
         .setMethodName(methodName)
-        .setLineNumber(hasPosition ? location.line : 0)
+        .setLineNumber(hasPosition ? location.line : -1)
         .build();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/debuginfo/NoLineInfoTest.java b/src/test/java/com/android/tools/r8/debuginfo/NoLineInfoTest.java
index f0d4c89..3831e44 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/NoLineInfoTest.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/NoLineInfoTest.java
@@ -207,17 +207,16 @@
   private StackTrace getUnexpectedRetracedStacktrace() {
     assertFalse(parameters.isCfRuntime());
     StackTraceLine fooLine;
-    if (customSourceFile) {
-      // TODO(b/232212653): Should retrace convert out of "0" and represent it as <noline>/-1?
-      fooLine = inputLine("foo", 0);
-    } else if (isRuntimeWithPcAsLineNumberSupport()) {
+    if (isRuntimeWithPcAsLineNumberSupport() && !customSourceFile) {
       // TODO(b/232212653): Retrace should convert PC 1 to line <noline>/-1/0.
       fooLine = inputLine("foo", 1);
     } else {
       fooLine = inputLine("foo", -1);
     }
-    StackTraceLine barLine = inputLine("bar", getPcEncoding(0));
-    StackTraceLine bazLine = inputLine("baz", getPcEncoding(0));
+    int position =
+        isCompileWithPcAsLineNumberSupport() && !customSourceFile ? -1 : getPcEncoding(0);
+    StackTraceLine barLine = inputLine("bar", position);
+    StackTraceLine bazLine = inputLine("baz", position);
     return StackTrace.builder()
         .add(fooLine)
         .add(barLine)
diff --git a/src/test/java/com/android/tools/r8/debuginfo/OverloadWithNoLineNumberTest.java b/src/test/java/com/android/tools/r8/debuginfo/OverloadWithNoLineNumberTest.java
index 3695c0e..d0a41c5 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/OverloadWithNoLineNumberTest.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/OverloadWithNoLineNumberTest.java
@@ -65,8 +65,8 @@
                   String className = typeName(SimpleCallChainClassWithOverloads.class);
                   assertEquals(
                       StringUtils.joinLines(
-                          "\tat " + className + ".void test(long)(" + SOURCE_FILE_NAME + ":0)",
-                          "\tat " + className + ".void test()(" + SOURCE_FILE_NAME + ":0)",
+                          "\tat " + className + ".void test(long)(" + SOURCE_FILE_NAME + ")",
+                          "\tat " + className + ".void test()(" + SOURCE_FILE_NAME + ")",
                           "\tat "
                               + className
                               + ".void main(java.lang.String[])("
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MethodBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/MethodBackportTest.java
new file mode 100644
index 0000000..78c2770
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MethodBackportTest.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.backports;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public final class MethodBackportTest extends AbstractBackportTest {
+  @Parameters(name = "{0}")
+  public static Iterable<?> data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public MethodBackportTest(TestParameters parameters) {
+    super(parameters, String.class, Main.class);
+    registerTarget(AndroidApiLevel.O, 0);
+  }
+
+  static final class Main extends MiniAssert {
+    public static void main(String[] args) throws NoSuchMethodException {
+      assertEquals(0, Main.class.getMethod("empty").getParameterCount());
+      assertEquals(
+          2, Main.class.getMethod("wideArgs", long.class, double.class).getParameterCount());
+      assertEquals(1, Main.class.getMethod("args", Object[].class).getParameterCount());
+    }
+
+    public static void empty() {}
+
+    public static void wideArgs(long l, double d) {}
+
+    public static void args(Object... o) {}
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
index 28e63fa..446b84e 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
@@ -96,6 +96,10 @@
         minApiLevel == AndroidApiLevel.L,
         supportsMethodButNotAllMethodsInClass(
             "java/util/stream/DoubleStream#parallel()Ljava/util/stream/DoubleStream;"));
+    assertEquals(
+        minApiLevel == AndroidApiLevel.L,
+        supportsMethodButNotAllMethodsInClass(
+            "java/util/stream/BaseStream#parallel()Ljava/util/stream/BaseStream;"));
     assertTrue(
         supportsMethodButNotAllMethodsInClass(
             "java/util/stream/DoubleStream#allMatch(Ljava/util/function/DoublePredicate;)Z"));
@@ -222,9 +226,10 @@
   }
 
   public static void main(String[] args) throws Exception {
-    // Generate all html docs.
-    Path folder = Paths.get("html");
-    Files.createDirectories(folder);
+    // Generate all html docs and lint files.
+    Path top = Paths.get("generated");
+    Path html = top.resolve("html");
+    Files.createDirectories(html);
     ImmutableList<LibraryDesugaringSpecification> specs =
         ImmutableList.of(JDK8, JDK11_MINIMAL, JDK11, JDK11_PATH, JDK11_LEGACY);
     for (LibraryDesugaringSpecification spec : specs) {
@@ -232,9 +237,13 @@
           spec == JDK8
               ? ToolHelper.DESUGARED_JDK_8_LIB_JAR
               : LibraryDesugaringSpecification.getTempLibraryJDK11Undesugar();
-      new GenerateHtmlDoc(
-              spec.getSpecification().toString(), jdkLibJar.toString(), folder.toString())
+      new GenerateHtmlDoc(spec.getSpecification().toString(), jdkLibJar.toString(), html.toString())
           .run(spec + ".html");
+      Path lint = top.resolve("lint_" + spec);
+      Files.createDirectories(lint);
+      new GenerateLintFiles(
+              spec.getSpecification().toString(), jdkLibJar.toString(), lint.toString())
+          .run();
     }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
index 9b23bd1..96b1358 100644
--- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
@@ -138,7 +138,7 @@
   }
 
   @Test
-  @IgnoreForRangeOfVmVersions(from = Version.V7_0_0, to = Version.V13_0_0) // No desugaring
+  @IgnoreForRangeOfVmVersions(from = Version.V7_0_0, to = Version.V14_0_0) // No desugaring
   public void testInvokeDefault1() throws Exception {
     ensureCustomCheck(
         (javaResult, d8Result, r8Result, r8ShakenResult) -> {
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 ced7e4f..84a1bf3 100644
--- a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
@@ -92,7 +92,8 @@
     );
 
     AndroidApp application = buildApplication(builder);
-    AppInfoWithClassHierarchy appInfo = computeAppInfoWithClassHierarchy(application);
+    AppView<AppInfoWithClassHierarchy> appView = computeAppViewWithClassHierarchy(application);
+    AppInfoWithClassHierarchy appInfo = appView.appInfo();
     CodeInspector inspector = new CodeInspector(appInfo.app());
     ProgramMethod method = getMethod(inspector, DEFAULT_CLASS_NAME, "int", "x", ImmutableList.of());
     assertFalse(
@@ -100,8 +101,8 @@
             .resolveMethodOnClassHolderLegacy(method.getReference())
             .getSingleTarget()
             .isVirtualMethod());
-    assertNull(appInfo.lookupDirectTarget(method.getReference(), method));
-    assertNotNull(appInfo.lookupStaticTarget(method.getReference(), method));
+    assertNull(appInfo.lookupDirectTarget(method.getReference(), method, appView));
+    assertNotNull(appInfo.lookupStaticTarget(method.getReference(), method, appView));
 
     if (ToolHelper.getDexVm().getVersion().isOlderThanOrEqual(DexVm.Version.V4_4_4)) {
       // Dalvik rejects at verification time instead of producing the
@@ -165,7 +166,8 @@
     );
 
     AndroidApp application = buildApplication(builder);
-    AppInfoWithClassHierarchy appInfo = computeAppInfoWithClassHierarchy(application);
+    AppView<AppInfoWithClassHierarchy> appView = computeAppViewWithClassHierarchy(application);
+    AppInfoWithClassHierarchy appInfo = appView.appInfo();
     CodeInspector inspector = new CodeInspector(appInfo.app());
 
     ProgramMethod methodXOnTestSuper =
@@ -191,13 +193,14 @@
     assertNull(
         appInfo.resolveMethodOnClassLegacy(classTest, methodXOnTestReference).getSingleTarget());
 
-    assertNull(appInfo.lookupDirectTarget(methodXOnTestSuper.getReference(), methodXOnTestSuper));
-    assertNull(appInfo.lookupDirectTarget(methodXOnTestReference, methodYOnTest));
+    assertNull(
+        appInfo.lookupDirectTarget(methodXOnTestSuper.getReference(), methodXOnTestSuper, appView));
+    assertNull(appInfo.lookupDirectTarget(methodXOnTestReference, methodYOnTest, appView));
 
     assertNotNull(
-        appInfo.lookupStaticTarget(methodXOnTestSuper.getReference(), methodXOnTestSuper));
+        appInfo.lookupStaticTarget(methodXOnTestSuper.getReference(), methodXOnTestSuper, appView));
     // Accessing a private target on a different type will fail resolution outright.
-    assertNull(appInfo.lookupStaticTarget(methodXOnTestReference, methodYOnTest));
+    assertNull(appInfo.lookupStaticTarget(methodXOnTestReference, methodYOnTest, appView));
 
     assertEquals("OK", runArt(application));
   }
@@ -254,7 +257,8 @@
       builder.addLibraryFiles(ToolHelper.getDefaultAndroidJar());
     }
     AndroidApp application = builder.build();
-    AppInfoWithClassHierarchy appInfo = computeAppInfoWithClassHierarchy(application);
+    AppView<AppInfoWithClassHierarchy> appView = computeAppViewWithClassHierarchy(application);
+    AppInfoWithClassHierarchy appInfo = appView.appInfo();
     DexItemFactory factory = appInfo.dexItemFactory();
 
     DexType i0 = factory.createType("L" + pkg + "/I0;");
@@ -276,13 +280,13 @@
     DexMethod mOnI3 = factory.createMethod(i3, mProto, m);
     DexMethod mOnI4 = factory.createMethod(i4, mProto, m);
 
-    assertEquals(mOnI0, appInfo.lookupSuperTarget(mOnC0, c1).getReference());
-    assertEquals(mOnI1, appInfo.lookupSuperTarget(mOnI1, c1).getReference());
-    assertEquals(mOnI2, appInfo.lookupSuperTarget(mOnI2, c1).getReference());
+    assertEquals(mOnI0, appInfo.lookupSuperTarget(mOnC0, c1, appView).getReference());
+    assertEquals(mOnI1, appInfo.lookupSuperTarget(mOnI1, c1, appView).getReference());
+    assertEquals(mOnI2, appInfo.lookupSuperTarget(mOnI2, c1, appView).getReference());
 
-    assertNull(appInfo.lookupSuperTarget(mOnC1, c2)); // C2 is not a subclass of C1.
-    assertEquals(mOnI1, appInfo.lookupSuperTarget(mOnI3, c2).getReference());
-    assertEquals(mOnI2, appInfo.lookupSuperTarget(mOnI4, c2).getReference());
+    assertNull(appInfo.lookupSuperTarget(mOnC1, c2, appView)); // C2 is not a subclass of C1.
+    assertEquals(mOnI1, appInfo.lookupSuperTarget(mOnI3, c2, appView).getReference());
+    assertEquals(mOnI2, appInfo.lookupSuperTarget(mOnI4, c2, appView).getReference());
 
     // Copy classes to run on the Java VM.
     Path out = temp.newFolder().toPath();
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 b8078ae..440e8f8 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
@@ -81,7 +81,7 @@
     AppInfoWithLiveness appInfo = null; // TODO(b/154881041): Remove or compute liveness.
     LookupResult lookupResult =
         resolutionResult.lookupVirtualDispatchTargets(
-            clazz, appInfo(), appInfo, dexReference -> false);
+            clazz, appView, appInfo, dexReference -> false);
     assertTrue(lookupResult.isLookupResultSuccess());
     assertTrue(lookupResult.asLookupResultSuccess().contains(method));
   }
@@ -99,7 +99,7 @@
     LookupResultSuccess lookupResult =
         appInfo()
             .resolveMethodOnInterfaceLegacy(clazz, method.getReference())
-            .lookupVirtualDispatchTargets(clazz, appInfo(), appInfo, dexReference -> false)
+            .lookupVirtualDispatchTargets(clazz, appView, appInfo, dexReference -> false)
             .asLookupResultSuccess();
     assertNotNull(lookupResult);
     assertFalse(lookupResult.hasLambdaTargets());
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 037bb82..bd6303e 100644
--- a/src/test/java/com/android/tools/r8/ir/InlineTest.java
+++ b/src/test/java/com/android/tools/r8/ir/InlineTest.java
@@ -23,7 +23,7 @@
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InstructionListIterator;
 import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ProfileCollectionAdditions;
 import com.android.tools.r8.shaking.Enqueuer;
 import com.android.tools.r8.shaking.EnqueuerFactory;
 import com.android.tools.r8.shaking.EnqueuerResult;
@@ -74,21 +74,20 @@
       throws ExecutionException {
     AppView<AppInfoWithClassHierarchy> appView = AppView.createForR8(application.asDirect());
     appView.setAppServices(AppServices.builder(appView).build());
-    ArtProfileCollectionAdditions artProfileCollectionAdditions =
-        ArtProfileCollectionAdditions.nop();
+    ProfileCollectionAdditions profileCollectionAdditions = ProfileCollectionAdditions.nop();
     ExecutorService executorService = ThreadUtils.getExecutorService(options);
     SubtypingInfo subtypingInfo = SubtypingInfo.create(appView);
     appView.setRootSet(
         RootSet.builder(
                 appView,
-                artProfileCollectionAdditions,
+                profileCollectionAdditions,
                 subtypingInfo,
                 ImmutableList.of(ProguardKeepRule.defaultKeepAllRule(unused -> {})))
             .build(executorService));
     Timing timing = Timing.empty();
     Enqueuer enqueuer =
         EnqueuerFactory.createForInitialTreeShaking(
-            appView, artProfileCollectionAdditions, executorService, subtypingInfo);
+            appView, profileCollectionAdditions, executorService, subtypingInfo);
     EnqueuerResult enqueuerResult =
         enqueuer.traceApplication(appView.rootSet(), executorService, timing);
     appView.setAppInfo(enqueuerResult.getAppInfo());
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
index 429f2be..3a7cb1e 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
@@ -50,6 +50,7 @@
           IntegerMethods.class,
           LongMethods.class,
           MathMethods.class,
+          MethodMethods.class,
           ObjectsMethods.class,
           OptionalMethods.class,
           PredicateMethods.class,
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/MethodMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/MethodMethods.java
new file mode 100644
index 0000000..0c03f1f6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/MethodMethods.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.backports;
+
+import java.lang.reflect.Method;
+
+public class MethodMethods {
+
+  public static int getParameterCount(Method method) {
+    return method.getParameterTypes().length;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/InvokeSuperCallInStaticTest.java b/src/test/java/com/android/tools/r8/resolution/InvokeSuperCallInStaticTest.java
index 1b62946..028675a 100644
--- a/src/test/java/com/android/tools/r8/resolution/InvokeSuperCallInStaticTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/InvokeSuperCallInStaticTest.java
@@ -63,8 +63,7 @@
     assertTrue(resolutionResult.isSingleResolution());
     DexProgramClass context =
         appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory()));
-    DexClassAndMethod lookedUpMethod =
-        resolutionResult.lookupInvokeSuperTarget(context, appView.appInfo());
+    DexClassAndMethod lookedUpMethod = resolutionResult.lookupInvokeSuperTarget(context, appView);
     assertNotNull(lookedUpMethod);
     assertEquals(lookedUpMethod.getReference(), method);
   }
diff --git a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
index 4c668d0..e010bfe 100644
--- a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
@@ -215,7 +215,7 @@
       LookupResult lookupResult =
           resolutionResult.lookupVirtualDispatchTargets(
               appView.definitionForProgramType(buildType(Main.class, appView.dexItemFactory())),
-              appInfo);
+              appView);
       assertTrue(lookupResult.isLookupResultSuccess());
       assertFalse(lookupResult.asLookupResultSuccess().hasLambdaTargets());
       Set<DexType> targetHolders = Sets.newIdentityHashSet();
diff --git a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodTest.java b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodTest.java
index 86a0415..a2bf03a 100644
--- a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodTest.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.MethodResolutionResult;
@@ -59,14 +60,15 @@
   public static List<Class<?>> CLASSES =
       ImmutableList.of(A.class, B.class, C.class, I.class, Main.class);
 
+  private static AppView<AppInfoWithLiveness> appView;
   private static AppInfoWithLiveness appInfo;
 
   @BeforeClass
   public static void computeAppInfo() throws Exception {
-    appInfo =
+    appView =
         computeAppViewWithLiveness(
-                buildClasses(CLASSES).addLibraryFile(getMostRecentAndroidJar()).build(), Main.class)
-            .appInfo();
+            buildClasses(CLASSES).addLibraryFile(getMostRecentAndroidJar()).build(), Main.class);
+    appInfo = appView.appInfo();
   }
 
   private static DexMethod buildMethod(Class<?> clazz, String name) {
@@ -92,7 +94,7 @@
     MethodResolutionResult resolutionResult =
         appInfo.resolveMethodOnClassLegacy(methodOnB.holder, methodOnB);
     DexClass context = appInfo.definitionFor(methodOnB.holder);
-    assertTrue(resolutionResult.isIllegalAccessErrorResult(context, appInfo));
+    assertTrue(resolutionResult.isIllegalAccessErrorResult(context, appView));
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
index 2c0e590..8a540f7 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
@@ -132,11 +132,11 @@
 
     assertEquals(
         OptionalBool.of(inSameNest),
-        resolutionResult.isAccessibleFrom(callerClassDefinition, appInfo));
+        resolutionResult.isAccessibleFrom(callerClassDefinition, appView));
     DexClassAndMethod targetSpecial =
-        resolutionResult.lookupInvokeSpecialTarget(callerClassDefinition, appInfo);
+        resolutionResult.lookupInvokeSpecialTarget(callerClassDefinition, appView);
     DexClassAndMethod targetSuper =
-        resolutionResult.lookupInvokeSuperTarget(callerClassDefinition, appInfo);
+        resolutionResult.lookupInvokeSuperTarget(callerClassDefinition, appView);
     if (inSameNest) {
       assertEquals(definingClassDefinition.getType(), targetSpecial.getHolderType());
       assertEquals(targetSpecial.getReference(), targetSuper.getReference());
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java
index 61712f9..a165e20 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java
@@ -105,13 +105,13 @@
     // Verify that the resolved method is accessible if in the same nest.
     assertEquals(
         OptionalBool.of(inSameNest),
-        resolutionResult.isAccessibleFrom(callerClassDefinition, appInfo));
+        resolutionResult.isAccessibleFrom(callerClassDefinition, appView));
 
     // Verify that looking up the dispatch target returns the defining method.
     DexClassAndMethod targetSpecial =
-        resolutionResult.lookupInvokeSpecialTarget(callerClassDefinition, appInfo);
+        resolutionResult.lookupInvokeSpecialTarget(callerClassDefinition, appView);
     DexClassAndMethod targetSuper =
-        resolutionResult.lookupInvokeSuperTarget(callerClassDefinition, appInfo);
+        resolutionResult.lookupInvokeSuperTarget(callerClassDefinition, appView);
     if (inSameNest) {
       assertEquals(definingClassDefinition.type, targetSpecial.getHolderType());
       assertEquals(targetSpecial.getReference(), targetSuper.getReference());
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
index 465b11e..5609ff3 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
@@ -126,9 +126,11 @@
     // Resolution fails when there is a mismatch between the symbolic reference and the definition.
     if (!symbolicReferenceIsDefiningType) {
       if (inSameNest) {
-        assertTrue(resolutionResult.isNoSuchMethodErrorResult(callerClassDefinition, appInfo));
+        assertTrue(
+            resolutionResult.isNoSuchMethodErrorResult(callerClassDefinition, appView, appInfo));
       } else {
-        assertTrue(resolutionResult.isIllegalAccessErrorResult(callerClassDefinition, appInfo));
+        assertTrue(
+            resolutionResult.isIllegalAccessErrorResult(callerClassDefinition, appView, appInfo));
       }
       return;
     }
@@ -140,14 +142,14 @@
     // Verify that the resolved method is accessible only when in the same nest.
     assertEquals(
         OptionalBool.of(inSameNest),
-        resolutionResult.isAccessibleFrom(callerClassDefinition, appInfo));
+        resolutionResult.isAccessibleFrom(callerClassDefinition, appView));
 
     // Verify that looking up the dispatch target returns a valid target
     // iff in the same nest and declaredHolder == definingHolder.
     DexClassAndMethod targetSpecial =
-        resolutionResult.lookupInvokeSpecialTarget(callerClassDefinition, appInfo);
+        resolutionResult.lookupInvokeSpecialTarget(callerClassDefinition, appView);
     DexClassAndMethod targetSuper =
-        resolutionResult.lookupInvokeSuperTarget(callerClassDefinition, appInfo);
+        resolutionResult.lookupInvokeSuperTarget(callerClassDefinition, appView);
     if (inSameNest) {
       assertEquals(definingClassDefinition.type, targetSpecial.getHolderType());
       assertEquals(targetSpecial.getReference(), targetSuper.getReference());
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java
index 0a20eab..1a72976 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java
@@ -106,15 +106,15 @@
 
     // Verify that the resolved method is accessible (it is public).
     assertEquals(
-        OptionalBool.TRUE, resolutionResult.isAccessibleFrom(callerClassDefinition, appInfo));
+        OptionalBool.TRUE, resolutionResult.isAccessibleFrom(callerClassDefinition, appView));
 
     // Verify that looking up the dispatch target returns the defining method.
     DexClassAndMethod targetSpecial =
-        resolutionResult.lookupInvokeSpecialTarget(callerClassDefinition, appInfo);
+        resolutionResult.lookupInvokeSpecialTarget(callerClassDefinition, appView);
     assertEquals(definingClassDefinition.type, targetSpecial.getHolderType());
 
     DexClassAndMethod targetSuper =
-        resolutionResult.lookupInvokeSuperTarget(callerClassDefinition, appInfo);
+        resolutionResult.lookupInvokeSuperTarget(callerClassDefinition, appView);
     assertEquals(targetSpecial.getReference(), targetSuper.getReference());
   }
 
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessTest.java
index 9216e56..d0b2806 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessTest.java
@@ -75,7 +75,7 @@
         appInfo.definitionFor(buildType(B.class, appInfo.dexItemFactory())).asProgramClass();
     DexMethod bar = buildMethod(A.class.getDeclaredMethod("bar"), appInfo.dexItemFactory());
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(bar);
-    assertEquals(OptionalBool.of(inSameNest), resolutionResult.isAccessibleFrom(bClass, appInfo));
+    assertEquals(OptionalBool.of(inSameNest), resolutionResult.isAccessibleFrom(bClass, appView));
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessWithIntermediateClassTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessWithIntermediateClassTest.java
index 235b3d1..3d09c38 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessWithIntermediateClassTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessWithIntermediateClassTest.java
@@ -74,7 +74,7 @@
         appInfo.definitionFor(buildType(B.class, appInfo.dexItemFactory())).asProgramClass();
     DexMethod bar = buildMethod(A.class.getDeclaredMethod("bar"), appInfo.dexItemFactory());
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(bar);
-    assertEquals(OptionalBool.of(inSameNest), resolutionResult.isAccessibleFrom(bClass, appInfo));
+    assertEquals(OptionalBool.of(inSameNest), resolutionResult.isAccessibleFrom(bClass, appView));
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessTest.java
index d86d67d..b2f74fb 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessTest.java
@@ -77,7 +77,7 @@
         appInfo.definitionFor(buildType(B.class, appInfo.dexItemFactory())).asProgramClass();
     DexMethod bar = buildMethod(A.class.getDeclaredMethod("bar"), appInfo.dexItemFactory());
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(bar);
-    assertEquals(OptionalBool.of(inSameNest), resolutionResult.isAccessibleFrom(bClass, appInfo));
+    assertEquals(OptionalBool.of(inSameNest), resolutionResult.isAccessibleFrom(bClass, appView));
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessWithIntermediateClassTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessWithIntermediateClassTest.java
index 6c3e040..96fe463 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessWithIntermediateClassTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessWithIntermediateClassTest.java
@@ -73,7 +73,7 @@
         appInfo.definitionFor(buildType(B.class, appInfo.dexItemFactory())).asProgramClass();
     DexMethod bar = buildMethod(A.class.getDeclaredMethod("bar"), appInfo.dexItemFactory());
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(bar);
-    assertEquals(OptionalBool.of(inSameNest), resolutionResult.isAccessibleFrom(bClass, appInfo));
+    assertEquals(OptionalBool.of(inSameNest), resolutionResult.isAccessibleFrom(bClass, appView));
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/resolution/access/SelfVirtualMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/SelfVirtualMethodAccessTest.java
index aff8d60..d6f4618 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/SelfVirtualMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/SelfVirtualMethodAccessTest.java
@@ -60,7 +60,7 @@
         appInfo.definitionFor(buildType(A.class, appInfo.dexItemFactory())).asProgramClass();
     DexMethod bar = buildMethod(A.class.getDeclaredMethod("bar"), appInfo.dexItemFactory());
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(bar);
-    assertEquals(OptionalBool.TRUE, resolutionResult.isAccessibleFrom(aClass, appInfo));
+    assertEquals(OptionalBool.TRUE, resolutionResult.isAccessibleFrom(aClass, appView));
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/resolution/access/indirectfield/IndirectFieldAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/indirectfield/IndirectFieldAccessTest.java
index 99b02c3..f2b60f2 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/indirectfield/IndirectFieldAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/indirectfield/IndirectFieldAccessTest.java
@@ -67,7 +67,7 @@
             appInfo.dexItemFactory());
     FieldResolutionResult resolutionResult = appInfo.resolveField(f);
     assertTrue(resolutionResult.isSingleFieldResolutionResult());
-    assertEquals(OptionalBool.TRUE, resolutionResult.isAccessibleFrom(barMethod, appInfo));
+    assertEquals(OptionalBool.TRUE, resolutionResult.isAccessibleFrom(barMethod, appView));
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/resolution/access/indirectmethod/IndirectMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/indirectmethod/IndirectMethodAccessTest.java
index 528b8aa..0599233 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/indirectmethod/IndirectMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/indirectmethod/IndirectMethodAccessTest.java
@@ -67,7 +67,7 @@
     DexMethod bar = buildMethod(B.class.getMethod("foo"), appInfo.dexItemFactory());
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(bar);
     assertEquals(
-        OptionalBool.TRUE, resolutionResult.isAccessibleForVirtualDispatchFrom(cClass, appInfo));
+        OptionalBool.TRUE, resolutionResult.isAccessibleForVirtualDispatchFrom(cClass, appView));
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificSingleLibraryPartialTest.java b/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificSingleLibraryPartialTest.java
index e2fd96f..0aac67a 100644
--- a/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificSingleLibraryPartialTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificSingleLibraryPartialTest.java
@@ -95,7 +95,7 @@
                 (resolution.getResolvedHolder().isProgramClass() ? "Program: " : "Library: ")
                     + resolution.getResolvedMethod().getReference().toString());
           } else {
-            assertTrue(result.isNoSuchMethodErrorResult(mainClass, appInfo));
+            assertTrue(result.isNoSuchMethodErrorResult(mainClass, appView));
             methodResults.add(typeName(NoSuchMethodError.class));
             result
                 .asFailedResolution()
diff --git a/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificSingleProgramPartialTest.java b/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificSingleProgramPartialTest.java
index 3d22646..ca57c2f 100644
--- a/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificSingleProgramPartialTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificSingleProgramPartialTest.java
@@ -93,7 +93,7 @@
                 (resolution.getResolvedHolder().isProgramClass() ? "Program: " : "Library: ")
                     + resolution.getResolvedMethod().getReference().toString());
           } else {
-            assertTrue(result.isNoSuchMethodErrorResult(mainClass, appInfo));
+            assertTrue(result.isNoSuchMethodErrorResult(mainClass, appView));
             methodResults.add(typeName(NoSuchMethodError.class));
             result
                 .asFailedResolution()
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java
index 200a3ad..91562c7 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java
@@ -67,7 +67,7 @@
         appInfo.resolveMethodOnInterfaceLegacy(method.holder, method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultSuccess());
     Set<String> targets = new HashSet<>();
     lookupResult.forEach(
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodLambdaTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodLambdaTest.java
index 3f9476e..cf0e5f4 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodLambdaTest.java
@@ -66,7 +66,7 @@
         appInfo.resolveMethodOnInterfaceLegacy(method.holder, method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultSuccess());
     Set<String> targets = new HashSet<>();
     lookupResult.forEach(
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultWithoutTopTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultWithoutTopTest.java
index 1579e3f..49ec9ea 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultWithoutTopTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultWithoutTopTest.java
@@ -66,7 +66,7 @@
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOnInterfaceHolderLegacy(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultSuccess());
     Set<String> targets = new HashSet<>();
     lookupResult.forEach(
@@ -115,7 +115,7 @@
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOnInterfaceHolderLegacy(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultSuccess());
     Set<String> targets = new HashSet<>();
     lookupResult.forEach(
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DuplicateImportsTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DuplicateImportsTest.java
index 56e1a51..62c0d0c 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DuplicateImportsTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DuplicateImportsTest.java
@@ -66,7 +66,7 @@
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOnInterfaceHolderLegacy(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultSuccess());
     Set<String> targets = new HashSet<>();
     lookupResult.forEach(
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java
index 667b6c7..3c6d716 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java
@@ -57,7 +57,7 @@
         () ->
             appInfo
                 .resolveMethodOnInterfaceHolderLegacy(method)
-                .lookupVirtualDispatchTargets(context, appInfo));
+                .lookupVirtualDispatchTargets(context, appView));
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/LambdaMultipleInterfacesTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/LambdaMultipleInterfacesTest.java
index c01dc38..ba0ffb3 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/LambdaMultipleInterfacesTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/LambdaMultipleInterfacesTest.java
@@ -66,7 +66,7 @@
         appInfo.resolveMethodOnInterfaceLegacy(method.holder, method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultSuccess());
     Set<String> targets = new HashSet<>();
     lookupResult.forEach(
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/MultipleImplementsTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/MultipleImplementsTest.java
index 5dad4b6..455c14d 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/MultipleImplementsTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/MultipleImplementsTest.java
@@ -64,7 +64,7 @@
         appInfo.resolveMethodOnInterfaceLegacy(method.holder, method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultSuccess());
     Set<String> targets = new HashSet<>();
     lookupResult.forEach(
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SimpleInterfaceInvokeTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SimpleInterfaceInvokeTest.java
index fab0e87..75af94a 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SimpleInterfaceInvokeTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SimpleInterfaceInvokeTest.java
@@ -64,7 +64,7 @@
         appInfo.resolveMethodOnInterfaceLegacy(method.holder, method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultSuccess());
     assertFalse(lookupResult.asLookupResultSuccess().hasLambdaTargets());
     Set<String> targets = new HashSet<>();
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubInterfaceOverridesTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubInterfaceOverridesTest.java
index 4fb6ea3..c2727e5 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubInterfaceOverridesTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubInterfaceOverridesTest.java
@@ -66,7 +66,7 @@
         appInfo.resolveMethodOnInterfaceLegacy(method.holder, method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultSuccess());
     assertFalse(lookupResult.asLookupResultSuccess().hasLambdaTargets());
     Set<String> targets = new HashSet<>();
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeMissingOverridesTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeMissingOverridesTest.java
index 5563d1c..a0c812a 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeMissingOverridesTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeMissingOverridesTest.java
@@ -63,7 +63,7 @@
         appInfo.resolveMethodOnInterfaceLegacy(method.holder, method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultSuccess());
     assertFalse(lookupResult.asLookupResultSuccess().hasLambdaTargets());
     Set<String> targets = new HashSet<>();
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeOverridesTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeOverridesTest.java
index edc31a9..b5d846d 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeOverridesTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeOverridesTest.java
@@ -63,7 +63,7 @@
         appInfo.resolveMethodOnInterfaceLegacy(method.holder, method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultSuccess());
     assertFalse(lookupResult.asLookupResultSuccess().hasLambdaTargets());
     Set<String> targets = new HashSet<>();
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateClasspathWidenTest.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateClasspathWidenTest.java
index 8a36359..5da6e09 100644
--- a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateClasspathWidenTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateClasspathWidenTest.java
@@ -77,7 +77,7 @@
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Abstract.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultSuccess());
     Set<String> targets = new HashSet<>();
     lookupResult.forEach(
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateInitialResolutionHolderTest.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateInitialResolutionHolderTest.java
index ae99bf7..68bcc86 100644
--- a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateInitialResolutionHolderTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateInitialResolutionHolderTest.java
@@ -66,7 +66,7 @@
         appInfo.definitionForProgramType(
             buildType(
                 Reference.classFromDescriptor(descriptor(Main.class)), appInfo.dexItemFactory()));
-    assertEquals(OptionalBool.FALSE, resolutionResult.isAccessibleFrom(programClass, appInfo));
+    assertEquals(OptionalBool.FALSE, resolutionResult.isAccessibleFrom(programClass, appView));
     DexType cType =
         buildType(Reference.classFromDescriptor(newCDescriptor), appInfo.dexItemFactory());
     DexProgramClass cClass = appView.definitionForProgramType(cType);
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryTest.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryTest.java
index d6c548d..7513d4d 100644
--- a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryTest.java
@@ -62,7 +62,7 @@
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultSuccess());
     Set<String> targets = new HashSet<>();
     lookupResult.forEach(
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryWithNarrowingTest.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryWithNarrowingTest.java
index 7cbbb62..e4da615 100644
--- a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryWithNarrowingTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryWithNarrowingTest.java
@@ -66,7 +66,7 @@
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultSuccess());
     Set<String> targets = new HashSet<>();
     lookupResult.forEach(
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethod2Test.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethod2Test.java
index 4bf6dc7..10e3530 100644
--- a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethod2Test.java
+++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethod2Test.java
@@ -76,7 +76,7 @@
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultSuccess());
     Set<String> targets = new HashSet<>();
     lookupResult.forEach(
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethodTest.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethodTest.java
index 9984687..ead6a82 100644
--- a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethodTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethodTest.java
@@ -71,7 +71,7 @@
     DexProgramClass context =
         appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory()));
     assertTrue(resolutionResult.isAccessibleFrom(context, appView).isFalse());
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultFailure());
   }
 
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/WidenAccessOutsidePackageTest.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/WidenAccessOutsidePackageTest.java
index 8745ee1..21c346c 100644
--- a/src/test/java/com/android/tools/r8/resolution/packageprivate/WidenAccessOutsidePackageTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/WidenAccessOutsidePackageTest.java
@@ -63,7 +63,7 @@
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertFalse(lookupResult.asLookupResultSuccess().hasLambdaTargets());
     Set<String> targets = new HashSet<>();
     lookupResult
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 42733a1..788e55b 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
@@ -136,7 +136,7 @@
     DexProgramClass upperBound = appView.definitionForProgramType(typeA);
     DexProgramClass lowerBound = appView.definitionForProgramType(typeC);
     LookupResult lookupResult =
-        resolution.lookupVirtualDispatchTargets(context, appInfo, upperBound, lowerBound);
+        resolution.lookupVirtualDispatchTargets(context, appView, upperBound, lowerBound);
     Set<DexMethod> expected = Sets.newIdentityHashSet();
     expected.add(fooA);
     expected.add(fooB);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/AbstractInMiddleTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/AbstractInMiddleTest.java
index ac915f2..f9c5927 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/AbstractInMiddleTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/AbstractInMiddleTest.java
@@ -62,7 +62,7 @@
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultSuccess());
     Set<String> targets = new HashSet<>();
     lookupResult.forEach(
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceSubTypeTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceSubTypeTest.java
index efed293..a64145e 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceSubTypeTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceSubTypeTest.java
@@ -65,7 +65,7 @@
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultSuccess());
     Set<String> targets = new HashSet<>();
     lookupResult.forEach(
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceTest.java
index bfe1d1b..c227f79 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceTest.java
@@ -65,7 +65,7 @@
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultSuccess());
     Set<String> targets = new HashSet<>();
     lookupResult.forEach(
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultWithoutTopTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultWithoutTopTest.java
index 57b31a1..d9f121b 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultWithoutTopTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultWithoutTopTest.java
@@ -66,7 +66,7 @@
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultSuccess());
     Set<String> targets = new HashSet<>();
     lookupResult.forEach(
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvokeVirtualToInterfaceDefinitionTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvokeVirtualToInterfaceDefinitionTest.java
index 64e046a..edb66c9 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvokeVirtualToInterfaceDefinitionTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvokeVirtualToInterfaceDefinitionTest.java
@@ -65,7 +65,7 @@
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultSuccess());
     Set<String> targets = new HashSet<>();
     lookupResult.forEach(
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 a96b487..d2dae9b 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
@@ -94,7 +94,7 @@
                 .appInfo()
                 .definitionForWithoutExistenceAssert(
                     buildType(Unrelated.class, appInfo.dexItemFactory())));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultSuccess());
     LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
     Set<String> targets = new HashSet<>();
@@ -245,7 +245,7 @@
     LookupResult lookupResult =
         resolutionResult.lookupVirtualDispatchTargets(
             classB,
-            appInfo,
+            appView,
             (type, subTypeConsumer, callSiteConsumer) -> {
               if (type == typeB) {
                 subTypeConsumer.accept(classB);
@@ -276,7 +276,7 @@
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Unrelated.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultSuccess());
     LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
     Set<String> targets = new HashSet<>();
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateChainTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateChainTest.java
index e1dad43..e1b4be1 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateChainTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateChainTest.java
@@ -65,7 +65,7 @@
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(TopRunner.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultSuccess());
     Set<String> targets = new HashSet<>();
     lookupResult.forEach(
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideTest.java
index ad30339..4b5f8d2 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideTest.java
@@ -72,7 +72,7 @@
     DexProgramClass context =
         appView.definitionForProgramType(
             buildType(ViewModelRunner.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultSuccess());
     Set<String> targets = new HashSet<>();
     lookupResult.forEach(
@@ -122,7 +122,7 @@
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultFailure());
   }
 
@@ -175,7 +175,7 @@
     DexProgramClass context =
         appView.definitionForProgramType(
             buildType(ViewModelRunnerWithCast.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultSuccess());
     Set<String> targets = new HashSet<>();
     lookupResult.forEach(
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PrivateOverrideOfVirtualTargetTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PrivateOverrideOfVirtualTargetTest.java
index 536e845..a076a58 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PrivateOverrideOfVirtualTargetTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PrivateOverrideOfVirtualTargetTest.java
@@ -64,7 +64,7 @@
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(B.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultSuccess());
     Set<String> targets = new HashSet<>();
     lookupResult.forEach(
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedDifferentPackageLookupTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedDifferentPackageLookupTest.java
index f7bff16..4ec5810 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedDifferentPackageLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedDifferentPackageLookupTest.java
@@ -57,7 +57,7 @@
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     // TODO(b/173363527): Should be an error.
     assertTrue(lookupResult.isLookupResultSuccess());
   }
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedSamePackageLookupTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedSamePackageLookupTest.java
index fcfd723..cc72a23 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedSamePackageLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedSamePackageLookupTest.java
@@ -53,7 +53,7 @@
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultSuccess());
   }
 
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java
index 56fa675..8aef8bb 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java
@@ -66,7 +66,7 @@
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOnInterfaceHolderLegacy(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
-    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+    LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
     assertTrue(lookupResult.isLookupResultSuccess());
     assertFalse(lookupResult.asLookupResultSuccess().hasLambdaTargets());
     Set<String> targets = new HashSet<>();
diff --git a/src/test/java/com/android/tools/r8/retrace/OverloadsWithoutLineNumberTest.java b/src/test/java/com/android/tools/r8/retrace/OverloadsWithoutLineNumberTest.java
index cbe2680..b1c1b96 100644
--- a/src/test/java/com/android/tools/r8/retrace/OverloadsWithoutLineNumberTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/OverloadsWithoutLineNumberTest.java
@@ -66,7 +66,7 @@
     assertEquals(
         "\tat "
             + typeName(ClassWithOverload.class)
-            + ".void test(int)(OverloadsWithoutLineNumberTest.java:0)",
+            + ".void test(int)(OverloadsWithoutLineNumberTest.java)",
         box.get().get(1));
   }
 
diff --git a/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndNoLineTableTestRunner.java b/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndNoLineTableTestRunner.java
index a89c8a4..8258c8b 100644
--- a/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndNoLineTableTestRunner.java
+++ b/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndNoLineTableTestRunner.java
@@ -88,8 +88,7 @@
                     b -> b.setLineNumber(10),
                     b -> {
                       if (parameters.isDexRuntime()) {
-                        // TODO(b/255705077): Should not have position 0.
-                        b.setLineNumber(0);
+                        b.setLineNumber(-1);
                       }
                     })
                 .build())
@@ -103,8 +102,7 @@
                     b -> b.setLineNumber(15),
                     b -> {
                       if (parameters.isDexRuntime()) {
-                        // TODO(b/255705077): Should not have position 0.
-                        b.setLineNumber(0);
+                        b.setLineNumber(-1);
                       }
                     })
                 .build())
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineFileNameStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineFileNameStackTrace.java
index 048b941..b8243aa 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineFileNameStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineFileNameStackTrace.java
@@ -21,7 +21,7 @@
   public List<String> retracedStackTrace() {
     return Arrays.asList(
         "Exception in thread \"main\" java.lang.NullPointerException",
-        "\tat foo.Bar$Baz.baz(Bar.java:0)",
+        "\tat foo.Bar$Baz.baz(Bar.java)",
         "\tat Foo$Bar.bar(Foo.java:2)",
         "\tat com.android.tools.r8.naming.retrace.Main$Foo.method1(Main.java:8)",
         "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java:7)");
@@ -31,7 +31,7 @@
   public List<String> retraceVerboseStackTrace() {
     return Arrays.asList(
         "Exception in thread \"main\" java.lang.NullPointerException",
-        "\tat foo.Bar$Baz.void baz(long)(Bar.java:0)",
+        "\tat foo.Bar$Baz.void baz(long)(Bar.java)",
         "\tat Foo$Bar.void bar(int)(Foo.java:2)",
         "\tat com.android.tools.r8.naming.retrace.Main$Foo"
             + ".void method1(java.lang.String)(Main.java:8)",
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineFileNameWithInnerClassesStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineFileNameWithInnerClassesStackTrace.java
index 177eb8a..1c53b40 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineFileNameWithInnerClassesStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineFileNameWithInnerClassesStackTrace.java
@@ -21,7 +21,7 @@
   public List<String> retracedStackTrace() {
     return Arrays.asList(
         "Exception in thread \"main\" java.lang.NullPointerException",
-        "\tat foo.Bar$Baz$Qux.baz(Bar.java:0)",
+        "\tat foo.Bar$Baz$Qux.baz(Bar.java)",
         "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java:7)");
   }
 
@@ -29,7 +29,7 @@
   public List<String> retraceVerboseStackTrace() {
     return Arrays.asList(
         "Exception in thread \"main\" java.lang.NullPointerException",
-        "\tat foo.Bar$Baz$Qux.void baz(long)(Bar.java:0)",
+        "\tat foo.Bar$Baz$Qux.void baz(long)(Bar.java)",
         "\tat com.android.tools.r8.naming.retrace.Main.void main(java.lang.String[])(Main.java:7)");
   }
 
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineNoLineNumberStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineNoLineNumberStackTrace.java
index 763ec80..28e94c5 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineNoLineNumberStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineNoLineNumberStackTrace.java
@@ -21,20 +21,20 @@
   public List<String> retracedStackTrace() {
     return Arrays.asList(
         "Exception in thread \"main\" java.lang.NullPointerException",
-        "\tat com.android.tools.r8.naming.retrace.Main.method3(Main.java:0)",
-        "\tat com.android.tools.r8.naming.retrace.Main.method2(Main.java:0)",
-        "\tat com.android.tools.r8.naming.retrace.Main.method1(Main.java:0)",
-        "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java:0)");
+        "\tat com.android.tools.r8.naming.retrace.Main.method3(Main.java)",
+        "\tat com.android.tools.r8.naming.retrace.Main.method2(Main.java)",
+        "\tat com.android.tools.r8.naming.retrace.Main.method1(Main.java)",
+        "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java)");
   }
 
   @Override
   public List<String> retraceVerboseStackTrace() {
     return Arrays.asList(
         "Exception in thread \"main\" java.lang.NullPointerException",
-        "\tat com.android.tools.r8.naming.retrace.Main.void method3(long)(Main.java:0)",
-        "\tat com.android.tools.r8.naming.retrace.Main.void method2(int)(Main.java:0)",
-        "\tat com.android.tools.r8.naming.retrace.Main.void method1(java.lang.String)(Main.java:0)",
-        "\tat com.android.tools.r8.naming.retrace.Main.void main(java.lang.String[])(Main.java:0)");
+        "\tat com.android.tools.r8.naming.retrace.Main.void method3(long)(Main.java)",
+        "\tat com.android.tools.r8.naming.retrace.Main.void method2(int)(Main.java)",
+        "\tat com.android.tools.r8.naming.retrace.Main.void method1(java.lang.String)(Main.java)",
+        "\tat com.android.tools.r8.naming.retrace.Main.void main(java.lang.String[])(Main.java)");
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/NoObfuscationRangeMappingWithStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/NoObfuscationRangeMappingWithStackTrace.java
index 8ec36a8..67a0d43 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/NoObfuscationRangeMappingWithStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/NoObfuscationRangeMappingWithStackTrace.java
@@ -26,8 +26,8 @@
         "Exception in thread \"main\" java.lang.NullPointerException",
         "\tat com.android.tools.r8.naming.retrace.Main.foo(Main.java:1)",
         "\tat com.android.tools.r8.naming.retrace.Main.bar(Main.java:3)",
-        "\tat com.android.tools.r8.naming.retrace.Main.baz(Main.java:0)",
-        "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java:0)");
+        "\tat com.android.tools.r8.naming.retrace.Main.baz(Main.java)",
+        "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java)");
   }
 
   @Override
@@ -36,8 +36,8 @@
         "Exception in thread \"main\" java.lang.NullPointerException",
         "\tat com.android.tools.r8.naming.retrace.Main.void foo(long)(Main.java:1)",
         "\tat com.android.tools.r8.naming.retrace.Main.void bar(int)(Main.java:3)",
-        "\tat com.android.tools.r8.naming.retrace.Main.void baz()(Main.java:0)",
-        "\tat com.android.tools.r8.naming.retrace.Main.void main(java.lang.String[])(Main.java:0)");
+        "\tat com.android.tools.r8.naming.retrace.Main.void baz()(Main.java)",
+        "\tat com.android.tools.r8.naming.retrace.Main.void main(java.lang.String[])(Main.java)");
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/PreambleLineNumberStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/PreambleLineNumberStackTrace.java
index 98a6ed6..bbd72bd 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/PreambleLineNumberStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/PreambleLineNumberStackTrace.java
@@ -33,7 +33,7 @@
     return Arrays.asList(
         "Exception in thread \"main\" java.lang.NullPointerException",
         "  at kotlin.ResultKt.createFailure(Result.kt)",
-        "  at kotlin.ResultKt.createFailure(Result.kt:0)",
+        "  at kotlin.ResultKt.createFailure(Result.kt)",
         "  at kotlin.ResultKt.createFailure(Result.kt:122)",
         "  at kotlin.ResultKt.createFailure(Result.kt:124)");
   }
@@ -43,7 +43,7 @@
     return Arrays.asList(
         "Exception in thread \"main\" java.lang.NullPointerException",
         "  at kotlin.ResultKt.void createFailure(java.lang.Throwable)(Result.kt)",
-        "  at kotlin.ResultKt.void createFailure(java.lang.Throwable)(Result.kt:0)",
+        "  at kotlin.ResultKt.void createFailure(java.lang.Throwable)(Result.kt)",
         "  at kotlin.ResultKt.void createFailure(java.lang.Throwable)(Result.kt:122)",
         "  at kotlin.ResultKt.void createFailure(java.lang.Throwable)(Result.kt:124)");
   }
diff --git a/src/test/java/com/android/tools/r8/shaking/ForwardingConstructorShakingOnDexTest.java b/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorShakingOnDexTest.java
similarity index 96%
rename from src/test/java/com/android/tools/r8/shaking/ForwardingConstructorShakingOnDexTest.java
rename to src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorShakingOnDexTest.java
index 34fe591..96e9e92 100644
--- a/src/test/java/com/android/tools/r8/shaking/ForwardingConstructorShakingOnDexTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorShakingOnDexTest.java
@@ -1,8 +1,8 @@
-// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-package com.android.tools.r8.shaking;
+package com.android.tools.r8.shaking.constructor;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
diff --git a/src/test/java/com/android/tools/r8/shaking/ForwardingConstructorShakingOnDexWithClassMergingTest.java b/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorShakingOnDexWithClassMergingTest.java
similarity index 97%
rename from src/test/java/com/android/tools/r8/shaking/ForwardingConstructorShakingOnDexWithClassMergingTest.java
rename to src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorShakingOnDexWithClassMergingTest.java
index 587abff..fddfa66 100644
--- a/src/test/java/com/android/tools/r8/shaking/ForwardingConstructorShakingOnDexWithClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorShakingOnDexWithClassMergingTest.java
@@ -1,8 +1,8 @@
-// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-package com.android.tools.r8.shaking;
+package com.android.tools.r8.shaking.constructor;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
diff --git a/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java b/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java
new file mode 100644
index 0000000..534bbb9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java
@@ -0,0 +1,75 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking.constructor;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ForwardingConstructorUsedFromPlatformShakingOnDexTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(Main.class, MyFragment.class)
+        .addLibraryClasses(Fragment.class)
+        .addLibraryFiles(parameters.getDefaultRuntimeLibrary())
+        .addKeepMainRule(Main.class)
+        .enableNeverClassInliningAnnotations()
+        .setMinApi(parameters)
+        .compile()
+        .inspect(this::inspect)
+        .addBootClasspathClasses(Fragment.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Instantiating");
+  }
+
+  private void inspect(CodeInspector inspector) {
+    ClassSubject myFragmentClassSubject = inspector.clazz(MyFragment.class);
+    assertThat(myFragmentClassSubject, isPresent());
+    assertThat(myFragmentClassSubject.init(), isPresent());
+  }
+
+  public abstract static class Fragment {
+
+    public Fragment newInstance() throws Exception {
+      System.out.println("Instantiating");
+      return getClass().getDeclaredConstructor().newInstance();
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) throws Exception {
+      new MyFragment().newInstance();
+    }
+  }
+
+  @NeverClassInline
+  public static class MyFragment extends Fragment {
+
+    public MyFragment() {}
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java b/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java
index fa1dd30..53c02be 100644
--- a/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java
+++ b/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java
@@ -12,7 +12,6 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.profile.art.ArtProfileBuilderUtils.SyntheticToSyntheticContextGeneralization;
 import com.android.tools.r8.startup.profile.ExternalStartupItem;
 import com.android.tools.r8.startup.utils.StartupTestingUtils;
 import com.android.tools.r8.utils.AndroidApiLevel;
@@ -55,9 +54,7 @@
         .compile()
         .addRunClasspathFiles(StartupTestingUtils.getAndroidUtilLog(temp))
         .run(parameters.getRuntime(), Main.class)
-        .apply(
-            StartupTestingUtils.removeStartupListFromStdout(
-                startupList::add, SyntheticToSyntheticContextGeneralization.createForR8()))
+        .apply(StartupTestingUtils.removeStartupListFromStdout(startupList::add))
         .assertSuccessWithOutputLines(getExpectedOutput());
 
     testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java b/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java
index 8399d05..d53257c 100644
--- a/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java
+++ b/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java
@@ -9,7 +9,6 @@
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.profile.art.ArtProfileBuilderUtils.SyntheticToSyntheticContextGeneralization;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.startup.profile.ExternalStartupClass;
 import com.android.tools.r8.startup.profile.ExternalStartupItem;
@@ -50,8 +49,6 @@
   public void test() throws Exception {
     Path out = temp.newFolder().toPath().resolve("out.txt").toAbsolutePath();
     Set<ExternalStartupItem> startupList = new LinkedHashSet<>();
-    SyntheticToSyntheticContextGeneralization syntheticGeneralization =
-        SyntheticToSyntheticContextGeneralization.createForD8();
     testForD8(parameters.getBackend())
         .addInnerClasses(getClass())
         .applyIf(
@@ -68,11 +65,8 @@
         .run(parameters.getRuntime(), Main.class, Boolean.toString(logcat), out.toString())
         .applyIf(
             logcat,
-            StartupTestingUtils.removeStartupListFromStdout(
-                startupList::add, syntheticGeneralization),
-            runResult ->
-                StartupTestingUtils.readStartupListFromFile(
-                    out, startupList::add, syntheticGeneralization))
+            StartupTestingUtils.removeStartupListFromStdout(startupList::add),
+            runResult -> StartupTestingUtils.readStartupListFromFile(out, startupList::add))
         .assertSuccessWithOutputLines(getExpectedOutput());
     assertEquals(getExpectedStartupList(), startupList);
   }
diff --git a/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java b/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
index 742361e..92d3129 100644
--- a/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
+++ b/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
@@ -14,23 +14,23 @@
 import com.android.tools.r8.D8TestCompileResult;
 import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompileResult;
 import com.android.tools.r8.TestCompilerBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.ir.desugar.LambdaClass;
-import com.android.tools.r8.profile.art.ArtProfileBuilderUtils.SyntheticToSyntheticContextGeneralization;
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.startup.profile.ExternalStartupClass;
 import com.android.tools.r8.startup.profile.ExternalStartupItem;
 import com.android.tools.r8.startup.profile.ExternalStartupMethod;
-import com.android.tools.r8.startup.profile.ExternalSyntheticStartupMethod;
 import com.android.tools.r8.startup.utils.MixedSectionLayoutInspector;
 import com.android.tools.r8.startup.utils.StartupTestingUtils;
 import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.TypeReferenceUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -38,6 +38,7 @@
 import com.google.common.collect.ImmutableSet;
 import java.nio.file.Path;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
@@ -51,6 +52,11 @@
 @RunWith(Parameterized.class)
 public class StartupSyntheticPlacementTest extends TestBase {
 
+  private enum Compiler {
+    D8,
+    R8
+  }
+
   @Parameter(0)
   public TestParameters parameters;
 
@@ -103,9 +109,7 @@
         .compile()
         .addRunClasspathFiles(StartupTestingUtils.getAndroidUtilLog(temp))
         .run(parameters.getRuntime(), Main.class, Boolean.toString(useLambda))
-        .apply(
-            StartupTestingUtils.removeStartupListFromStdout(
-                startupList::add, SyntheticToSyntheticContextGeneralization.createForD8()))
+        .apply(StartupTestingUtils.removeStartupListFromStdout(startupList::add))
         .assertSuccessWithOutputLines(getExpectedOutput())
         .apply(
             runResult ->
@@ -123,6 +127,7 @@
         .compile()
         .inspectMultiDex(
             r8CompileResult.writeProguardMap(), this::inspectPrimaryDex, this::inspectSecondaryDex)
+        .apply(this::checkCompleteness)
         .run(parameters.getRuntime(), Main.class, Boolean.toString(useLambda))
         .assertSuccessWithOutputLines(getExpectedOutput());
   }
@@ -144,9 +149,7 @@
     instrumentationCompileResult
         .addRunClasspathFiles(StartupTestingUtils.getAndroidUtilLog(temp))
         .run(parameters.getRuntime(), Main.class, Boolean.toString(useLambda))
-        .apply(
-            StartupTestingUtils.removeStartupListFromStdout(
-                startupList::add, SyntheticToSyntheticContextGeneralization.createForR8()))
+        .apply(StartupTestingUtils.removeStartupListFromStdout(startupList::add))
         .assertSuccessWithOutputLines(getExpectedOutput())
         .apply(
             runResult ->
@@ -164,11 +167,17 @@
         .setMinApi(parameters)
         .compile()
         .inspectMultiDex(this::inspectPrimaryDex, this::inspectSecondaryDex)
+        .apply(this::checkCompleteness)
         .run(parameters.getRuntime(), Main.class, Boolean.toString(useLambda))
-        .applyIf(
-            enableStartupCompletenessCheck && useLambda,
-            runResult -> runResult.assertFailureWithErrorThatThrows(NullPointerException.class),
-            runResult -> runResult.assertSuccessWithOutputLines(getExpectedOutput()));
+        .assertSuccessWithOutputLines(getExpectedOutput());
+  }
+
+  private void checkCompleteness(TestCompileResult<?, ?> compileResult) throws Exception {
+    if (enableStartupCompletenessCheck && !useLambda) {
+      compileResult
+          .run(parameters.getRuntime(), Main.class, "true")
+          .assertFailureWithErrorThatThrows(NullPointerException.class);
+    }
   }
 
   private void configureStartupOptions(
@@ -185,7 +194,7 @@
               options
                   .getTestingOptions()
                   .setMixedSectionLayoutStrategyInspector(
-                      getMixedSectionLayoutInspector(inspector));
+                      getMixedSectionLayoutInspector(inspector, testBuilder.isD8TestBuilder()));
             })
         .apply(ignore -> StartupTestingUtils.setStartupConfiguration(testBuilder, startupList));
   }
@@ -215,10 +224,34 @@
                 Reference.methodFromMethod(B.class.getDeclaredMethod("b", boolean.class)))
             .build());
     if (useLambda) {
+      builder.add(
+          ExternalStartupMethod.builder()
+              .setMethodReference(
+                  Reference.methodFromMethod(B.class.getDeclaredMethod("synthesize")))
+              .build());
       if (isStartupListForOriginalApp) {
+        ClassReference syntheticLambdaClassReference = getSyntheticLambdaClassReference();
         builder.add(
-            ExternalSyntheticStartupMethod.builder()
-                .setSyntheticContextReference(Reference.classFromClass(B.class))
+            ExternalStartupClass.builder().setClassReference(syntheticLambdaClassReference).build(),
+            ExternalStartupMethod.builder()
+                .setMethodReference(
+                    MethodReferenceUtils.instanceConstructor(syntheticLambdaClassReference))
+                .build(),
+            ExternalStartupMethod.builder()
+                .setMethodReference(
+                    Reference.method(
+                        syntheticLambdaClassReference,
+                        "accept",
+                        Collections.singletonList(Reference.classFromClass(Object.class)),
+                        TypeReferenceUtils.getVoidType()))
+                .build(),
+            ExternalStartupMethod.builder()
+                .setMethodReference(
+                    Reference.method(
+                        Reference.classFromClass(B.class),
+                        "lambda$synthesize$0",
+                        Collections.singletonList(Reference.classFromClass(Object.class)),
+                        TypeReferenceUtils.getVoidType()))
                 .build());
       } else {
         ClassSubject bClassSubject = inspector.clazz(B.class);
@@ -266,7 +299,8 @@
       builder.add(
           ExternalStartupMethod.builder()
               .setMethodReference(
-                  Reference.methodFromMethod(B.class.getDeclaredMethod("lambda$b$0", Object.class)))
+                  Reference.methodFromMethod(
+                      B.class.getDeclaredMethod("lambda$synthesize$0", Object.class)))
               .build());
     }
     builder.add(
@@ -278,7 +312,7 @@
   }
 
   private List<ClassReference> getExpectedClassDataLayout(
-      CodeInspector inspector, int virtualFile) {
+      CodeInspector inspector, boolean isD8, int virtualFile) {
     ClassSubject syntheticLambdaClassSubject = inspector.clazz(getSyntheticLambdaClassReference());
 
     // The synthetic lambda should only be placed alongside its synthetic context (B) if it is used.
@@ -289,10 +323,17 @@
           Reference.classFromClass(Main.class),
           Reference.classFromClass(A.class),
           Reference.classFromClass(B.class));
-      if (useLambda) {
-        layoutBuilder.add(syntheticLambdaClassSubject.getFinalReference());
+      if (isD8) {
+        if (useLambda) {
+          layoutBuilder.add(syntheticLambdaClassSubject.getFinalReference());
+        }
+        layoutBuilder.add(Reference.classFromClass(C.class));
+      } else {
+        layoutBuilder.add(Reference.classFromClass(C.class));
+        if (useLambda) {
+          layoutBuilder.add(syntheticLambdaClassSubject.getFinalReference());
+        }
       }
-      layoutBuilder.add(Reference.classFromClass(C.class));
     }
     if (!useLambda) {
       if (!enableMinimalStartupDex || virtualFile == 1) {
@@ -302,12 +343,14 @@
     return layoutBuilder.build();
   }
 
-  private MixedSectionLayoutInspector getMixedSectionLayoutInspector(CodeInspector inspector) {
+  private MixedSectionLayoutInspector getMixedSectionLayoutInspector(
+      CodeInspector inspector, boolean isD8) {
     return new MixedSectionLayoutInspector() {
       @Override
       public void inspectClassDataLayout(int virtualFile, Collection<DexProgramClass> layout) {
         assertThat(
-            layout, isEqualToClassDataLayout(getExpectedClassDataLayout(inspector, virtualFile)));
+            layout,
+            isEqualToClassDataLayout(getExpectedClassDataLayout(inspector, isD8, virtualFile)));
       }
     };
   }
@@ -357,11 +400,15 @@
     static void b(boolean useLambda) {
       String message = System.currentTimeMillis() > 0 ? "B" : null;
       if (useLambda) {
-        Consumer<Object> consumer = obj -> {};
-        consumer.accept(consumer);
+        synthesize();
       }
       System.out.println(message);
     }
+
+    static void synthesize() {
+      Consumer<Object> consumer = obj -> {};
+      consumer.accept(consumer);
+    }
   }
 
   static class C {
diff --git a/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java b/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java
index 815b50d..14a1e1b 100644
--- a/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java
+++ b/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java
@@ -15,23 +15,23 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.profile.art.ArtProfileBuilderUtils.SyntheticToSyntheticContextGeneralization;
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.startup.profile.ExternalStartupClass;
 import com.android.tools.r8.startup.profile.ExternalStartupItem;
 import com.android.tools.r8.startup.profile.ExternalStartupMethod;
-import com.android.tools.r8.startup.profile.ExternalSyntheticStartupMethod;
 import com.android.tools.r8.startup.utils.MixedSectionLayoutInspector;
 import com.android.tools.r8.startup.utils.StartupTestingUtils;
 import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.TypeReferenceUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
@@ -63,7 +63,7 @@
   }
 
   @Test
-  public void test() throws Exception {
+  public void testR8() throws Exception {
     LinkedHashSet<ExternalStartupItem> startupList = new LinkedHashSet<>();
     testForD8(parameters.getBackend())
         .addInnerClasses(getClass())
@@ -74,9 +74,7 @@
         .compile()
         .addRunClasspathFiles(StartupTestingUtils.getAndroidUtilLog(temp))
         .run(parameters.getRuntime(), Main.class)
-        .apply(
-            StartupTestingUtils.removeStartupListFromStdout(
-                startupList::add, SyntheticToSyntheticContextGeneralization.createForR8()))
+        .apply(StartupTestingUtils.removeStartupListFromStdout(startupList::add))
         .assertSuccessWithOutputLines(getExpectedOutput());
     assertEquals(getExpectedStartupList(), startupList);
 
@@ -122,8 +120,20 @@
         ExternalStartupMethod.builder()
             .setMethodReference(Reference.methodFromMethod(B.class.getDeclaredMethod("b")))
             .build(),
-        ExternalSyntheticStartupMethod.builder()
-            .setSyntheticContextReference(Reference.classFromClass(B.class))
+        ExternalStartupClass.builder()
+            .setClassReference(getSyntheticLambdaClassReference(B.class))
+            .build(),
+        ExternalStartupMethod.builder()
+            .setMethodReference(
+                MethodReferenceUtils.instanceConstructor(getSyntheticLambdaClassReference(B.class)))
+            .build(),
+        ExternalStartupMethod.builder()
+            .setMethodReference(
+                Reference.method(
+                    getSyntheticLambdaClassReference(B.class),
+                    "run",
+                    Collections.emptyList(),
+                    TypeReferenceUtils.getVoidType()))
             .build(),
         ExternalStartupMethod.builder()
             .setMethodReference(Reference.methodFromMethod(B.class.getDeclaredMethod("lambda$b$0")))
@@ -140,8 +150,8 @@
       builder.add(
           Reference.classFromClass(Main.class),
           Reference.classFromClass(A.class),
-          getSyntheticLambdaClassReference(B.class),
-          Reference.classFromClass(C.class));
+          Reference.classFromClass(C.class),
+          getSyntheticLambdaClassReference(B.class));
     }
     if (!enableMinimalStartupDex || virtualFile == 1) {
       builder.add(getSyntheticLambdaClassReference(Main.class));
@@ -186,13 +196,15 @@
 
     public static void main(String[] args) {
       A.a();
-      Runnable r = System.currentTimeMillis() > 0 ? B.b() : Main::error;
+      Runnable r = System.currentTimeMillis() > 0 ? B.b() : error();
       r.run();
       C.c();
     }
 
-    static void error() {
-      throw new RuntimeException();
+    static Runnable error() {
+      return () -> {
+        throw new RuntimeException();
+      };
     }
   }
 
@@ -203,6 +215,8 @@
     }
   }
 
+  // Class B will be pruned as a result of inlining, yet the synthetic derived from B.b() remains in
+  // the startup list.
   static class B {
 
     static Runnable b() {
diff --git a/src/test/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnosticTest.java b/src/test/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnosticTest.java
index d9ed155..5cf70dd 100644
--- a/src/test/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnosticTest.java
+++ b/src/test/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnosticTest.java
@@ -9,6 +9,7 @@
 import static org.hamcrest.CoreMatchers.allOf;
 import static org.hamcrest.CoreMatchers.equalTo;
 
+import com.android.tools.r8.D8TestCompileResult;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestDiagnosticMessages;
 import com.android.tools.r8.TestParameters;
@@ -42,8 +43,16 @@
 
   @Test
   public void testD8() throws Exception {
+    // In D8 the startup profile is used to relayout an existing apk. Therefore, we first compile
+    // the program to dex, and then relayout the dex using D8 with a startup profile.
+    D8TestCompileResult compileResult =
+        testForD8(Backend.DEX)
+            .addProgramClasses(Main.class)
+            .release()
+            .setMinApi(AndroidApiLevel.LATEST)
+            .compile();
     testForD8(Backend.DEX)
-        .addProgramClasses(Main.class)
+        .addProgramFiles(compileResult.writeToZip())
         .addStartupProfileProviders(getStartupProfileProviders())
         .release()
         .setMinApi(AndroidApiLevel.LATEST)
@@ -52,13 +61,15 @@
 
   @Test
   public void testR8() throws Exception {
+    // In R8 we expect a startup profile that matches the input app. Since profiles gathered from
+    // running on ART will include synthetics, and these synthetics are not in the input app, we do
+    // not raise warnings if some rules in the profile do not match anything.
     testForR8(Backend.DEX)
         .addProgramClasses(Main.class)
         .addKeepMainRule(Main.class)
         .addStartupProfileProviders(getStartupProfileProviders())
-        .allowDiagnosticWarningMessages()
         .setMinApi(AndroidApiLevel.LATEST)
-        .compileWithExpectedDiagnostics(this::inspectDiagnostics);
+        .compileWithExpectedDiagnostics(TestDiagnosticMessages::assertNoMessages);
   }
 
   private static Collection<StartupProfileProvider> getStartupProfileProviders() {
@@ -68,7 +79,6 @@
           public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
             ClassReference fooClassReference = Reference.classFromTypeName("Foo");
             ClassReference barClassReference = Reference.classFromTypeName("Bar");
-            ClassReference bazClassReference = Reference.classFromTypeName("Baz");
             ClassReference jDollarClassReference = Reference.classFromTypeName("j$.Foo");
             startupProfileBuilder
                 .addStartupClass(
@@ -77,10 +87,6 @@
                     startupMethodBuilder ->
                         startupMethodBuilder.setMethodReference(
                             MethodReferenceUtils.mainMethod(barClassReference)))
-                .addSyntheticStartupMethod(
-                    syntheticStartupMethodBuilder ->
-                        syntheticStartupMethodBuilder.setSyntheticContextReference(
-                            bazClassReference))
                 .addStartupClass(
                     startupClassBuilder ->
                         startupClassBuilder.setClassReference(jDollarClassReference));
@@ -102,7 +108,6 @@
                 equalTo(
                     StringUtils.joinLines(
                         "Startup method not found: void Bar.main(java.lang.String[])",
-                        "Startup class not found: Baz",
                         "Startup class not found: Foo")))));
   }
 
diff --git a/src/test/java/com/android/tools/r8/startup/profile/ExternalStartupClass.java b/src/test/java/com/android/tools/r8/startup/profile/ExternalStartupClass.java
index 4eee018..1bcbb45 100644
--- a/src/test/java/com/android/tools/r8/startup/profile/ExternalStartupClass.java
+++ b/src/test/java/com/android/tools/r8/startup/profile/ExternalStartupClass.java
@@ -23,8 +23,7 @@
   @Override
   public <T> T apply(
       Function<ExternalStartupClass, T> classFunction,
-      Function<ExternalStartupMethod, T> methodFunction,
-      Function<ExternalSyntheticStartupMethod, T> syntheticMethodFunction) {
+      Function<ExternalStartupMethod, T> methodFunction) {
     return classFunction.apply(this);
   }
 
diff --git a/src/test/java/com/android/tools/r8/startup/profile/ExternalStartupItem.java b/src/test/java/com/android/tools/r8/startup/profile/ExternalStartupItem.java
index e3aa745..6009d74 100644
--- a/src/test/java/com/android/tools/r8/startup/profile/ExternalStartupItem.java
+++ b/src/test/java/com/android/tools/r8/startup/profile/ExternalStartupItem.java
@@ -10,6 +10,5 @@
 
   public abstract <T> T apply(
       Function<ExternalStartupClass, T> classFunction,
-      Function<ExternalStartupMethod, T> methodFunction,
-      Function<ExternalSyntheticStartupMethod, T> syntheticMethodFunction);
+      Function<ExternalStartupMethod, T> methodFunction);
 }
diff --git a/src/test/java/com/android/tools/r8/startup/profile/ExternalStartupMethod.java b/src/test/java/com/android/tools/r8/startup/profile/ExternalStartupMethod.java
index 109a028..55775ea 100644
--- a/src/test/java/com/android/tools/r8/startup/profile/ExternalStartupMethod.java
+++ b/src/test/java/com/android/tools/r8/startup/profile/ExternalStartupMethod.java
@@ -24,8 +24,7 @@
   @Override
   public <T> T apply(
       Function<ExternalStartupClass, T> classFunction,
-      Function<ExternalStartupMethod, T> methodFunction,
-      Function<ExternalSyntheticStartupMethod, T> syntheticMethodFunction) {
+      Function<ExternalStartupMethod, T> methodFunction) {
     return methodFunction.apply(this);
   }
 
diff --git a/src/test/java/com/android/tools/r8/startup/profile/ExternalSyntheticStartupMethod.java b/src/test/java/com/android/tools/r8/startup/profile/ExternalSyntheticStartupMethod.java
deleted file mode 100644
index 6d07638..0000000
--- a/src/test/java/com/android/tools/r8/startup/profile/ExternalSyntheticStartupMethod.java
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (c) 2022, 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.startup.profile;
-
-import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.startup.SyntheticStartupMethodBuilder;
-import java.util.function.Function;
-
-public class ExternalSyntheticStartupMethod extends ExternalStartupItem {
-
-  private final ClassReference syntheticContextReference;
-
-  ExternalSyntheticStartupMethod(ClassReference syntheticContextReference) {
-    this.syntheticContextReference = syntheticContextReference;
-  }
-
-  public static Builder builder() {
-    return new Builder();
-  }
-
-  @Override
-  public <T> T apply(
-      Function<ExternalStartupClass, T> classFunction,
-      Function<ExternalStartupMethod, T> methodFunction,
-      Function<ExternalSyntheticStartupMethod, T> syntheticMethodFunction) {
-    return syntheticMethodFunction.apply(this);
-  }
-
-  public ClassReference getSyntheticContextReference() {
-    return syntheticContextReference;
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (this == o) {
-      return true;
-    }
-    if (o == null || getClass() != o.getClass()) {
-      return false;
-    }
-    ExternalSyntheticStartupMethod that = (ExternalSyntheticStartupMethod) o;
-    return syntheticContextReference.equals(that.syntheticContextReference);
-  }
-
-  @Override
-  public int hashCode() {
-    return syntheticContextReference.hashCode();
-  }
-
-  @Override
-  public String toString() {
-    return "S(" + syntheticContextReference.getTypeName() + ")";
-  }
-
-  public static class Builder implements SyntheticStartupMethodBuilder {
-
-    private ClassReference syntheticContextReference;
-
-    @Override
-    public Builder setSyntheticContextReference(ClassReference syntheticContextReference) {
-      this.syntheticContextReference = syntheticContextReference;
-      return this;
-    }
-
-    public ExternalSyntheticStartupMethod build() {
-      return new ExternalSyntheticStartupMethod(syntheticContextReference);
-    }
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java b/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
index b6e08bf..93dc0a0 100644
--- a/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
+++ b/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
@@ -21,18 +21,15 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.profile.art.ArtProfileBuilder;
 import com.android.tools.r8.profile.art.ArtProfileBuilderUtils;
-import com.android.tools.r8.profile.art.ArtProfileBuilderUtils.SyntheticToSyntheticContextGeneralization;
 import com.android.tools.r8.profile.art.HumanReadableArtProfileParser;
 import com.android.tools.r8.profile.art.HumanReadableArtProfileParserBuilder;
 import com.android.tools.r8.startup.StartupClassBuilder;
 import com.android.tools.r8.startup.StartupMethodBuilder;
 import com.android.tools.r8.startup.StartupProfileBuilder;
 import com.android.tools.r8.startup.StartupProfileProvider;
-import com.android.tools.r8.startup.SyntheticStartupMethodBuilder;
 import com.android.tools.r8.startup.profile.ExternalStartupClass;
 import com.android.tools.r8.startup.profile.ExternalStartupItem;
 import com.android.tools.r8.startup.profile.ExternalStartupMethod;
-import com.android.tools.r8.startup.profile.ExternalSyntheticStartupMethod;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringUtils;
@@ -49,8 +46,7 @@
   private static String startupInstrumentationTag = "startup";
 
   private static ArtProfileBuilder createStartupItemFactory(
-      Consumer<ExternalStartupItem> startupItemConsumer,
-      SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization) {
+      Consumer<ExternalStartupItem> startupItemConsumer) {
     StartupProfileBuilder startupProfileBuilder =
         new StartupProfileBuilder() {
           @Override
@@ -72,16 +68,6 @@
           }
 
           @Override
-          public StartupProfileBuilder addSyntheticStartupMethod(
-              Consumer<SyntheticStartupMethodBuilder> syntheticStartupMethodBuilderConsumer) {
-            ExternalSyntheticStartupMethod.Builder syntheticStartupMethodBuilder =
-                ExternalSyntheticStartupMethod.builder();
-            syntheticStartupMethodBuilderConsumer.accept(syntheticStartupMethodBuilder);
-            startupItemConsumer.accept(syntheticStartupMethodBuilder.build());
-            return this;
-          }
-
-          @Override
           public StartupProfileBuilder addHumanReadableArtProfile(
               TextInputStream textInputStream,
               Consumer<HumanReadableArtProfileParserBuilder> parserBuilderConsumer) {
@@ -90,8 +76,7 @@
           }
         };
     return ArtProfileBuilderUtils.createBuilderForArtProfileToStartupProfileConversion(
-        startupProfileBuilder,
-        syntheticToSyntheticContextGeneralization);
+        startupProfileBuilder);
   }
 
   public static ThrowableConsumer<D8TestBuilder>
@@ -135,41 +120,29 @@
   }
 
   public static void readStartupListFromFile(
-      Path path,
-      Consumer<ExternalStartupItem> startupItemConsumer,
-      SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization)
-      throws IOException {
+      Path path, Consumer<ExternalStartupItem> startupItemConsumer) throws IOException {
     TestDiagnosticMessagesImpl diagnostics = new TestDiagnosticMessagesImpl();
     HumanReadableArtProfileParser parser =
         HumanReadableArtProfileParser.builder()
             .setReporter(new Reporter(diagnostics))
-            .setProfileBuilder(
-                createStartupItemFactory(
-                    startupItemConsumer, syntheticToSyntheticContextGeneralization))
+            .setProfileBuilder(createStartupItemFactory(startupItemConsumer))
             .build();
     parser.parse(new UTF8TextInputStream(path), Origin.unknown());
     diagnostics.assertNoMessages();
   }
 
   public static ThrowingConsumer<D8TestRunResult, RuntimeException> removeStartupListFromStdout(
-      Consumer<ExternalStartupItem> startupItemConsumer,
-      SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization) {
-    return runResult ->
-        removeStartupListFromStdout(
-            runResult, startupItemConsumer, syntheticToSyntheticContextGeneralization);
+      Consumer<ExternalStartupItem> startupItemConsumer) {
+    return runResult -> removeStartupListFromStdout(runResult, startupItemConsumer);
   }
 
   public static void removeStartupListFromStdout(
-      D8TestRunResult runResult,
-      Consumer<ExternalStartupItem> startupItemConsumer,
-      SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization) {
+      D8TestRunResult runResult, Consumer<ExternalStartupItem> startupItemConsumer) {
     TestDiagnosticMessagesImpl diagnostics = new TestDiagnosticMessagesImpl();
     HumanReadableArtProfileParser parser =
         HumanReadableArtProfileParser.builder()
             .setReporter(new Reporter(diagnostics))
-            .setProfileBuilder(
-                createStartupItemFactory(
-                    startupItemConsumer, syntheticToSyntheticContextGeneralization))
+            .setProfileBuilder(createStartupItemFactory(startupItemConsumer))
             .build();
     StringBuilder stdoutBuilder = new StringBuilder();
     String startupDescriptorPrefix = "[" + startupInstrumentationTag + "] ";
@@ -202,12 +175,7 @@
                       startupProfileBuilder.addStartupMethod(
                           startupMethodBuilder ->
                               startupMethodBuilder.setMethodReference(
-                                  startupMethod.getReference())),
-                  syntheticStartupMethod ->
-                      startupProfileBuilder.addSyntheticStartupMethod(
-                          syntheticStartupMethodBuilder ->
-                              syntheticStartupMethodBuilder.setSyntheticContextReference(
-                                  syntheticStartupMethod.getSyntheticContextReference())));
+                                  startupMethod.getReference())));
             }
           }
 
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentAnnotationSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentAnnotationSubject.java
index 610b054..2ad1eb6 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentAnnotationSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentAnnotationSubject.java
@@ -6,6 +6,7 @@
 
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexEncodedAnnotation;
+import com.android.tools.r8.graph.DexTypeAnnotation;
 
 public class AbsentAnnotationSubject extends AnnotationSubject {
 
@@ -28,4 +29,19 @@
   public DexEncodedAnnotation getAnnotation() {
     throw new UnsupportedOperationException();
   }
+
+  @Override
+  public int isVisible() {
+    throw new Unreachable("Subject is absent");
+  }
+
+  @Override
+  public boolean isTypeAnnotation() {
+    throw new Unreachable("Subject is absent");
+  }
+
+  @Override
+  public DexTypeAnnotation asDexTypeAnnotation() {
+    throw new Unreachable("Subject is absent");
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AnnotationSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AnnotationSubject.java
index 4bffcca..ddd737b 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AnnotationSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AnnotationSubject.java
@@ -5,8 +5,15 @@
 package com.android.tools.r8.utils.codeinspector;
 
 import com.android.tools.r8.graph.DexEncodedAnnotation;
+import com.android.tools.r8.graph.DexTypeAnnotation;
 
 public abstract class AnnotationSubject extends Subject {
 
   public abstract DexEncodedAnnotation getAnnotation();
+
+  public abstract int isVisible();
+
+  public abstract boolean isTypeAnnotation();
+
+  public abstract DexTypeAnnotation asDexTypeAnnotation();
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundAnnotationSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundAnnotationSubject.java
index e73c25a..4d67e5d 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundAnnotationSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundAnnotationSubject.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexEncodedAnnotation;
+import com.android.tools.r8.graph.DexTypeAnnotation;
 import com.android.tools.r8.utils.ListUtils;
 import java.util.List;
 
@@ -51,4 +52,19 @@
   public DexEncodedAnnotation getAnnotation() {
     return annotation.annotation;
   }
+
+  @Override
+  public int isVisible() {
+    return annotation.getVisibility();
+  }
+
+  @Override
+  public boolean isTypeAnnotation() {
+    return annotation.isTypeAnnotation();
+  }
+
+  @Override
+  public DexTypeAnnotation asDexTypeAnnotation() {
+    return annotation.asTypeAnnotation();
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
index d16221e..abc83be 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.utils.codeinspector;
 
-import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.graph.AccessFlags;
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexEncodedField;
@@ -115,7 +114,7 @@
 
   @Override
   public List<FoundAnnotationSubject> annotations() {
-    throw new Unimplemented();
+    return FoundAnnotationSubject.listFromDex(dexField.annotations(), codeInspector);
   }
 
   @Override
@@ -133,9 +132,7 @@
 
   @Override
   public String getJvmFieldSignatureAsString() {
-    return dexField.getReference().name.toString()
-        + ":"
-        + dexField.getReference().type.toDescriptorString();
+    return dexField.getReference().name + ":" + dexField.getReference().type.toDescriptorString();
   }
 
   @Override
diff --git a/tools/retrace.py b/tools/retrace.py
index a7ff23d..cbb2d33 100755
--- a/tools/retrace.py
+++ b/tools/retrace.py
@@ -3,6 +3,8 @@
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
 
+from os import path
+
 import argparse
 import os
 import subprocess
@@ -122,7 +124,7 @@
     else:
       args.commit_hash = r8_version_or_hash
     map_path = None
-    if get_hash_from_map_file(utils.R8LIB_MAP) == maphash:
+    if path.exists(utils.R8LIB_MAP) and get_hash_from_map_file(utils.R8LIB_MAP) == maphash:
       return utils.R8LIB_MAP
 
     try: