Defer pruning of missing startup items until after desugaring

Change-Id: I9a4a0814a05f7add7d2498d814c9e008f821a659
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/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 3f0da93..41ea658 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -699,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);
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 e434115..08b14b8 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -244,7 +244,7 @@
       StartupProfile startupProfile =
           appView.appInfo().hasClassHierarchy()
               ? appView.getStartupProfile()
-              : StartupProfile.createInitialStartupOrderForD8(appView);
+              : StartupProfile.createInitialStartupProfileForD8(appView);
       distributor =
           new VirtualFile.FillFilesDistributor(
               this, classes, options, executorService, startupProfile);
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 da63c75..60055c9 100644
--- a/src/main/java/com/android/tools/r8/dex/MixedSectionLayoutStrategy.java
+++ b/src/main/java/com/android/tools/r8/dex/MixedSectionLayoutStrategy.java
@@ -23,13 +23,13 @@
   public static MixedSectionLayoutStrategy create(
       AppView<?> appView, MixedSectionOffsets mixedSectionOffsets, VirtualFile virtualFile) {
     StartupProfile startupProfileForWriting;
-    if (virtualFile.getStartupOrder().isEmpty()) {
+    if (virtualFile.getStartupProfile().isEmpty()) {
       startupProfileForWriting = StartupProfile.empty();
     } else {
       assert virtualFile.getId() == 0;
       startupProfileForWriting =
           appView.options().getStartupOptions().isStartupLayoutOptimizationsEnabled()
-              ? virtualFile.getStartupOrder().toStartupOrderForWriting(appView)
+              ? virtualFile.getStartupProfile().toStartupProfileForWriting(appView)
               : StartupProfile.empty();
     }
     MixedSectionLayoutStrategy mixedSectionLayoutStrategy =
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 cf56867..270fcf2 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -137,7 +137,7 @@
     return featureSplit;
   }
 
-  public StartupProfile getStartupOrder() {
+  public StartupProfile getStartupProfile() {
     return startupProfile;
   }
 
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
index a2e3fe9..7f2a248 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupProfile.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupProfile.java
@@ -64,7 +64,12 @@
   }
 
   @Override
-  public EmptyStartupProfile toStartupOrderForWriting(AppView<?> appView) {
+  public EmptyStartupProfile toStartupProfileForWriting(AppView<?> appView) {
+    return this;
+  }
+
+  @Override
+  public StartupProfile withoutMissingItems(AppView<?> appView) {
     return this;
   }
 
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 2fa1094..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
@@ -12,6 +12,7 @@
 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.startup.diagnostic.MissingStartupProfileItemsDiagnostic;
 import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.Sets;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMaps;
@@ -27,7 +28,8 @@
     this.startupProfile =
         appView.hasClassHierarchy()
             ? appView.getStartupProfile()
-            : StartupProfile.createInitialStartupOrder(appView.options(), null);
+            : StartupProfile.createInitialStartupProfile(
+                appView.options(), origin -> MissingStartupProfileItemsDiagnostic.Builder.nop());
   }
 
   /**
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
index 3e4f660..e8be76e 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupProfile.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupProfile.java
@@ -11,11 +11,11 @@
 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.DexDefinitionSupplier;
 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;
@@ -34,6 +34,7 @@
 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> {
@@ -44,6 +45,10 @@
     return new Builder();
   }
 
+  public static Builder builderWithCapacity(int capacity) {
+    return new Builder(capacity);
+  }
+
   public static Builder builder(
       InternalOptions options,
       MissingStartupProfileItemsDiagnostic.Builder missingItemsDiagnosticBuilder,
@@ -51,9 +56,12 @@
     return new Builder(options, missingItemsDiagnosticBuilder, startupProfileProvider);
   }
 
-  public static StartupProfile createInitialStartupOrder(
-      InternalOptions options, DexDefinitionSupplier definitions) {
-    StartupProfile startupProfile = StartupProfile.parseStartupProfile(options, definitions);
+  public static StartupProfile createInitialStartupProfile(
+      InternalOptions options,
+      Function<Origin, MissingStartupProfileItemsDiagnostic.Builder>
+          missingItemsDiagnosticBuilderFactory) {
+    StartupProfile startupProfile =
+        StartupProfile.parseStartupProfile(options, missingItemsDiagnosticBuilderFactory);
     if (startupProfile == null || startupProfile.isEmpty()) {
       return empty();
     }
@@ -64,12 +72,18 @@
     return builder.build();
   }
 
-  public static StartupProfile createInitialStartupOrderForD8(AppView<?> appView) {
-    return createInitialStartupOrder(appView.options(), appView);
+  public static StartupProfile createInitialStartupProfileForD8(AppView<?> appView) {
+    return createInitialStartupProfile(
+        appView.options(),
+        origin -> new MissingStartupProfileItemsDiagnostic.Builder(appView).setOrigin(origin));
   }
 
-  public static StartupProfile createInitialStartupOrderForR8(DexApplication application) {
-    return createInitialStartupOrder(application.options, application);
+  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() {
@@ -100,7 +114,9 @@
    * </pre>
    */
   public static StartupProfile parseStartupProfile(
-      InternalOptions options, DexDefinitionSupplier definitions) {
+      InternalOptions options,
+      Function<Origin, MissingStartupProfileItemsDiagnostic.Builder>
+          missingItemsDiagnosticBuilderFactory) {
     if (!options.getStartupOptions().hasStartupProfileProviders()) {
       return null;
     }
@@ -109,8 +125,7 @@
     List<StartupProfile> startupProfiles = new ArrayList<>(startupProfileProviders.size());
     for (StartupProfileProvider startupProfileProvider : startupProfileProviders) {
       MissingStartupProfileItemsDiagnostic.Builder missingItemsDiagnosticBuilder =
-          new MissingStartupProfileItemsDiagnostic.Builder(definitions)
-              .setOrigin(startupProfileProvider.getOrigin());
+          missingItemsDiagnosticBuilderFactory.apply(startupProfileProvider.getOrigin());
       StartupProfile.Builder startupProfileBuilder =
           StartupProfile.builder(options, missingItemsDiagnosticBuilder, startupProfileProvider);
       startupProfileProvider.getStartupProfile(startupProfileBuilder);
@@ -128,7 +143,9 @@
 
   public abstract StartupProfile rewrittenWithLens(GraphLens graphLens);
 
-  public abstract StartupProfile toStartupOrderForWriting(AppView<?> appView);
+  public abstract StartupProfile toStartupProfileForWriting(AppView<?> appView);
+
+  public abstract StartupProfile withoutMissingItems(AppView<?> appView);
 
   public abstract StartupProfile withoutPrunedItems(
       PrunedItems prunedItems, SyntheticItems syntheticItems);
@@ -143,13 +160,21 @@
     private Reporter reporter;
     private final StartupProfileProvider startupProfileProvider;
 
-    private final LinkedHashMap<DexReference, StartupProfileRule> startupItems =
-        new LinkedHashMap<>();
+    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;
     }
 
@@ -160,6 +185,7 @@
       this.dexItemFactory = options.dexItemFactory();
       this.missingItemsDiagnosticBuilder = missingItemsDiagnosticBuilder;
       this.reporter = options.reporter;
+      this.startupItems = new LinkedHashMap<>();
       this.startupProfileProvider = startupProfileProvider;
     }
 
@@ -237,6 +263,10 @@
       return this;
     }
 
+    public int size() {
+      return startupItems.size();
+    }
+
     @Override
     public StartupProfile build() {
       if (startupItems.isEmpty()) {
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
index 90a8e7d..ab251a8 100644
--- 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
@@ -5,7 +5,9 @@
 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;
@@ -16,6 +18,7 @@
 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 {
 
@@ -68,26 +71,17 @@
 
   @Override
   public StartupProfile rewrittenWithLens(GraphLens graphLens) {
-    LinkedHashMap<DexReference, StartupProfileRule> rewrittenStartupItems =
-        new LinkedHashMap<>(startupItems.size());
-    for (StartupProfileRule startupItem : startupItems.values()) {
-      // TODO(b/271822426): This should account for one-to-many mappings. e.g., when a bridge is
-      //  created.
-      startupItem.apply(
-          startupClass ->
-              rewrittenStartupItems.put(
-                  startupClass.getReference(),
-                  StartupProfileClassRule.builder()
-                      .setClassReference(graphLens.lookupType(startupClass.getReference()))
-                      .build()),
-          startupMethod ->
-              rewrittenStartupItems.put(
-                  startupMethod.getReference(),
-                  StartupProfileMethodRule.builder()
-                      .setMethod(graphLens.getRenamedMethodSignature(startupMethod.getReference()))
-                      .build()));
-    }
-    return createNonEmpty(rewrittenStartupItems);
+    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() {
@@ -95,109 +89,97 @@
   }
 
   /**
-   * This is called to process the startup order before computing the startup layouts.
+   * This is called to process the startup profile before computing the startup layouts.
    *
-   * <p>This processing makes two key changes to the startup order:
+   * <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.
    *
-   * <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>
+   * <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 toStartupOrderForWriting(AppView<?> appView) {
-    LinkedHashMap<DexReference, StartupProfileRule> rewrittenStartupItems =
-        new LinkedHashMap<>(startupItems.size());
-    for (StartupProfileRule startupItem : startupItems.values()) {
-      addStartupItem(startupItem, rewrittenStartupItems, appView);
-    }
-    return createNonEmpty(rewrittenStartupItems);
+  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,
-      LinkedHashMap<DexReference, StartupProfileRule> rewrittenStartupItems,
-      AppView<?> appView) {
+      StartupProfileRule startupItem, Builder builder, AppView<?> appView) {
     startupItem.accept(
-        startupClass ->
-            addClassAndParentClasses(startupClass.getReference(), rewrittenStartupItems, appView),
-        startupMethod -> rewrittenStartupItems.put(startupItem.getReference(), startupItem));
+        classRule -> addClassAndParentClasses(classRule.getReference(), builder, appView),
+        builder::addMethodRule);
   }
 
-  private static boolean addClass(
-      DexProgramClass clazz,
-      LinkedHashMap<DexReference, StartupProfileRule> rewrittenStartupItems) {
-    StartupProfileRule previous =
-        rewrittenStartupItems.put(
-            clazz.getType(),
-            StartupProfileClassRule.builder().setClassReference(clazz.getType()).build());
-    return previous == null;
+  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,
-      LinkedHashMap<DexReference, StartupProfileRule> rewrittenStartupItems,
-      AppView<?> appView) {
+  private static void addClassAndParentClasses(DexType type, Builder builder, AppView<?> appView) {
     DexProgramClass definition = appView.app().programDefinitionFor(type);
     if (definition != null) {
-      addClassAndParentClasses(definition, rewrittenStartupItems, appView);
+      addClassAndParentClasses(definition, builder, appView);
     }
   }
 
   private static void addClassAndParentClasses(
-      DexProgramClass clazz,
-      LinkedHashMap<DexReference, StartupProfileRule> rewrittenStartupItems,
-      AppView<?> appView) {
-    if (addClass(clazz, rewrittenStartupItems)) {
-      addParentClasses(clazz, rewrittenStartupItems, appView);
+      DexProgramClass clazz, Builder builder, AppView<?> appView) {
+    if (addClass(clazz, builder)) {
+      addParentClasses(clazz, builder, appView);
     }
   }
 
-  private static void addParentClasses(
-      DexProgramClass clazz,
-      LinkedHashMap<DexReference, StartupProfileRule> rewrittenStartupItems,
-      AppView<?> appView) {
+  private static void addParentClasses(DexProgramClass clazz, Builder builder, AppView<?> appView) {
     clazz.forEachImmediateSupertype(
-        supertype -> addClassAndParentClasses(supertype, rewrittenStartupItems, appView));
+        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) {
-    LinkedHashMap<DexReference, StartupProfileRule> rewrittenStartupItems =
-        new LinkedHashMap<>(startupItems.size());
-    for (StartupProfileRule startupItem : startupItems.values()) {
-      // 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.put(startupClass.getReference(), startupItem);
-            }
-          },
-          startupMethod -> {
-            if (!prunedItems.isRemoved(startupMethod.getReference())) {
-              rewrittenStartupItems.put(startupMethod.getReference(), startupItem);
-            }
-          });
-    }
-    return createNonEmpty(rewrittenStartupItems);
+    return transform(
+        (classRule, builder) -> {
+          if (!prunedItems.isRemoved(classRule.getReference())) {
+            builder.addClassRule(classRule);
+          }
+        },
+        (methodRule, builder) -> {
+          if (!prunedItems.isRemoved(methodRule.getReference())) {
+            builder.addMethodRule(methodRule);
+          }
+        });
   }
 
-  private StartupProfile createNonEmpty(
-      LinkedHashMap<DexReference, StartupProfileRule> startupItems) {
-    if (startupItems.isEmpty()) {
-      assert false;
-      return empty();
-    }
-    return new NonEmptyStartupProfile(startupItems);
+  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/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index d7d5948..92848aa 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -253,7 +253,7 @@
     return new AppView<>(
         appInfo,
         ArtProfileCollection.createInitialArtProfileCollection(appInfo, appInfo.options()),
-        StartupProfile.createInitialStartupOrderForR8(application),
+        StartupProfile.createInitialStartupProfileForR8(application),
         WholeProgramOptimizations.ON,
         defaultTypeRewriter(appInfo));
   }