Prune dead root set items

Bug: 150736225
Change-Id: If40fc7dc173aa3959ee277b9dc27c9974793150d
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 78e4102..7c21891 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -2752,12 +2752,14 @@
     // Verify all references on the input app before synthesizing definitions.
     assert verifyReferences(appInfo.app());
 
-    // Rebuild a new app only containing referenced types.
-    // Ensure references from various root set collections.
-    // TODO(b/150736225): Can we instead prune the items that are not enqueued?
-    rootSet.noSideEffects.keySet().forEach(this::recordReference);
+    // Prune the root set items that turned out to be dead.
+    // TODO(b/150736225): Pruning of dead root set items is still incomplete.
+    rootSet.pruneDeadItems(appView, this);
+
+    // Ensure references from all hard coded factory items.
     appView.dexItemFactory().forEachPossiblyCompilerSynthesizedType(this::recordTypeReference);
 
+    // Rebuild a new app only containing referenced types.
     Set<DexLibraryClass> libraryClasses = Sets.newIdentityHashSet();
     Set<DexClasspathClass> classpathClasses = Sets.newIdentityHashSet();
     for (DexClass clazz : liveNonProgramTypes) {
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 369bcd9..b48c50c 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexDefinition;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMember;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -1318,8 +1319,8 @@
       this.noObfuscation = noObfuscation;
       this.reasonAsked = reasonAsked;
       this.checkDiscarded = checkDiscarded;
-      this.alwaysInline = Collections.unmodifiableSet(alwaysInline);
-      this.forceInline = Collections.unmodifiableSet(forceInline);
+      this.alwaysInline = alwaysInline;
+      this.forceInline = forceInline;
       this.neverInline = neverInline;
       this.bypassClinitForInlining = bypassClinitForInlining;
       this.whyAreYouNotInlining = whyAreYouNotInlining;
@@ -1329,7 +1330,7 @@
       this.neverReprocess = neverReprocess;
       this.alwaysClassInline = alwaysClassInline;
       this.neverClassInline = neverClassInline;
-      this.neverMerge = Collections.unmodifiableSet(neverMerge);
+      this.neverMerge = neverMerge;
       this.neverPropagateValue = neverPropagateValue;
       this.mayHaveSideEffects = mayHaveSideEffects;
       this.noSideEffects = noSideEffects;
@@ -1460,6 +1461,34 @@
       assumedValues.remove(reference);
     }
 
+    public void pruneDeadItems(DexDefinitionSupplier definitions, Enqueuer enqueuer) {
+      pruneDeadReferences(neverMerge, definitions, enqueuer);
+      pruneDeadReferences(alwaysInline, definitions, enqueuer);
+      pruneDeadReferences(noSideEffects.keySet(), definitions, enqueuer);
+    }
+
+    private static void pruneDeadReferences(
+        Set<? extends DexReference> references,
+        DexDefinitionSupplier definitions,
+        Enqueuer enqueuer) {
+      references.removeIf(
+          reference -> {
+            if (reference.isDexField()) {
+              DexEncodedField definition = definitions.definitionFor(reference.asDexField());
+              return definition == null || !enqueuer.isFieldReferenced(definition);
+            } else if (reference.isDexMethod()) {
+              DexEncodedMethod definition = definitions.definitionFor(reference.asDexMethod());
+              return definition == null
+                  || !(enqueuer.isMethodLive(definition) || enqueuer.isMethodTargeted(definition));
+            } else {
+              DexClass definition = definitions.definitionFor(reference.asDexType());
+              return definition == null
+                  || (definition.isProgramClass()
+                      && !enqueuer.isTypeLive(definition.asProgramClass()));
+            }
+          });
+    }
+
     public void move(DexReference original, DexReference rewritten) {
       copy(original, rewritten);
       prune(original);