Avoid parsing app more than once in R8 partial

This removes the dumping of the input in R8 partial to a zip and ensures that the read classes in the R8 partial partitioning step are retained in memory and passed directly to the D8 and R8 compilations, so that we do not parse the input application more than once.

This still retains the dexing and desugaring steps as two separate compilations. Follow up work will merge these into a single D8 compilation.

Change-Id: I5b4bf894eb5814f4e9a429868b952b5ee10a19c2
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 445f246..65458bb 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -301,13 +301,6 @@
         keepDeclarations = lazyLoaded.getKeepDeclarations();
         timing.begin("To direct app");
         DirectMappedDexApplication application = lazyLoaded.toDirect();
-        if (options.partialSubCompilationConfiguration != null) {
-          application =
-              options
-                  .partialSubCompilationConfiguration
-                  .asR8SubCompilationConfiguration()
-                  .commitDesugaringOutputClasses(application);
-        }
         timing.end();
         timing.end();
         options.loadMachineDesugaredLibrarySpecification(timing, application);
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index fecf04e..8142896 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -12,7 +12,9 @@
 import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
 import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
 import com.android.tools.r8.features.FeatureSplitConfiguration;
+import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.inspector.Inspector;
 import com.android.tools.r8.inspector.internal.InspectorImpl;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
@@ -50,6 +52,7 @@
 import com.android.tools.r8.utils.InternalOptions.HorizontalClassMergerOptions;
 import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
 import com.android.tools.r8.utils.InternalOptions.MappingComposeOptions;
+import com.android.tools.r8.utils.InternalProgramClassProvider;
 import com.android.tools.r8.utils.ProgramClassCollection;
 import com.android.tools.r8.utils.R8PartialCompilationConfiguration;
 import com.android.tools.r8.utils.Reporter;
@@ -468,8 +471,30 @@
 
     @Override
     public Builder addProgramResourceProvider(ProgramResourceProvider programProvider) {
-      return super.addProgramResourceProvider(
-          new EnsureNonDexProgramResourceProvider(programProvider));
+      if (programProvider instanceof InternalProgramClassProvider) {
+        InternalProgramClassProvider internalProgramProvider =
+            (InternalProgramClassProvider) programProvider;
+        assert verifyNonDexProgramResourceProvider(internalProgramProvider);
+        assert internalProgramProvider.getDataResourceProvider() == null;
+        return super.addProgramResourceProvider(internalProgramProvider);
+      } else {
+        return super.addProgramResourceProvider(
+            new EnsureNonDexProgramResourceProvider(programProvider));
+      }
+    }
+
+    private boolean verifyNonDexProgramResourceProvider(
+        InternalProgramClassProvider internalProgramProvider) {
+      for (DexProgramClass clazz : internalProgramProvider.getClasses()) {
+        for (DexEncodedMethod method : clazz.methods()) {
+          if (!method.hasCode()) {
+            continue;
+          }
+          assert !method.getCode().isDexCode()
+              : "Unexpected method with DEX code: " + method.toSourceString();
+        }
+      }
+      return true;
     }
 
     /**
@@ -771,10 +796,11 @@
       if (isPrintHelp() || isPrintVersion()) {
         return new R8Command(isPrintHelp(), isPrintVersion());
       }
-      return makeR8Command(new DexItemFactory());
+      DexItemFactory factory = new DexItemFactory();
+      return makeR8Command(factory, makeConfiguration(factory));
     }
 
-    R8Command makeR8Command(DexItemFactory factory) {
+    R8Command makeR8Command(DexItemFactory factory, ProguardConfiguration configuration) {
       long created = System.nanoTime();
       Reporter reporter = getReporter();
       List<ProguardConfigurationRule> mainDexKeepRules =
@@ -782,51 +808,6 @@
 
       DesugaredLibrarySpecification desugaredLibrarySpecification =
           getDesugaredLibraryConfiguration(factory, false);
-
-      ProguardConfigurationParserOptions parserOptions = parserOptionsBuilder.build();
-      ProguardConfigurationParser parser =
-          new ProguardConfigurationParser(
-              factory, reporter, parserOptions, inputDependencyGraphConsumer);
-      ProguardConfiguration.Builder configurationBuilder =
-          parser
-              .getConfigurationBuilder()
-              .setForceProguardCompatibility(forceProguardCompatibility);
-      if (!proguardConfigs.isEmpty()) {
-        parser.parse(proguardConfigs);
-      }
-
-      if (getMode() == CompilationMode.DEBUG) {
-        disableMinification = true;
-        configurationBuilder.disableOptimization();
-      }
-
-      if (disableTreeShaking) {
-        configurationBuilder.disableShrinking();
-      }
-
-      if (disableMinification) {
-        configurationBuilder.disableObfuscation();
-      }
-
-      if (proguardConfigurationConsumerForTesting != null) {
-        proguardConfigurationConsumerForTesting.accept(configurationBuilder);
-      }
-
-      // Add embedded keep rules.
-      amendWithRulesAndProvidersForInjarsAndMetaInf(reporter, parser);
-
-      // Extract out rules for keep annotations and amend the configuration.
-      // TODO(b/248408342): Remove this and parse annotations as part of R8 root-set & enqueuer.
-      extractKeepAnnotationRules(parser);
-      ProguardConfiguration configuration = configurationBuilder.build();
-      if (!parserOptions.isKeepRuntimeInvisibleAnnotationsEnabled()) {
-        if (configuration.getKeepAttributes().runtimeInvisibleAnnotations
-            || configuration.getKeepAttributes().runtimeInvisibleParameterAnnotations
-            || configuration.getKeepAttributes().runtimeInvisibleTypeAnnotations) {
-          throw fatalError(
-              new StringDiagnostic("Illegal attempt to keep runtime invisible annotations"));
-        }
-      }
       getAppBuilder().addFilteredLibraryArchives(configuration.getLibraryjars());
 
       assert getProgramConsumer() != null;
@@ -893,6 +874,54 @@
       return command;
     }
 
+    private ProguardConfiguration makeConfiguration(DexItemFactory factory) {
+      ProguardConfigurationParserOptions parserOptions = parserOptionsBuilder.build();
+      ProguardConfigurationParser parser =
+          new ProguardConfigurationParser(
+              factory, getReporter(), parserOptions, inputDependencyGraphConsumer);
+      ProguardConfiguration.Builder configurationBuilder =
+          parser
+              .getConfigurationBuilder()
+              .setForceProguardCompatibility(forceProguardCompatibility);
+      if (!proguardConfigs.isEmpty()) {
+        parser.parse(proguardConfigs);
+      }
+
+      if (getMode() == CompilationMode.DEBUG) {
+        disableMinification = true;
+        configurationBuilder.disableOptimization();
+      }
+
+      if (disableTreeShaking) {
+        configurationBuilder.disableShrinking();
+      }
+
+      if (disableMinification) {
+        configurationBuilder.disableObfuscation();
+      }
+
+      if (proguardConfigurationConsumerForTesting != null) {
+        proguardConfigurationConsumerForTesting.accept(configurationBuilder);
+      }
+
+      // Add embedded keep rules.
+      amendWithRulesAndProvidersForInjarsAndMetaInf(getReporter(), parser);
+
+      // Extract out rules for keep annotations and amend the configuration.
+      // TODO(b/248408342): Remove this and parse annotations as part of R8 root-set & enqueuer.
+      extractKeepAnnotationRules(parser);
+      ProguardConfiguration configuration = configurationBuilder.build();
+      if (!parserOptions.isKeepRuntimeInvisibleAnnotationsEnabled()) {
+        if (configuration.getKeepAttributes().runtimeInvisibleAnnotations
+            || configuration.getKeepAttributes().runtimeInvisibleParameterAnnotations
+            || configuration.getKeepAttributes().runtimeInvisibleTypeAnnotations) {
+          throw fatalError(
+              new StringDiagnostic("Illegal attempt to keep runtime invisible annotations"));
+        }
+      }
+      return configuration;
+    }
+
     private void amendWithRulesAndProvidersForInjarsAndMetaInf(
         Reporter reporter, ProguardConfigurationParser parser) {
 
diff --git a/src/main/java/com/android/tools/r8/R8Partial.java b/src/main/java/com/android/tools/r8/R8Partial.java
index f2e0b75..9302b2a 100644
--- a/src/main/java/com/android/tools/r8/R8Partial.java
+++ b/src/main/java/com/android/tools/r8/R8Partial.java
@@ -3,39 +3,28 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
-import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
-
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.diagnostic.R8VersionDiagnostic;
-import com.android.tools.r8.dump.CompilerDump;
-import com.android.tools.r8.graph.AppInfo;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DirectMappedDexApplication;
 import com.android.tools.r8.partial.R8PartialD8DexResult;
 import com.android.tools.r8.partial.R8PartialDesugarResult;
 import com.android.tools.r8.partial.R8PartialInput;
-import com.android.tools.r8.partial.R8PartialInputToDumpFlags;
+import com.android.tools.r8.partial.R8PartialProgramPartioning;
 import com.android.tools.r8.partial.R8PartialSubCompilationConfiguration.R8PartialD8DesugarSubCompilationConfiguration;
 import com.android.tools.r8.partial.R8PartialSubCompilationConfiguration.R8PartialD8DexSubCompilationConfiguration;
 import com.android.tools.r8.partial.R8PartialSubCompilationConfiguration.R8PartialR8SubCompilationConfiguration;
-import com.android.tools.r8.synthesis.SyntheticItems.GlobalSyntheticsStrategy;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.ForwardingDiagnosticsHandler;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.InternalProgramClassProvider;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
-import com.android.tools.r8.utils.ZipUtils;
-import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
-import com.google.common.io.ByteStreams;
 import java.io.IOException;
-import java.nio.file.Path;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.concurrent.ExecutorService;
 import java.util.function.Consumer;
-import java.util.stream.Collectors;
 
 class R8Partial {
 
@@ -66,8 +55,16 @@
   }
 
   void runInternal(AndroidApp app, ExecutorService executor) throws IOException, ResourceException {
+    if (!options.getArtProfileOptions().getArtProfileProviders().isEmpty()) {
+      throw options.reporter.fatalError(
+          "Partial shrinking does not support baseline profile rewriting");
+    }
+    if (!options.getStartupOptions().getStartupProfileProviders().isEmpty()) {
+      throw options.reporter.fatalError("Partial shrinking does not support startup profiles");
+    }
+
     timing.begin("Process input");
-    R8PartialInput input = runProcessInputStep(app);
+    R8PartialInput input = runProcessInputStep(app, executor);
 
     timing.end().begin("Run dexing");
     R8PartialD8DexResult dexingResult = runDexingStep(input, executor);
@@ -76,7 +73,7 @@
     R8PartialDesugarResult desugarResult = runDesugarStep(input, executor);
 
     timing.end().begin("Run R8");
-    runR8PartialStep(input, dexingResult, desugarResult, executor);
+    runR8PartialStep(app, input, dexingResult, desugarResult, executor);
     timing.end();
 
     if (options.isPrintTimesReportingEnabled()) {
@@ -84,69 +81,27 @@
     }
   }
 
-  private R8PartialInput runProcessInputStep(AndroidApp androidApp) throws IOException {
-    // Create a dump of the compiler input.
-    // TODO(b/309743298): Do not use compiler dump to handle splitting the compilation. This should
-    //  be all in memory.
-    ApplicationReader applicationReader = new ApplicationReader(androidApp, options, timing);
-    Path dumpFile = resolveTmp("dump.zip");
-    applicationReader.dump(new R8PartialInputToDumpFlags(dumpFile));
-    CompilerDump dump = CompilerDump.fromArchive(dumpFile);
-    if (dump.getBuildProperties().hasMainDexKeepRules()
-        || dump.getBuildProperties().hasArtProfileProviders()
-        || dump.getBuildProperties().hasStartupProfileProviders()) {
-      throw options.reporter.fatalError(
-          "Partial shrinking does not support legacy multi-dex, baseline or startup profiles");
-    }
-
-    DexApplication app = applicationReader.read().toDirect();
-    AppInfoWithClassHierarchy appInfo =
-        AppInfoWithClassHierarchy.createForDesugaring(
-            AppInfo.createInitialAppInfo(app, GlobalSyntheticsStrategy.forNonSynthesizing()));
-
-    Set<DexProgramClass> d8classes = new HashSet<>();
-    for (DexProgramClass clazz : appInfo.classes()) {
-      if (!d8classes.contains(clazz) && !options.partialCompilationConfiguration.test(clazz)) {
-        d8classes.add(clazz);
-        // TODO(b/309743298): Improve this to only visit each class once and stop at
-        //  library boundary.
-        appInfo.forEachSuperType(
-            clazz,
-            (superType, subclass, ignored) -> {
-              DexProgramClass superClass = asProgramClassOrNull(appInfo.definitionFor(superType));
-              if (superClass != null) {
-                d8classes.add(superClass);
-              }
-            });
-      }
-    }
-
-    // Filter the program input into the D8 and R8 parts.
-    Set<String> d8ZipEntries =
-        d8classes.stream()
-            .map(clazz -> ZipUtils.zipEntryNameForClass(clazz.getClassReference()))
-            .collect(Collectors.toSet());
-    ZipBuilder d8ProgramBuilder = ZipBuilder.builder(resolveTmp("d8-program.jar"));
-    ZipBuilder r8ProgramBuilder = ZipBuilder.builder(resolveTmp("r8-program.jar"));
-    ZipUtils.iter(
-        dump.getProgramArchive(),
-        (entry, input) -> {
-          if (d8ZipEntries.contains(entry.getName())) {
-            d8ProgramBuilder.addBytes(entry.getName(), ByteStreams.toByteArray(input));
-          } else {
-            r8ProgramBuilder.addBytes(entry.getName(), ByteStreams.toByteArray(input));
-          }
-        });
-    Path d8Program = d8ProgramBuilder.build();
-    Path r8Program = r8ProgramBuilder.build();
-    return new R8PartialInput(d8Program, r8Program, dump);
+  private R8PartialInput runProcessInputStep(AndroidApp androidApp, ExecutorService executor)
+      throws IOException {
+    // TODO(b/388421578): Add support for generating R8 partial compile dumps.
+    DirectMappedDexApplication app =
+        new ApplicationReader(androidApp, options, timing).readWithoutDumping(executor).toDirect();
+    R8PartialProgramPartioning partioning = R8PartialProgramPartioning.create(app);
+    return new R8PartialInput(
+        partioning.getD8Classes(),
+        partioning.getR8Classes(),
+        app.classpathClasses(),
+        app.libraryClasses());
   }
 
   private R8PartialD8DexResult runDexingStep(R8PartialInput input, ExecutorService executor)
       throws IOException {
-    // Compile D8 input with D8.
     D8Command.Builder d8Builder =
-        D8Command.builder(options.reporter).setProgramConsumer(DexIndexedConsumer.emptyConsumer());
+        D8Command.builder(options.reporter)
+            .setMinApiLevel(options.getMinApiLevel().getLevel())
+            .setMode(options.getCompilationMode())
+            .setProgramConsumer(DexIndexedConsumer.emptyConsumer());
+    // TODO(b/391572031): Configure library desugaring.
     input.configure(d8Builder);
     d8Builder.validate();
     D8Command d8Command = d8Builder.makeD8Command(options.dexItemFactory());
@@ -172,7 +127,10 @@
     // TODO(b/389039057): This runs a full D8 compilation. For build speed, consider if the various
     //  passes in D8 can be disabled when the `partialSubCompilationConfiguration` is set.
     D8Command.Builder d8Builder =
-        D8Command.builder(options.reporter).setProgramConsumer(ClassFileConsumer.emptyConsumer());
+        D8Command.builder(options.reporter)
+            .setMinApiLevel(options.getMinApiLevel().getLevel())
+            .setMode(options.getCompilationMode())
+            .setProgramConsumer(ClassFileConsumer.emptyConsumer());
     // TODO(b/390327883): This should enable intermediate mode.
     input.configureDesugar(d8Builder);
     d8Builder.validate();
@@ -188,6 +146,7 @@
   }
 
   private void runR8PartialStep(
+      AndroidApp app,
       R8PartialInput input,
       R8PartialD8DexResult dexingResult,
       R8PartialDesugarResult desugarResult,
@@ -209,11 +168,44 @@
     // TODO(b/390389764): Disable desugaring.
     R8Command.Builder r8Builder =
         R8Command.builder(r8DiagnosticsHandler)
+            .addProgramResourceProvider(
+                new InternalProgramClassProvider(desugarResult.getOutputClasses()))
             .enableLegacyFullModeForKeepRules(true)
+            .setMinApiLevel(options.getMinApiLevel().getLevel())
+            .setMode(options.getCompilationMode())
             .setProgramConsumer(options.programConsumer);
+    // The program input that R8 must compile is provided above using an
+    // InternalProgramClassProvider. This passes in the data resources that we must either rewrite
+    // or pass through.
+    for (ProgramResourceProvider programResourceProvider : app.getProgramResourceProviders()) {
+      if (programResourceProvider.getDataResourceProvider() == null) {
+        programResourceProvider.finished(options.reporter);
+      } else {
+        r8Builder.addProgramResourceProvider(
+            new ProgramResourceProvider() {
+
+              @Override
+              public Collection<ProgramResource> getProgramResources() {
+                return Collections.emptyList();
+              }
+
+              @Override
+              public DataResourceProvider getDataResourceProvider() {
+                return programResourceProvider.getDataResourceProvider();
+              }
+
+              @Override
+              public void finished(DiagnosticsHandler handler) throws IOException {
+                programResourceProvider.finished(handler);
+              }
+            });
+      }
+    }
     input.configure(r8Builder);
     r8Builder.validate();
-    R8Command r8Command = r8Builder.makeR8Command(options.dexItemFactory());
+    // TODO(b/391572031): Configure library desugaring.
+    R8Command r8Command =
+        r8Builder.makeR8Command(options.dexItemFactory(), options.getProguardConfiguration());
     AndroidApp r8App = r8Command.getInputApp();
     if (r8InputAppConsumer != null) {
       r8InputAppConsumer.accept(r8App);
@@ -221,8 +213,7 @@
     InternalOptions r8Options = r8Command.getInternalOptions();
     options.partialCompilationConfiguration.r8OptionsConsumer.accept(r8Options);
     r8Options.partialSubCompilationConfiguration =
-        new R8PartialR8SubCompilationConfiguration(
-            desugarResult.getOutputClasses(), dexingResult.getOutputClasses(), timing);
+        new R8PartialR8SubCompilationConfiguration(dexingResult.getOutputClasses(), timing);
     r8Options.mapConsumer = options.mapConsumer;
     if (options.androidResourceProvider != null) {
       r8Options.androidResourceProvider = options.androidResourceProvider;
@@ -231,8 +222,4 @@
     }
     R8.runInternal(r8App, r8Options, executor);
   }
-
-  private Path resolveTmp(String string) throws IOException {
-    return options.partialCompilationConfiguration.getTempDir().resolve(string);
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index 8890de3..d7a0d7d 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -449,7 +449,9 @@
     }
 
     void readSources() throws IOException, ResourceException, ExecutionException {
-      Collection<ProgramResource> resources = inputApp.computeAllProgramResources();
+      Collection<ProgramResource> resources =
+          inputApp.computeAllProgramResources(
+              internalProvider -> programClasses.addAll(internalProvider.getClasses()));
       List<ProgramResource> dexResources = new ArrayList<>(resources.size());
       List<ProgramResource> cfResources = new ArrayList<>(resources.size());
       for (ProgramResource resource : resources) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
index 25248cb..57225a5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
@@ -17,7 +17,10 @@
 import com.android.tools.r8.utils.structural.StructuralItem;
 import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
+import java.util.Collection;
+import java.util.IdentityHashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
@@ -66,6 +69,37 @@
     assert kind == Kind.CF : "Invalid kind " + kind + " for class-path class " + type;
   }
 
+  public static DexClasspathClass toClasspathClass(DexProgramClass programClass) {
+    return new DexClasspathClass(
+        programClass.getType(),
+        programClass.getOriginKind(),
+        programClass.getOrigin(),
+        programClass.getAccessFlags(),
+        programClass.getSuperType(),
+        programClass.getInterfaces(),
+        programClass.getSourceFile(),
+        programClass.getNestHostClassAttribute(),
+        programClass.getNestMembersClassAttributes(),
+        programClass.getPermittedSubclassAttributes(),
+        programClass.getRecordComponents(),
+        programClass.getEnclosingMethodAttribute(),
+        programClass.getInnerClasses(),
+        programClass.getClassSignature(),
+        programClass.annotations(),
+        FieldCollectionFactory.fromFieldCollection(programClass.getFieldCollection()),
+        MethodCollectionFactory.fromMethodCollection(programClass.getMethodCollection()),
+        false);
+  }
+
+  public static Map<DexType, DexClasspathClass> toClasspathClasses(
+      Collection<DexProgramClass> programClasses) {
+    Map<DexType, DexClasspathClass> classpathClasses = new IdentityHashMap<>(programClasses.size());
+    for (DexProgramClass programClass : programClasses) {
+      classpathClasses.put(programClass.getType(), toClasspathClass(programClass));
+    }
+    return classpathClasses;
+  }
+
   @Override
   public void accept(
       Consumer<DexProgramClass> programClassConsumer,
diff --git a/src/main/java/com/android/tools/r8/graph/MethodCollection.java b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
index 17b7e8b..b41851f 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
@@ -32,6 +32,10 @@
         DexEncodedMethod[] directs, DexEncodedMethod[] virtuals) {
       return holder -> MethodCollection.create(holder, directs, virtuals);
     }
+
+    static MethodCollectionFactory fromMethodCollection(MethodCollection collection) {
+      return holder -> new MethodCollection(holder, collection.backing);
+    }
   }
 
   // Threshold between using an array and a map for the backing store.
diff --git a/src/main/java/com/android/tools/r8/partial/R8PartialInput.java b/src/main/java/com/android/tools/r8/partial/R8PartialInput.java
index 0367701..5698cb6 100644
--- a/src/main/java/com/android/tools/r8/partial/R8PartialInput.java
+++ b/src/main/java/com/android/tools/r8/partial/R8PartialInput.java
@@ -3,67 +3,72 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.partial;
 
-import static java.nio.charset.StandardCharsets.UTF_8;
 
 import com.android.tools.r8.BaseCompilerCommand;
 import com.android.tools.r8.D8Command;
 import com.android.tools.r8.R8Command;
-import com.android.tools.r8.dump.CompilerDump;
-import com.android.tools.r8.tracereferences.TraceReferencesCommand;
-import com.android.tools.r8.utils.ArchiveDataResourceProvider;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClasspathClass;
+import com.android.tools.r8.graph.DexLibraryClass;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.InternalClasspathOrLibraryClassProvider;
+import com.android.tools.r8.utils.InternalProgramClassProvider;
+import com.android.tools.r8.utils.MapUtils;
 import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
+import java.util.Collection;
+import java.util.IdentityHashMap;
+import java.util.Map;
 
 public class R8PartialInput {
 
-  private final Path d8Program;
-  private final Path r8Program;
-  private final CompilerDump dump;
+  private final Collection<DexProgramClass> d8Classes;
+  private final Collection<DexProgramClass> r8Classes;
+  private final Map<DexType, DexClasspathClass> classpathClasses;
+  private final Map<DexType, DexLibraryClass> libraryClasses;
 
-  public R8PartialInput(Path d8Program, Path r8Program, CompilerDump dump) {
-    this.d8Program = d8Program;
-    this.r8Program = r8Program;
-    this.dump = dump;
+  public R8PartialInput(
+      Collection<DexProgramClass> d8Classes,
+      Collection<DexProgramClass> r8Classes,
+      Collection<DexClasspathClass> classpathClasses,
+      Collection<DexLibraryClass> libraryClasses) {
+    this.d8Classes = d8Classes;
+    this.r8Classes = r8Classes;
+    this.classpathClasses =
+        MapUtils.transform(classpathClasses, IdentityHashMap::new, DexClass::getType);
+    this.libraryClasses =
+        MapUtils.transform(libraryClasses, IdentityHashMap::new, DexClass::getType);
   }
 
   public void configure(D8Command.Builder commandBuilder) throws IOException {
     configureBase(commandBuilder);
-    configureDesugaredLibrary(commandBuilder);
-    commandBuilder.addProgramFiles(d8Program).addClasspathFiles(r8Program);
+    commandBuilder
+        .addProgramResourceProvider(new InternalProgramClassProvider(d8Classes))
+        .addClasspathResourceProvider(
+            new InternalClasspathOrLibraryClassProvider<>(
+                DexClasspathClass.toClasspathClasses(r8Classes)));
   }
 
-  public void configureDesugar(D8Command.Builder commandBuilder) throws IOException {
+  public void configureDesugar(D8Command.Builder commandBuilder) {
     configureBase(commandBuilder);
-    commandBuilder.addProgramFiles(r8Program).addClasspathFiles(d8Program);
+    commandBuilder
+        .addProgramResourceProvider(new InternalProgramClassProvider(r8Classes))
+        .addClasspathResourceProvider(
+            new InternalClasspathOrLibraryClassProvider<>(
+                DexClasspathClass.toClasspathClasses(d8Classes)));
   }
 
   public void configure(R8Command.Builder commandBuilder) throws IOException {
     configureBase(commandBuilder);
-    configureDesugaredLibrary(commandBuilder);
+    commandBuilder.addClasspathResourceProvider(
+        new InternalClasspathOrLibraryClassProvider<>(
+            DexClasspathClass.toClasspathClasses(d8Classes)));
+  }
+
+  private void configureBase(BaseCompilerCommand.Builder<?, ?> commandBuilder) {
     commandBuilder
-        .addProgramResourceProvider(new ArchiveDataResourceProvider(r8Program))
-        .addClasspathFiles(d8Program)
-        .addProguardConfigurationFiles(dump.getProguardConfigFile());
-  }
-
-  public void configure(TraceReferencesCommand.Builder commandBuilder) throws IOException {
-    commandBuilder.addLibraryFiles(dump.getLibraryArchive()).addSourceFiles(d8Program);
-  }
-
-  private void configureBase(BaseCompilerCommand.Builder<?, ?> commandBuilder) throws IOException {
-    commandBuilder
-        .addClasspathFiles(dump.getClasspathArchive())
-        .addLibraryFiles(dump.getLibraryArchive())
-        .setMinApiLevel(dump.getBuildProperties().getMinApi())
-        .setMode(dump.getBuildProperties().getCompilationMode());
-  }
-
-  private void configureDesugaredLibrary(BaseCompilerCommand.Builder<?, ?> commandBuilder)
-      throws IOException {
-    if (dump.hasDesugaredLibrary()) {
-      commandBuilder.addDesugaredLibraryConfiguration(
-          Files.readString(dump.getDesugaredLibraryFile(), UTF_8));
-    }
+        .addClasspathResourceProvider(
+            new InternalClasspathOrLibraryClassProvider<>(classpathClasses))
+        .addLibraryResourceProvider(new InternalClasspathOrLibraryClassProvider<>(libraryClasses));
   }
 }
diff --git a/src/main/java/com/android/tools/r8/partial/R8PartialProgramPartioning.java b/src/main/java/com/android/tools/r8/partial/R8PartialProgramPartioning.java
new file mode 100644
index 0000000..2e8feb4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/partial/R8PartialProgramPartioning.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2025, 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.partial;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.utils.R8PartialCompilationConfiguration;
+import com.android.tools.r8.utils.WorkList;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/** Partitions the input classes to R8 partial into the classes for D8 and R8. */
+public class R8PartialProgramPartioning {
+
+  private final Set<DexProgramClass> d8Classes = new LinkedHashSet<>();
+  private final Set<DexProgramClass> r8Classes = new LinkedHashSet<>();
+
+  private R8PartialProgramPartioning() {}
+
+  public static R8PartialProgramPartioning create(DirectMappedDexApplication app) {
+    R8PartialProgramPartioning partioning = new R8PartialProgramPartioning();
+    R8PartialCompilationConfiguration partialCompilationConfiguration =
+        app.options.partialCompilationConfiguration;
+    for (DexProgramClass clazz : app.classes()) {
+      if (partialCompilationConfiguration.test(clazz)) {
+        partioning.r8Classes.add(clazz);
+      } else {
+        partioning.d8Classes.add(clazz);
+      }
+    }
+    // Collect all transitive superclasses of all D8 classes and treat these as D8 classes.
+    WorkList<DexClass> worklist = WorkList.newIdentityWorkList(partioning.d8Classes);
+    worklist.process(
+        clazz ->
+            clazz.forEachImmediateSuperClassMatching(
+                app,
+                (supertype, superclass) -> superclass != null && !superclass.isLibraryClass(),
+                (supertype, superclass) -> {
+                  if (superclass.isProgramClass()
+                      && partioning.r8Classes.remove(superclass.asProgramClass())) {
+                    partioning.d8Classes.add(superclass.asProgramClass());
+                  }
+                  worklist.addIfNotSeen(superclass);
+                }));
+    return partioning;
+  }
+
+  public Set<DexProgramClass> getD8Classes() {
+    return d8Classes;
+  }
+
+  public Set<DexProgramClass> getR8Classes() {
+    return r8Classes;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/partial/R8PartialSubCompilationConfiguration.java b/src/main/java/com/android/tools/r8/partial/R8PartialSubCompilationConfiguration.java
index 5725e15..35d84d1 100644
--- a/src/main/java/com/android/tools/r8/partial/R8PartialSubCompilationConfiguration.java
+++ b/src/main/java/com/android/tools/r8/partial/R8PartialSubCompilationConfiguration.java
@@ -82,15 +82,12 @@
   public static class R8PartialR8SubCompilationConfiguration
       extends R8PartialSubCompilationConfiguration {
 
-    private Collection<DexProgramClass> desugaringOutputClasses;
     private Collection<DexProgramClass> dexingOutputClasses;
 
     public R8PartialR8SubCompilationConfiguration(
-        Collection<DexProgramClass> desugaringOutputClasses,
         Collection<DexProgramClass> dexingOutputClasses,
         Timing timing) {
       super(timing);
-      this.desugaringOutputClasses = desugaringOutputClasses;
       this.dexingOutputClasses = dexingOutputClasses;
     }
 
@@ -99,15 +96,6 @@
       return dexingOutputClasses;
     }
 
-    public DirectMappedDexApplication commitDesugaringOutputClasses(
-        DirectMappedDexApplication app) {
-      assert app.classes().isEmpty();
-      DirectMappedDexApplication newApp =
-          app.builder().addProgramClasses(desugaringOutputClasses).build();
-      desugaringOutputClasses = null;
-      return newApp;
-    }
-
     public void commitDexingOutputClasses(AppView<? extends AppInfoWithClassHierarchy> appView) {
       Set<DexType> dexingOutputTypes =
           SetUtils.mapIdentityHashSet(dexingOutputClasses, DexClass::getType);
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 6672f02..f3c1487 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -267,11 +267,21 @@
     return bytes;
   }
 
-  /** Get full collection of all program resources from all program providers. */
   public Collection<ProgramResource> computeAllProgramResources() throws ResourceException {
+    return computeAllProgramResources(null);
+  }
+
+  /** Get full collection of all program resources from all program providers. */
+  public Collection<ProgramResource> computeAllProgramResources(
+      Consumer<InternalProgramClassProvider> internalProviderConsumer) throws ResourceException {
     List<ProgramResource> resources = new ArrayList<>();
     for (ProgramResourceProvider provider : programResourceProviders) {
-      resources.addAll(provider.getProgramResources());
+      if (provider instanceof InternalProgramClassProvider) {
+        InternalProgramClassProvider internalProvider = (InternalProgramClassProvider) provider;
+        internalProviderConsumer.accept(internalProvider);
+      } else {
+        resources.addAll(provider.getProgramResources());
+      }
     }
     return resources;
   }
diff --git a/src/main/java/com/android/tools/r8/utils/ArchiveDataResourceProvider.java b/src/main/java/com/android/tools/r8/utils/ArchiveDataResourceProvider.java
deleted file mode 100644
index b93663e..0000000
--- a/src/main/java/com/android/tools/r8/utils/ArchiveDataResourceProvider.java
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) 2025, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.utils;
-
-import com.android.tools.r8.ProgramResource;
-import com.android.tools.r8.ResourceException;
-import com.android.tools.r8.shaking.FilteredClassPath;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.Collections;
-
-public class ArchiveDataResourceProvider extends ArchiveResourceProvider {
-
-  public ArchiveDataResourceProvider(Path archive) {
-    super(FilteredClassPath.unfiltered(archive), false);
-  }
-
-  @Override
-  public Collection<ProgramResource> getProgramResources() throws ResourceException {
-    return Collections.emptyList();
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/utils/CfLineToMethodMapper.java b/src/main/java/com/android/tools/r8/utils/CfLineToMethodMapper.java
index d4acc5d..cbd9561 100644
--- a/src/main/java/com/android/tools/r8/utils/CfLineToMethodMapper.java
+++ b/src/main/java/com/android/tools/r8/utils/CfLineToMethodMapper.java
@@ -39,6 +39,10 @@
   private void readLineNumbersFromClassFiles() throws ResourceException {
     ClassVisitor classVisitor = new ClassVisitor();
     for (ProgramResourceProvider resourceProvider : inputApp.getProgramResourceProviders()) {
+      // TODO(b/391785584): Do not use the input providers here.
+      if (resourceProvider instanceof InternalProgramClassProvider) {
+        continue;
+      }
       if (resourceProvider instanceof ArchiveResourceProvider) {
         ArchiveResourceProvider provider = (ArchiveResourceProvider) resourceProvider;
         provider.accept(
diff --git a/src/main/java/com/android/tools/r8/utils/ClassProvider.java b/src/main/java/com/android/tools/r8/utils/ClassProvider.java
index e46606e..9608497 100644
--- a/src/main/java/com/android/tools/r8/utils/ClassProvider.java
+++ b/src/main/java/com/android/tools/r8/utils/ClassProvider.java
@@ -94,8 +94,18 @@
       this.reader = reader;
     }
 
+    @SuppressWarnings("unchecked")
     @Override
     public void collectClass(DexType type, Consumer<T> classConsumer) {
+      if (provider instanceof InternalClasspathOrLibraryClassProvider) {
+        InternalClasspathOrLibraryClassProvider<T> internalProvider =
+            (InternalClasspathOrLibraryClassProvider<T>) provider;
+        T clazz = internalProvider.getClass(type);
+        if (clazz != null) {
+          classConsumer.accept(clazz);
+        }
+        return;
+      }
       String descriptor = type.descriptor.toString();
       ProgramResource resource = provider.getProgramResource(descriptor);
       if (resource != null) {
@@ -111,6 +121,11 @@
 
     @Override
     public Collection<DexType> collectTypes() {
+      if (provider instanceof InternalClasspathOrLibraryClassProvider) {
+        InternalClasspathOrLibraryClassProvider<?> internalProvider =
+            (InternalClasspathOrLibraryClassProvider<?>) provider;
+        return internalProvider.getTypes();
+      }
       List<DexType> types = new ArrayList<>();
       for (String descriptor : provider.getClassDescriptors()) {
         types.add(reader.options.itemFactory.createType(descriptor));
diff --git a/src/main/java/com/android/tools/r8/utils/InternalClasspathOrLibraryClassProvider.java b/src/main/java/com/android/tools/r8/utils/InternalClasspathOrLibraryClassProvider.java
new file mode 100644
index 0000000..cb002a6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/InternalClasspathOrLibraryClassProvider.java
@@ -0,0 +1,41 @@
+// Copyright (c) 2025, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.ClassFileResourceProvider;
+import com.android.tools.r8.ProgramResource;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexType;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+public class InternalClasspathOrLibraryClassProvider<T extends DexClass>
+    implements ClassFileResourceProvider {
+
+  private final Map<DexType, T> classes;
+
+  public InternalClasspathOrLibraryClassProvider(Map<DexType, T> classes) {
+    this.classes = classes;
+  }
+
+  public T getClass(DexType type) {
+    return classes.get(type);
+  }
+
+  public Collection<DexType> getTypes() {
+    return classes.keySet();
+  }
+
+  @Override
+  public Set<String> getClassDescriptors() {
+    throw new Unreachable();
+  }
+
+  @Override
+  public ProgramResource getProgramResource(String descriptor) {
+    throw new Unreachable();
+  }
+}
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 be13063..02659d4 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -993,6 +993,10 @@
 
   public boolean debug = false;
 
+  public CompilationMode getCompilationMode() {
+    return debug ? CompilationMode.DEBUG : CompilationMode.RELEASE;
+  }
+
   public boolean shouldCompileMethodInDebugMode(ProgramMethod method) {
     return debug || method.isReachabilitySensitive();
   }
diff --git a/src/main/java/com/android/tools/r8/utils/InternalProgramClassProvider.java b/src/main/java/com/android/tools/r8/utils/InternalProgramClassProvider.java
new file mode 100644
index 0000000..d0d8552
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/InternalProgramClassProvider.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2025, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.ProgramResource;
+import com.android.tools.r8.ProgramResourceProvider;
+import com.android.tools.r8.ResourceException;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexProgramClass;
+import java.util.Collection;
+
+public class InternalProgramClassProvider implements ProgramResourceProvider {
+
+  private final Collection<DexProgramClass> classes;
+
+  public InternalProgramClassProvider(Collection<DexProgramClass> classes) {
+    this.classes = classes;
+  }
+
+  public Collection<DexProgramClass> getClasses() {
+    return classes;
+  }
+
+  @Override
+  public Collection<ProgramResource> getProgramResources() throws ResourceException {
+    throw new Unreachable();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/MapUtils.java b/src/main/java/com/android/tools/r8/utils/MapUtils.java
index 13a6ba2..26208fc 100644
--- a/src/main/java/com/android/tools/r8/utils/MapUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/MapUtils.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.utils;
 
+import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.utils.StringUtils.BraceType;
 import com.google.common.collect.ImmutableMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
@@ -117,6 +118,50 @@
         ",", map.entrySet(), entry -> entry.getKey() + ":" + entry.getValue(), BraceType.TUBORG);
   }
 
+  public static <K, V> Map<K, V> transform(
+      Collection<V> collection, IntFunction<Map<K, V>> factory, Function<V, K> keyMapping) {
+    return transform(collection, factory, keyMapping, Function.identity());
+  }
+
+  public static <T, K, V> Map<K, V> transform(
+      Collection<T> collection,
+      IntFunction<Map<K, V>> factory,
+      Function<T, K> keyMapping,
+      Function<T, V> valueMapping) {
+    return transform(
+        collection,
+        factory,
+        keyMapping,
+        valueMapping,
+        (key, existingValue, value) -> {
+          throw new Unreachable();
+        });
+  }
+
+  public static <T, K, V> Map<K, V> transform(
+      Collection<T> collection,
+      IntFunction<Map<K, V>> factory,
+      Function<T, K> keyMapping,
+      Function<T, V> valueMapping,
+      TriFunction<K, V, V, V> valueMerger) {
+    Map<K, V> result = factory.apply(collection.size());
+    for (T element : collection) {
+      K key = keyMapping.apply(element);
+      if (key == null) {
+        continue;
+      }
+      V value = valueMapping.apply(element);
+      if (value == null) {
+        continue;
+      }
+      V existingValue = result.put(key, value);
+      if (existingValue != null) {
+        result.put(key, valueMerger.apply(key, existingValue, value));
+      }
+    }
+    return result;
+  }
+
   public static <K1, V1, K2, V2> Map<K2, V2> transform(
       Map<K1, V1> map,
       IntFunction<Map<K2, V2>> factory,
diff --git a/src/main/java/com/android/tools/r8/utils/WorkList.java b/src/main/java/com/android/tools/r8/utils/WorkList.java
index ed63185..aac9d19 100644
--- a/src/main/java/com/android/tools/r8/utils/WorkList.java
+++ b/src/main/java/com/android/tools/r8/utils/WorkList.java
@@ -51,7 +51,7 @@
     return workList;
   }
 
-  public static <T> WorkList<T> newIdentityWorkList(Iterable<T> items) {
+  public static <T> WorkList<T> newIdentityWorkList(Iterable<? extends T> items) {
     WorkList<T> workList = new WorkList<>(EqualityTest.IDENTITY);
     workList.addIfNotSeen(items);
     return workList;