Collect immediate subtypes of live types in the enqueuer.

Follow-up work will use this for looking up live virtual targets.

Bug: 139464956

Change-Id: I7e8b338d80f0212d253e4d263e6231ee36575ac0
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 d0e971e..9ba9f6a 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -106,6 +106,7 @@
 import java.lang.reflect.InvocationHandler;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Deque;
 import java.util.HashMap;
@@ -171,6 +172,9 @@
   private ProguardClassFilter dontWarnPatterns;
   private final EnqueuerUseRegistryFactory useRegistryFactory;
 
+  private final Map<DexProgramClass, Set<DexProgramClass>> immediateSubtypesOfLiveTypes =
+      new IdentityHashMap<>();
+
   private final Map<DexMethod, Set<DexEncodedMethod>> virtualInvokes = new IdentityHashMap<>();
   private final Map<DexMethod, Set<DexEncodedMethod>> interfaceInvokes = new IdentityHashMap<>();
   private final Map<DexMethod, Set<DexEncodedMethod>> superInvokes = new IdentityHashMap<>();
@@ -1248,6 +1252,19 @@
         witness);
   }
 
+  private void addImmediateSubtype(DexProgramClass superType, DexProgramClass subType) {
+    assert liveTypes.contains(subType);
+    assert subType.superType == superType.type
+        || Arrays.asList(subType.interfaces.values).contains(superType.type);
+    immediateSubtypesOfLiveTypes
+        .computeIfAbsent(superType, k -> Sets.newIdentityHashSet())
+        .add(subType);
+  }
+
+  private Set<DexProgramClass> getImmediateLiveSubtypes(DexProgramClass clazz) {
+    return immediateSubtypesOfLiveTypes.getOrDefault(clazz, Collections.emptySet());
+  }
+
   private void markTypeAsLive(
       DexProgramClass holder, ScopedDexMethodSet seen, KeepReasonWitness witness) {
     if (!liveTypes.add(holder, witness)) {
@@ -1277,6 +1294,10 @@
               holder.superType, ignore -> new ScopedDexMethodSet());
       seen.setParent(seenForSuper);
       markTypeAsLive(holder.superType, reason);
+      DexProgramClass superClass = getProgramClassOrNull(holder.superType);
+      if (superClass != null) {
+        addImmediateSubtype(superClass, holder);
+      }
     }
 
     // If this is an interface that has just become live, then report previously seen but unreported
@@ -1345,6 +1366,8 @@
       return;
     }
 
+    addImmediateSubtype(clazz, implementer);
+
     if (!appView.options().enableUnusedInterfaceRemoval || mode.isTracingMainDex()) {
       markTypeAsLive(clazz, graphReporter.reportClassReferencedFrom(clazz, implementer));
     } else {
@@ -2938,18 +2961,16 @@
 
     // If there is a subtype of `clazz` that escapes into the library and does not override `method`
     // then we need to mark the method as being reachable.
-    Deque<DexType> worklist = new ArrayDeque<>(appView.appInfo().allImmediateSubtypes(clazz.type));
-
-    Set<DexType> visited = Sets.newIdentityHashSet();
-    visited.addAll(worklist);
+    Set<DexProgramClass> immediateSubtypes = getImmediateLiveSubtypes(clazz);
+    if (immediateSubtypes.isEmpty()) {
+      return false;
+    }
+    Deque<DexProgramClass> worklist = new ArrayDeque<>(immediateSubtypes);
+    Set<DexProgramClass> visited = SetUtils.newIdentityHashSet(immediateSubtypes);
 
     while (!worklist.isEmpty()) {
-      DexClass current = appView.definitionFor(worklist.removeFirst());
-      if (current == null) {
-        continue;
-      }
-
-      assert visited.contains(current.type);
+      DexProgramClass current = worklist.removeFirst();
+      assert visited.contains(current);
 
       if (current.lookupVirtualMethod(method.method) != null) {
         continue;
@@ -2959,7 +2980,7 @@
         return true;
       }
 
-      for (DexType subtype : appView.appInfo().allImmediateSubtypes(current.type)) {
+      for (DexProgramClass subtype : getImmediateLiveSubtypes(current)) {
         if (visited.add(subtype)) {
           worklist.add(subtype);
         }