Skeleton for lir-to-lir library desugaring
Bug: b/391572031
Change-Id: I9f6a485fbff5d5de21c6a5ab0cf861998df1d9ce
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index a39590c..4b63983 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -47,6 +47,7 @@
import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAmender;
import com.android.tools.r8.ir.desugar.desugaredlibrary.PrefixRewritingNamingLens;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.R8LibraryDesugaring;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
import com.android.tools.r8.ir.desugar.records.RecordFieldValuesRewriter;
import com.android.tools.r8.ir.desugar.records.RecordInstructionDesugaring;
@@ -729,6 +730,8 @@
new NonStartupInStartupOutliner(appView).runIfNecessary(executorService, timing);
+ R8LibraryDesugaring.runIfNecessary(appView, executorService, timing);
+
if (appView.appInfo().hasLiveness()) {
SyntheticFinalization.finalizeWithLiveness(appView.withLiveness(), executorService, timing);
} else {
diff --git a/src/main/java/com/android/tools/r8/graph/lens/MethodLookupResult.java b/src/main/java/com/android/tools/r8/graph/lens/MethodLookupResult.java
index f58004e..23eb2e1 100644
--- a/src/main/java/com/android/tools/r8/graph/lens/MethodLookupResult.java
+++ b/src/main/java/com/android/tools/r8/graph/lens/MethodLookupResult.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
import com.android.tools.r8.ir.code.InvokeType;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.R8LibraryDesugaringGraphLens;
import com.android.tools.r8.optimize.bridgehoisting.BridgeHoistingLens;
import com.android.tools.r8.utils.OptionalBool;
@@ -20,6 +21,7 @@
public class MethodLookupResult extends MemberLookupResult<DexMethod> {
private final OptionalBool isInterface;
+ private final boolean needsDesugaredLibraryApiConversion;
private final InvokeType type;
private final RewrittenPrototypeDescription prototypeChanges;
@@ -27,10 +29,12 @@
DexMethod reference,
DexMethod reboundReference,
OptionalBool isInterface,
+ boolean needsDesugaredLibraryApiConversion,
InvokeType type,
RewrittenPrototypeDescription prototypeChanges) {
super(reference, reboundReference);
this.isInterface = isInterface;
+ this.needsDesugaredLibraryApiConversion = needsDesugaredLibraryApiConversion;
this.type = type;
this.prototypeChanges = prototypeChanges;
}
@@ -43,6 +47,10 @@
return isInterface;
}
+ public boolean isNeedsDesugaredLibraryApiConversionSet() {
+ return needsDesugaredLibraryApiConversion;
+ }
+
public InvokeType getType() {
return type;
}
@@ -62,6 +70,7 @@
|| lens.isEnumUnboxerLens()
|| lens.isNumberUnboxerLens()
|| lens instanceof BridgeHoistingLens
+ || lens instanceof R8LibraryDesugaringGraphLens
: lens;
return this;
}
@@ -72,6 +81,7 @@
private final GraphLens codeLens;
private OptionalBool isInterface = OptionalBool.UNKNOWN;
+ private boolean needsDesugaredLibraryApiConversion = false;
private RewrittenPrototypeDescription prototypeChanges = RewrittenPrototypeDescription.none();
private InvokeType type;
@@ -89,6 +99,11 @@
return this;
}
+ public Builder setNeedsDesugaredLibraryApiConversion() {
+ this.needsDesugaredLibraryApiConversion = true;
+ return this;
+ }
+
public Builder setPrototypeChanges(RewrittenPrototypeDescription prototypeChanges) {
this.prototypeChanges = prototypeChanges;
return this;
@@ -101,7 +116,12 @@
public MethodLookupResult build() {
return new MethodLookupResult(
- reference, reboundReference, isInterface, type, prototypeChanges)
+ reference,
+ reboundReference,
+ isInterface,
+ needsDesugaredLibraryApiConversion,
+ type,
+ prototypeChanges)
.verify(lens, codeLens);
}
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 9f294ee..b8ef523 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
@@ -119,6 +119,15 @@
appView, profileCollectionAdditions, eventConsumer);
}
+ public static CfInstructionDesugaringEventConsumer createForR8LirToLirLibraryDesugaring(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ ProfileCollectionAdditions profileCollectionAdditions) {
+ CfInstructionDesugaringEventConsumer eventConsumer =
+ new R8LibraryDesugaringCfInstructionDesugaringEventConsumer();
+ return ProfileRewritingCfInstructionDesugaringEventConsumer.attach(
+ appView, profileCollectionAdditions, eventConsumer);
+ }
+
public abstract List<ProgramMethod> finalizeDesugaring();
public abstract boolean verifyNothingToFinalize();
@@ -851,4 +860,268 @@
// Intentionally empty. The method will be hit by tracing if required.
}
}
+
+ public static class R8LibraryDesugaringCfInstructionDesugaringEventConsumer
+ extends CfInstructionDesugaringEventConsumer {
+
+ private R8LibraryDesugaringCfInstructionDesugaringEventConsumer() {}
+
+ @Override
+ public void acceptAPIConversionOutline(ProgramMethod method, ProgramMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptClasspathEmulatedInterface(DexClasspathClass clazz) {
+ assert false;
+ }
+
+ @Override
+ public void acceptCollectionConversion(ProgramMethod method, ProgramMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptConstantDynamicRewrittenBootstrapMethod(
+ ProgramMethod method, DexMethod oldSignature) {
+ assert false;
+ }
+
+ @Override
+ public void acceptCovariantRetargetMethod(ProgramMethod method, ProgramMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptDesugaredLibraryRetargeterDispatchClasspathClass(DexClasspathClass clazz) {
+ assert false;
+ }
+
+ @Override
+ public void acceptEnumConversionClasspathClass(DexClasspathClass clazz) {
+ assert false;
+ }
+
+ @Override
+ public void acceptGenericApiConversionStub(DexClasspathClass clazz) {
+ assert false;
+ }
+
+ @Override
+ public void acceptWrapperClasspathClass(DexClasspathClass clazz) {
+ assert false;
+ }
+
+ @Override
+ public List<ProgramMethod> finalizeDesugaring() {
+ assert false;
+ return Collections.emptyList();
+ }
+
+ @Override
+ public boolean verifyNothingToFinalize() {
+ assert false;
+ return true;
+ }
+
+ @Override
+ public void acceptOutlinedMethod(ProgramMethod outlinedMethod, ProgramMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptBackportedMethod(ProgramMethod backportedMethod, ProgramMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptBackportedClass(DexProgramClass backportedClass, ProgramMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptConstantDynamicClass(
+ ConstantDynamicClass constantDynamicClass, ProgramMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptAutoCloseableDispatchMethod(ProgramMethod method, ProgramDefinition context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptAutoCloseableForwardingMethod(
+ ProgramMethod method, ProgramDefinition context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptDesugaredLibraryBridge(ProgramMethod method, ProgramMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptInvokeSpecialBridgeInfo(InvokeSpecialBridgeInfo info) {
+ assert false;
+ }
+
+ @Override
+ public void acceptCompanionClassClinit(ProgramMethod method, ProgramMethod companionMethod) {
+ assert false;
+ }
+
+ @Override
+ public void acceptDefaultAsCompanionMethod(
+ ProgramMethod method, ProgramMethod companionMethod) {
+ assert false;
+ }
+
+ @Override
+ public void acceptPrivateAsCompanionMethod(
+ ProgramMethod method, ProgramMethod companionMethod) {
+ assert false;
+ }
+
+ @Override
+ public void acceptStaticAsCompanionMethod(ProgramMethod method, ProgramMethod companionMethod) {
+ assert false;
+ }
+
+ @Override
+ public void acceptInvokeStaticInterfaceOutliningMethod(
+ ProgramMethod method, ProgramMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptInvokeObjectCloneOutliningMethod(
+ ProgramMethod method, ProgramMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptLambdaClass(LambdaClass lambdaClass, ProgramMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptNestConstructorBridge(
+ ProgramMethod target,
+ ProgramMethod bridge,
+ DexClass argumentClass,
+ DexClassAndMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptNestFieldGetBridge(
+ ProgramField target, ProgramMethod bridge, DexClassAndMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptNestFieldPutBridge(
+ ProgramField target, ProgramMethod bridge, DexClassAndMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptNestMethodBridge(
+ ProgramMethod target, ProgramMethod bridge, DexClassAndMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptRecordClass(DexProgramClass recordTagClass) {
+ assert false;
+ }
+
+ @Override
+ public void acceptRecordClassContext(DexProgramClass recordTagClass, ProgramMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptRecordEqualsHelperMethod(ProgramMethod method, ProgramMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptRecordGetFieldsAsObjectsHelperMethod(
+ ProgramMethod method, ProgramMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptRecordHashCodeHelperMethod(ProgramMethod method, ProgramMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptRecordToStringHelperMethod(ProgramMethod method, ProgramMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptTwrCloseResourceMethod(ProgramMethod closeMethod, ProgramMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptTypeSwitchMethod(ProgramMethod typeSwitchMethod, ProgramMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptTypeSwitchClass(DexProgramClass typeSwitchClass, ProgramMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptVarHandleDesugaringClass(DexProgramClass clazz) {
+ assert false;
+ }
+
+ @Override
+ public void acceptVarHandleDesugaringClassContext(
+ DexProgramClass clazz, ProgramDefinition context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptUtilityToStringIfNotNullMethod(ProgramMethod method, ProgramMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptUtilityThrowClassCastExceptionIfNotNullMethod(
+ ProgramMethod method, ProgramMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptUtilityThrowIllegalAccessErrorMethod(
+ ProgramMethod method, ProgramMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptUtilityThrowIncompatibleClassChangeErrorMethod(
+ ProgramMethod method, ProgramMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptUtilityThrowNoSuchMethodErrorMethod(
+ ProgramMethod method, ProgramMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptUtilityThrowRuntimeExceptionWithMessageMethod(
+ ProgramMethod method, ProgramMethod context) {
+ assert false;
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
index 22b75bb..996a13a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.desugar;
+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.DexClass;
import com.android.tools.r8.graph.DexProgramClass;
@@ -36,6 +38,13 @@
return empty();
}
+ @SuppressWarnings("DoNotCallSuggester")
+ public static CfPostProcessingDesugaringCollection createForR8LirToLirLibraryDesugaring(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ InterfaceMethodProcessorFacade interfaceDesugaring) {
+ throw new Unreachable();
+ }
+
static CfPostProcessingDesugaringCollection empty() {
return EmptyCfPostProcessingDesugaringCollection.getInstance();
}
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 e1a3cd9..2c8e392 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
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.desugar;
+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;
@@ -63,6 +64,15 @@
appView, profileCollectionAdditions, eventConsumer);
}
+ public static CfPostProcessingDesugaringEventConsumer createForR8LirToLirLibraryDesugaring(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ ProfileCollectionAdditions profileCollectionAdditions) {
+ CfPostProcessingDesugaringEventConsumer eventConsumer =
+ new R8LibraryDesugaringPostProcessingDesugaringEventConsumer();
+ return ProfileRewritingCfPostProcessingDesugaringEventConsumer.attach(
+ appView, profileCollectionAdditions, eventConsumer);
+ }
+
public abstract Set<DexMethod> getNewlyLiveMethods();
public abstract void finalizeDesugaring() throws ExecutionException;
@@ -309,4 +319,109 @@
additions.addLiveMethod(method);
}
}
+
+ public static class R8LibraryDesugaringPostProcessingDesugaringEventConsumer
+ extends CfPostProcessingDesugaringEventConsumer {
+
+ private R8LibraryDesugaringPostProcessingDesugaringEventConsumer() {}
+
+ @Override
+ public void acceptAPIConversionCallback(
+ ProgramMethod callbackMethod, ProgramMethod convertedMethod) {
+ assert false;
+ }
+
+ @Override
+ public void acceptDesugaredLibraryRetargeterDispatchClasspathClass(DexClasspathClass clazz) {
+ assert false;
+ }
+
+ @Override
+ public void acceptDesugaredLibraryRetargeterForwardingMethod(
+ ProgramMethod method, EmulatedDispatchMethodDescriptor descriptor) {
+ assert false;
+ }
+
+ @Override
+ public void acceptEmulatedInterfaceMarkerInterface(
+ DexProgramClass clazz, DexClasspathClass newInterface) {
+ assert false;
+ }
+
+ @Override
+ public void acceptInterfaceInjection(DexProgramClass clazz, DexClass newInterface) {
+ assert false;
+ }
+
+ @Override
+ public void acceptInterfaceMethodDesugaringForwardingMethod(
+ ProgramMethod method, DexClassAndMethod baseMethod) {
+ assert false;
+ }
+
+ @Override
+ public void acceptWrapperClasspathClass(DexClasspathClass clazz) {
+ assert false;
+ }
+
+ @Override
+ public Set<DexMethod> getNewlyLiveMethods() {
+ assert false;
+ return Collections.emptySet();
+ }
+
+ @Override
+ public void finalizeDesugaring() {
+ assert false;
+ }
+
+ @Override
+ public void warnMissingInterface(
+ DexProgramClass context, DexType missing, InterfaceDesugaringSyntheticHelper helper) {
+ assert false;
+ }
+
+ @Override
+ public void acceptCollectionConversion(ProgramMethod arrayConversion, ProgramMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptEnumConversionClasspathClass(DexClasspathClass clazz) {
+ assert false;
+ }
+
+ @Override
+ public void acceptGenericApiConversionStub(DexClasspathClass dexClasspathClass) {
+ assert false;
+ }
+
+ @Override
+ public void acceptAutoCloseableDispatchMethod(ProgramMethod method, ProgramDefinition context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptAutoCloseableForwardingMethod(
+ ProgramMethod method, ProgramDefinition context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptAutoCloseableInterfaceInjection(
+ DexProgramClass clazz, DexClass newInterface) {
+ assert false;
+ }
+
+ @Override
+ public void acceptCovariantRetargetMethod(ProgramMethod method, ProgramMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptThrowingMethod(
+ ProgramMethod method, DexType errorType, FailedResolutionResult resolutionResult) {
+ assert false;
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/LibraryDesugaringOptions.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/LibraryDesugaringOptions.java
index b350cb3..bfc67df 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/LibraryDesugaringOptions.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/LibraryDesugaringOptions.java
@@ -70,10 +70,18 @@
return machineDesugaredLibrarySpecification.isLibraryCompilation();
}
+ public boolean isEnabled() {
+ return !machineDesugaredLibrarySpecification.isEmpty();
+ }
+
public boolean isL8() {
return !synthesizedClassPrefix.isEmpty();
}
+ public boolean isR8LirToLirLibraryDesugaringEnabled() {
+ return options.partialSubCompilationConfiguration != null;
+ }
+
public void resetDesugaredLibrarySpecificationForTesting() {
loadMachineDesugaredLibrarySpecification = null;
machineDesugaredLibrarySpecification = DesugaredLibrarySpecification.empty();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/R8LibraryDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/R8LibraryDesugaring.java
new file mode 100644
index 0000000..915fe93
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/R8LibraryDesugaring.java
@@ -0,0 +1,173 @@
+// 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.ir.desugar.desugaredlibrary;
+
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.conversion.IRFinalizer;
+import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
+import com.android.tools.r8.ir.conversion.LirConverter;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringCollection;
+import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
+import com.android.tools.r8.ir.optimize.DeadCodeRemover;
+import com.android.tools.r8.profile.rewriting.ProfileCollectionAdditions;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import com.android.tools.r8.utils.collections.ProgramMethodSet.ConcurrentProgramMethodSet;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+public class R8LibraryDesugaring {
+
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
+ private final InternalOptions options;
+
+ public R8LibraryDesugaring(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ this.appView = appView;
+ this.options = appView.options();
+ }
+
+ public static void runIfNecessary(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ ExecutorService executorService,
+ Timing timing)
+ throws ExecutionException {
+ InternalOptions options = appView.options();
+ if (options.isDesugaring()
+ && options.getLibraryDesugaringOptions().isEnabled()
+ && options.getLibraryDesugaringOptions().isR8LirToLirLibraryDesugaringEnabled()
+ && options.isGeneratingDex()) {
+ new R8LibraryDesugaring(appView).run(executorService, timing);
+ }
+ }
+
+ private void run(ExecutorService executorService, Timing timing) throws ExecutionException {
+ // Bring the LIR up-to-date.
+ LirConverter.rewriteLirWithLens(appView, timing, executorService);
+
+ // In R8 partial, commit the D8 classes to the app so that they will also be subject to library
+ // desugaring.
+ partialSubCompilationSetup();
+
+ // Apply library desugaring.
+ ProfileCollectionAdditions profileCollectionAdditions =
+ ProfileCollectionAdditions.create(appView);
+ ConcurrentProgramMethodSet synthesizedMethods = ProgramMethodSet.createConcurrent();
+ runInstructionDesugaring(profileCollectionAdditions, synthesizedMethods, executorService);
+ runPostProcessingDesugaring(
+ profileCollectionAdditions, synthesizedMethods, executorService, timing);
+
+ // Commit profile updates and convert synthesized methods to DEX.
+ profileCollectionAdditions.commit(appView);
+ processSynthesizedMethods(synthesizedMethods, executorService);
+
+ // In R8 partial, uncommit the D8 classes from the app so that they will not be subject to whole
+ // program optimizations.
+ partialSubCompilationTearDown();
+
+ assert !appView.getSyntheticItems().hasPendingSyntheticClasses();
+ }
+
+ @SuppressWarnings("UnusedVariable")
+ private void runInstructionDesugaring(
+ ProfileCollectionAdditions profileCollectionAdditions,
+ ConcurrentProgramMethodSet synthesizedMethods,
+ ExecutorService executorService)
+ throws ExecutionException {
+ ProcessorContext processorContext = appView.createProcessorContext();
+ CfInstructionDesugaringEventConsumer eventConsumer =
+ CfInstructionDesugaringEventConsumer.createForR8LirToLirLibraryDesugaring(
+ appView, profileCollectionAdditions);
+ LensCodeRewriterUtils emptyRewriterUtils = LensCodeRewriterUtils.empty();
+ ThreadUtils.processItems(
+ appView.appInfo().classes(),
+ clazz ->
+ clazz.forEachProgramMethodMatching(
+ method -> method.hasCode() && method.getCode().isLirCode(),
+ method -> {
+ MethodProcessingContext methodProcessingContext =
+ processorContext.createMethodProcessingContext(method);
+ R8LibraryDesugaringGraphLens libraryDesugaringGraphLens =
+ new R8LibraryDesugaringGraphLens(
+ appView, eventConsumer, method, methodProcessingContext);
+ LirConverter.rewriteLirMethodWithLens(
+ method, appView, libraryDesugaringGraphLens, emptyRewriterUtils);
+ }),
+ options.getThreadingModule(),
+ executorService);
+ appView.dexItemFactory().clearTypeElementsCache();
+
+ // Move the pending methods and mark them live and ready for tracing.
+ List<ProgramMethod> needsProcessing = eventConsumer.finalizeDesugaring();
+ assert needsProcessing.isEmpty();
+
+ // TODO(b/391572031): Generate desugared library API tracking warnings.
+
+ // Commit pending synthetics.
+ appView.rebuildAppInfo();
+ }
+
+ @SuppressWarnings("UnusedVariable")
+ private void runPostProcessingDesugaring(
+ ProfileCollectionAdditions profileCollectionAdditions,
+ ConcurrentProgramMethodSet synthesizedMethods,
+ ExecutorService executorService,
+ Timing timing)
+ throws ExecutionException {
+ CfPostProcessingDesugaringEventConsumer eventConsumer =
+ CfPostProcessingDesugaringEventConsumer.createForR8LirToLirLibraryDesugaring(
+ appView, profileCollectionAdditions);
+ InterfaceMethodProcessorFacade interfaceDesugaring =
+ InterfaceMethodProcessorFacade.createForR8LirToLirLibraryDesugaring();
+ CfPostProcessingDesugaringCollection.createForR8LirToLirLibraryDesugaring(
+ appView, interfaceDesugaring)
+ .postProcessingDesugaring(
+ appView.appInfo().classes(), eventConsumer, executorService, timing);
+
+ // Commit pending synthetics.
+ appView.rebuildAppInfo();
+ }
+
+ private void processSynthesizedMethods(
+ ProgramMethodSet synthesizedMethods, ExecutorService executorService)
+ throws ExecutionException {
+ ThreadUtils.processItems(
+ synthesizedMethods,
+ method -> {
+ IRCode code = method.buildIR(appView, MethodConversionOptions.forLirPhase(appView));
+ DeadCodeRemover deadCodeRemover = new DeadCodeRemover(appView);
+ IRFinalizer<?> finalizer =
+ code.getConversionOptions().getFinalizer(deadCodeRemover, appView);
+ Code lirCode =
+ finalizer.finalizeCode(code, BytecodeMetadataProvider.empty(), Timing.empty());
+ method.setCode(lirCode, appView);
+ },
+ options.getThreadingModule(),
+ executorService);
+ }
+
+ private void partialSubCompilationSetup() {
+ if (options.partialSubCompilationConfiguration != null) {
+ options.partialSubCompilationConfiguration.asR8().commitDexingOutputClasses(appView);
+ }
+ }
+
+ private void partialSubCompilationTearDown() {
+ if (options.partialSubCompilationConfiguration != null) {
+ options.partialSubCompilationConfiguration.asR8().uncommitDexingOutputClasses(appView);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/R8LibraryDesugaringGraphLens.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/R8LibraryDesugaringGraphLens.java
new file mode 100644
index 0000000..25a7ae1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/R8LibraryDesugaringGraphLens.java
@@ -0,0 +1,120 @@
+// 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.ir.desugar.desugaredlibrary;
+
+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.DexMethod;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.lens.DefaultNonIdentityGraphLens;
+import com.android.tools.r8.graph.lens.FieldLookupResult;
+import com.android.tools.r8.graph.lens.GraphLens;
+import com.android.tools.r8.graph.lens.MethodLookupResult;
+import com.android.tools.r8.graph.lens.NonIdentityGraphLens;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.BasicBlockInstructionListIterator;
+import com.android.tools.r8.ir.code.BasicBlockIterator;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Phi;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
+import com.android.tools.r8.ir.optimize.CustomLensCodeRewriter;
+import java.util.Collections;
+import java.util.Set;
+
+public class R8LibraryDesugaringGraphLens extends DefaultNonIdentityGraphLens {
+
+ @SuppressWarnings("UnusedVariable")
+ private final CfInstructionDesugaringEventConsumer eventConsumer;
+
+ @SuppressWarnings("UnusedVariable")
+ private final ProgramMethod method;
+
+ @SuppressWarnings("UnusedVariable")
+ private final MethodProcessingContext methodProcessingContext;
+
+ public R8LibraryDesugaringGraphLens(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ ProgramMethod method,
+ MethodProcessingContext methodProcessingContext) {
+ super(appView);
+ this.eventConsumer = eventConsumer;
+ this.method = method;
+ this.methodProcessingContext = methodProcessingContext;
+ }
+
+ @Override
+ public boolean hasCustomLensCodeRewriter() {
+ return true;
+ }
+
+ @Override
+ public CustomLensCodeRewriter getCustomLensCodeRewriter() {
+ return new R8LibraryDesugaringLensCodeRewriter();
+ }
+
+ @Override
+ protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
+ // TODO(b/391572031): Implement field access desugaring.
+ return previous;
+ }
+
+ @Override
+ protected MethodLookupResult internalDescribeLookupMethod(
+ MethodLookupResult previous, DexMethod context, GraphLens codeLens) {
+ // TODO(b/391572031): Implement invoke desugaring.
+ assert previous.getPrototypeChanges().isEmpty();
+ return previous;
+ }
+
+ private class R8LibraryDesugaringLensCodeRewriter implements CustomLensCodeRewriter {
+
+ @Override
+ public Set<Phi> rewriteCode(
+ IRCode code,
+ MethodProcessor methodProcessor,
+ RewrittenPrototypeDescription prototypeChanges,
+ NonIdentityGraphLens lens) {
+ boolean changed = false;
+ BasicBlockIterator blocks = code.listIterator();
+ GraphLens codeLens = code.context().getDefinition().getCode().getCodeLens(appView);
+ while (blocks.hasNext()) {
+ BasicBlock block = blocks.next();
+ BasicBlockInstructionListIterator instructions = block.listIterator();
+ while (instructions.hasNext()) {
+ InvokeMethod invoke = instructions.next().asInvokeMethod();
+ if (invoke == null) {
+ continue;
+ }
+ MethodLookupResult lookupResult =
+ lookupMethod(
+ invoke.getInvokedMethod(),
+ code.context().getReference(),
+ invoke.getType(),
+ codeLens,
+ invoke.getInterfaceBit());
+ if (lookupResult.isNeedsDesugaredLibraryApiConversionSet()) {
+ rewriteInvoke(code, blocks, instructions, invoke);
+ }
+ changed = true;
+ }
+ }
+ assert changed;
+ return Collections.emptySet();
+ }
+
+ @SuppressWarnings("UnusedVariable")
+ private void rewriteInvoke(
+ IRCode code,
+ BasicBlockIterator blocks,
+ BasicBlockInstructionListIterator instructions,
+ InvokeMethod invoke) {
+ // TODO(b/): Implement IR-to-IR invoke desugaring.
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java
index 7e29629..fe8e567 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java
@@ -6,6 +6,7 @@
import static com.google.common.base.Predicates.alwaysTrue;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramMethod;
@@ -44,6 +45,11 @@
this.classProcessor = new ClassProcessor(appView, isLiveMethod, desugaringMode);
}
+ @SuppressWarnings("DoNotCallSuggester")
+ public static InterfaceMethodProcessorFacade createForR8LirToLirLibraryDesugaring() {
+ throw new Unreachable();
+ }
+
private boolean shouldProcess(DexProgramClass clazz) {
return !appView.isAlreadyLibraryDesugared(clazz) && !clazz.originatesFromDexResource();
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LirLensCodeRewriter.java b/src/main/java/com/android/tools/r8/lightir/LirLensCodeRewriter.java
index eb4ec46..34718c2 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirLensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirLensCodeRewriter.java
@@ -32,6 +32,7 @@
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.ir.conversion.MethodConversionOptions;
import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.R8LibraryDesugaringGraphLens;
import com.android.tools.r8.ir.optimize.AffectedValues;
import com.android.tools.r8.ir.optimize.DeadCodeRemover;
import com.android.tools.r8.lightir.LirBuilder.NameComputationPayload;
@@ -180,6 +181,13 @@
}
}
}
+ if (graphLens instanceof R8LibraryDesugaringGraphLens) {
+ if (result.isNeedsDesugaredLibraryApiConversionSet()) {
+ return true;
+ }
+ } else {
+ assert !result.isNeedsDesugaredLibraryApiConversionSet();
+ }
assert result.getPrototypeChanges().isEmpty();
return false;
}
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 36a4f6b..5e82a63 100644
--- a/src/main/java/com/android/tools/r8/partial/R8PartialSubCompilationConfiguration.java
+++ b/src/main/java/com/android/tools/r8/partial/R8PartialSubCompilationConfiguration.java
@@ -248,6 +248,23 @@
assert amendMissingClasses(appView);
}
+ public void uncommitDexingOutputClasses(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ List<DexClasspathClass> newClasspathClasses =
+ ListUtils.sort(
+ DexClasspathClass.toClasspathClasses(dexingOutputClasses.values()).values(),
+ Comparator.comparing(DexClass::getType));
+ DirectMappedDexApplication newApp =
+ appView
+ .app()
+ .asDirect()
+ .builder()
+ .removeProgramClasses(clazz -> dexingOutputClasses.containsKey(clazz.getType()))
+ .addClasspathClasses(newClasspathClasses)
+ .build();
+ appView.rebuildAppInfo(newApp);
+ assert amendMissingClasses(appView);
+ }
+
public boolean hasD8DefinitionFor(DexReference reference) {
if (reference.isDexType()) {
return dexingOutputClasses.containsKey(reference.asDexType());