DesugaredLibraryRetargeter cf to cf in D8
Bug: 188767735
Change-Id: Id66cba48eb859bb23f23e1b73a27807e1f68c7ff
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
index 8f83dca..83e0ff5 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
@@ -107,6 +107,9 @@
assert instructionDesugaringEventConsumer.verifyNothingToFinalize();
}
+ converter.finalizeDesugaredLibraryRetargeting(instructionDesugaringEventConsumer);
+ assert instructionDesugaringEventConsumer.verifyNothingToFinalize();
+
classes = deferred;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 1843cea..5dd5114 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -222,10 +222,7 @@
assert options.desugarState.isOn();
this.instructionDesugaring = CfInstructionDesugaringCollection.create(appView);
this.classDesugaring = instructionDesugaring.createClassDesugaringCollection();
- this.desugaredLibraryRetargeter =
- options.desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()
- ? null
- : new DesugaredLibraryRetargeter(appView);
+ this.desugaredLibraryRetargeter = null; // Managed cf to cf.
this.interfaceMethodRewriter =
options.desugaredLibraryConfiguration.getEmulateLibraryInterface().isEmpty()
? null
@@ -258,14 +255,15 @@
? CfInstructionDesugaringCollection.empty()
: CfInstructionDesugaringCollection.create(appView);
this.classDesugaring = instructionDesugaring.createClassDesugaringCollection();
+ this.desugaredLibraryRetargeter =
+ options.desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()
+ || !appView.enableWholeProgramOptimizations()
+ ? null
+ : new DesugaredLibraryRetargeter(appView);
this.interfaceMethodRewriter =
options.isInterfaceMethodDesugaringEnabled()
? new InterfaceMethodRewriter(appView, this)
: null;
- this.desugaredLibraryRetargeter =
- options.desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()
- ? null
- : new DesugaredLibraryRetargeter(appView);
this.covariantReturnTypeAnnotationTransformer =
options.processCovariantReturnTypeAnnotations
? new CovariantReturnTypeAnnotationTransformer(this, appView.dexItemFactory())
@@ -371,6 +369,12 @@
D8NestBasedAccessDesugaring::clearNestAttributes);
}
+ public void finalizeDesugaredLibraryRetargeting(
+ D8CfInstructionDesugaringEventConsumer instructionDesugaringEventConsumer) {
+ instructionDesugaring.withDesugaredLibraryRetargeter(
+ retargeter -> retargeter.finalizeDesugaring(instructionDesugaringEventConsumer));
+ }
+
private void staticizeClasses(
OptimizationFeedback feedback, ExecutorService executorService, GraphLens applied)
throws ExecutionException {
@@ -397,12 +401,13 @@
}
}
- private void synthesizeRetargetClass(ExecutorService executorService) throws ExecutionException {
+ private void synthesizeRetargetClass() throws ExecutionException {
if (desugaredLibraryRetargeter != null) {
- desugaredLibraryRetargeter.synthesizeRetargetClasses(executorService, this);
+ desugaredLibraryRetargeter.synthesizeRetargetClasses();
}
}
+
private void synthesizeEnumUnboxingUtilityMethods(ExecutorService executorService)
throws ExecutionException {
if (enumUnboxer != null) {
@@ -441,7 +446,6 @@
Builder<?> builder = application.builder().setHighestSortingString(highestSortingString);
desugarInterfaceMethods(builder, ExcludeDexResources, executor);
- synthesizeRetargetClass(executor);
processCovariantReturnTypeAnnotations(builder);
generateDesugaredLibraryAPIWrappers(builder, executor);
@@ -587,7 +591,6 @@
new NeedsIRDesugarUseRegistry(
method,
appView,
- desugaredLibraryRetargeter,
interfaceMethodRewriter,
desugaredLibraryAPIConverter);
method.registerCodeReferences(useRegistry);
@@ -779,7 +782,7 @@
feedback.updateVisibleOptimizationInfo();
printPhase("Utility classes synthesis");
- synthesizeRetargetClass(executorService);
+ synthesizeRetargetClass();
synthesizeEnumUnboxingUtilityMethods(executorService);
printPhase("Desugared library API Conversion finalization");
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java b/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java
index bbc0bcb..5a4dec1 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java
@@ -19,26 +19,22 @@
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
class NeedsIRDesugarUseRegistry extends UseRegistry {
private boolean needsDesugaring = false;
private final ProgramMethod context;
- private final DesugaredLibraryRetargeter desugaredLibraryRetargeter;
private final InterfaceMethodRewriter interfaceMethodRewriter;
private final DesugaredLibraryAPIConverter desugaredLibraryAPIConverter;
public NeedsIRDesugarUseRegistry(
ProgramMethod method,
AppView<?> appView,
- DesugaredLibraryRetargeter desugaredLibraryRetargeter,
InterfaceMethodRewriter interfaceMethodRewriter,
DesugaredLibraryAPIConverter desugaredLibraryAPIConverter) {
super(appView.dexItemFactory());
this.context = method;
- this.desugaredLibraryRetargeter = desugaredLibraryRetargeter;
this.interfaceMethodRewriter = interfaceMethodRewriter;
this.desugaredLibraryAPIConverter = desugaredLibraryAPIConverter;
}
@@ -58,14 +54,12 @@
@Override
public void registerInvokeVirtual(DexMethod method) {
- registerLibraryRetargeting(method, false);
registerInterfaceMethodRewriting(method, VIRTUAL);
registerDesugaredLibraryAPIConverter(method);
}
@Override
public void registerInvokeDirect(DexMethod method) {
- registerLibraryRetargeting(method, false);
registerInterfaceMethodRewriting(method, DIRECT);
registerDesugaredLibraryAPIConverter(method);
}
@@ -86,24 +80,14 @@
}
}
- private void registerLibraryRetargeting(DexMethod method, boolean b) {
- if (!needsDesugaring) {
- needsDesugaring =
- desugaredLibraryRetargeter != null
- && desugaredLibraryRetargeter.getRetargetedMethod(method, b) != null;
- }
- }
-
@Override
public void registerInvokeStatic(DexMethod method) {
- registerLibraryRetargeting(method, false);
registerInterfaceMethodRewriting(method, STATIC);
registerDesugaredLibraryAPIConverter(method);
}
@Override
public void registerInvokeInterface(DexMethod method) {
- registerLibraryRetargeting(method, true);
registerInterfaceMethodRewriting(method, INTERFACE);
registerDesugaredLibraryAPIConverter(method);
}
@@ -124,7 +108,6 @@
@Override
public void registerInvokeSuper(DexMethod method) {
- registerLibraryRetargeting(method, false);
registerInterfaceMethodRewriting(method, SUPER);
registerDesugaredLibraryAPIConverter(method);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
index 3fb9329..78291d7 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
@@ -56,5 +56,8 @@
public abstract <T extends Throwable> void withD8NestBasedAccessDesugaring(
ThrowingConsumer<D8NestBasedAccessDesugaring, T> consumer) throws T;
+ public abstract void withDesugaredLibraryRetargeter(
+ Consumer<DesugaredLibraryRetargeter> consumer);
+
public abstract void withRecordRewriter(Consumer<RecordRewriter> consumer);
}
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 6951b39..1c6b337 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
@@ -6,6 +6,7 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClasspathClass;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.ProgramField;
@@ -42,7 +43,8 @@
NestBasedAccessDesugaringEventConsumer,
RecordDesugaringEventConsumer,
TwrCloseResourceDesugaringEventConsumer,
- InterfaceMethodDesugaringEventConsumer {
+ InterfaceMethodDesugaringEventConsumer,
+ DesugaredLibraryRetargeterEventConsumer {
public static D8CfInstructionDesugaringEventConsumer createForD8(
D8MethodProcessor methodProcessor) {
@@ -61,6 +63,21 @@
return new CfInstructionDesugaringEventConsumer() {
@Override
+ public void acceptDesugaredLibraryRetargeterDispatchProgramClass(DexProgramClass clazz) {
+ assert false;
+ }
+
+ @Override
+ public void acceptDesugaredLibraryRetargeterDispatchClasspathClass(DexClasspathClass clazz) {
+ assert false;
+ }
+
+ @Override
+ public void acceptForwardingMethod(ProgramMethod method) {
+ assert false;
+ }
+
+ @Override
public void acceptThrowMethod(ProgramMethod method, ProgramMethod context) {
assert false;
}
@@ -132,6 +149,21 @@
}
@Override
+ public void acceptDesugaredLibraryRetargeterDispatchProgramClass(DexProgramClass clazz) {
+ methodProcessor.scheduleDesugaredMethodsForProcessing(clazz.programMethods());
+ }
+
+ @Override
+ public void acceptDesugaredLibraryRetargeterDispatchClasspathClass(DexClasspathClass clazz) {
+ // Intentionnaly empty.
+ }
+
+ @Override
+ public void acceptForwardingMethod(ProgramMethod method) {
+ methodProcessor.scheduleDesugaredMethodForProcessing(method);
+ }
+
+ @Override
public void acceptBackportedMethod(ProgramMethod backportedMethod, ProgramMethod context) {
methodProcessor.scheduleMethodForProcessing(backportedMethod, this);
}
@@ -270,6 +302,24 @@
}
@Override
+ public void acceptDesugaredLibraryRetargeterDispatchProgramClass(DexProgramClass clazz) {
+ // Called only in Desugared library compilation which is D8.
+ assert false;
+ }
+
+ @Override
+ public void acceptDesugaredLibraryRetargeterDispatchClasspathClass(DexClasspathClass clazz) {
+ // TODO(b/188767735): R8 currently relies on IR desugaring.
+ // The classpath class should be marked as liveNonProgramType.
+ }
+
+ @Override
+ public void acceptForwardingMethod(ProgramMethod method) {
+ // TODO(b/188767735): R8 currently relies on IR desugaring.
+ // The method should be marked live, and assert everything it references is traced.
+ }
+
+ @Override
public void acceptRecordClass(DexProgramClass recordClass) {
// This is called each time an instruction or a class is found to require the record class.
assert false : "TODO(b/179146128): To be implemented";
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
index 68aca31..a3f492c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
@@ -4,7 +4,12 @@
package com.android.tools.r8.ir.desugar;
+import static com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter.InvokeRetargetingResult.NO_REWRITING;
+
import com.android.tools.r8.ProgramResource.Kind;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
@@ -33,13 +38,13 @@
import com.android.tools.r8.graph.NestHostClassAttribute;
import com.android.tools.r8.graph.NestMemberClassAttribute;
import com.android.tools.r8.graph.ParameterAnnotationsList;
+import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeStatic;
-import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.synthetic.EmulateInterfaceSyntheticCfCodeProvider;
import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.synthesis.SyntheticClassBuilder;
@@ -48,10 +53,9 @@
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.WorkList;
import com.android.tools.r8.utils.collections.DexClassAndMethodSet;
-import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.IdentityHashMap;
@@ -61,11 +65,11 @@
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
+import java.util.function.Function;
+import org.objectweb.asm.Opcodes;
-public class DesugaredLibraryRetargeter {
+public class DesugaredLibraryRetargeter implements CfInstructionDesugaring {
private final AppView<?> appView;
private final Map<DexMethod, DexMethod> retargetLibraryMember = new IdentityHashMap<>();
@@ -76,8 +80,6 @@
// Non final virtual library methods requiring generation of emulated dispatch.
private final DexClassAndMethodSet emulatedDispatchMethods = DexClassAndMethodSet.create();
- private final Set<DexProgramClass> programSynthesizedClasses = Sets.newConcurrentHashSet();
-
public DesugaredLibraryRetargeter(AppView<?> appView) {
this.appView = appView;
if (appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()) {
@@ -215,6 +217,32 @@
retargetLibraryMember.keySet().forEach(consumer);
}
+ @Override
+ public Collection<CfInstruction> desugarInstruction(
+ CfInstruction instruction,
+ FreshLocalProvider freshLocalProvider,
+ LocalStackAllocator localStackAllocator,
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ ProgramMethod context,
+ MethodProcessingContext methodProcessingContext,
+ DexItemFactory dexItemFactory) {
+ InvokeRetargetingResult invokeRetargetingResult = computeNewInvokeTarget(instruction, context);
+
+ if (!invokeRetargetingResult.hasNewInvokeTarget()) {
+ return null;
+ }
+
+ DexMethod newInvokeTarget = invokeRetargetingResult.getNewInvokeTarget(eventConsumer);
+ return Collections.singletonList(
+ new CfInvoke(Opcodes.INVOKESTATIC, newInvokeTarget, instruction.asInvoke().isInterface()));
+ }
+
+ @Override
+ public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
+ return computeNewInvokeTarget(instruction, context).hasNewInvokeTarget();
+ }
+
+ @Deprecated // Use Cf to Cf desugaring instead.
public void desugar(IRCode code) {
if (retargetLibraryMember.isEmpty()) {
return;
@@ -231,68 +259,129 @@
DexMethod invokedMethod = invoke.getInvokedMethod();
boolean isInterface = invoke.getInterfaceBit();
- DexMethod retarget = getRetargetedMethod(invokedMethod, isInterface);
- if (retarget == null) {
- continue;
+ InvokeRetargetingResult invokeRetargetingResult =
+ computeNewInvokeTarget(
+ invokedMethod, isInterface, invoke.isInvokeSuper(), code.context());
+ if (invokeRetargetingResult.hasNewInvokeTarget()) {
+ DexMethod newInvokeTarget = invokeRetargetingResult.getNewInvokeTarget(null);
+ iterator.replaceCurrentInstruction(
+ new InvokeStatic(newInvokeTarget, invoke.outValue(), invoke.inValues()));
}
-
- // Due to emulated dispatch, we have to rewrite invoke-super differently or we end up in
- // infinite loops. We do direct resolution. This is a very uncommon case.
- if (invoke.isInvokeSuper() && matchesNonFinalHolderRewrite(invoke.getInvokedMethod())) {
- DexClassAndMethod superTarget =
- appView
- .appInfoForDesugaring()
- .lookupSuperTarget(invoke.getInvokedMethod(), code.context());
- // Final methods can be rewritten as a normal invoke.
- if (superTarget != null && !superTarget.getAccessFlags().isFinal()) {
- DexMethod retargetMethod =
- appView.options().desugaredLibraryConfiguration.retargetMethod(superTarget, appView);
- if (retargetMethod != null) {
- iterator.replaceCurrentInstruction(
- new InvokeStatic(retargetMethod, invoke.outValue(), invoke.arguments()));
- }
- continue;
- }
- }
-
- iterator.replaceCurrentInstruction(
- new InvokeStatic(retarget, invoke.outValue(), invoke.inValues()));
}
}
- public DexMethod getRetargetedMethod(DexMethod invokedMethod, boolean isInterface) {
- DexMethod retarget = getRetargetLibraryMember(invokedMethod);
- if (retarget == null) {
+ static class InvokeRetargetingResult {
+
+ static InvokeRetargetingResult NO_REWRITING =
+ new InvokeRetargetingResult(false, ignored -> null);
+
+ private final boolean hasNewInvokeTarget;
+ private final Function<DesugaredLibraryRetargeterEventConsumer, DexMethod>
+ newInvokeTargetSupplier;
+
+ static InvokeRetargetingResult createInvokeRetargetingResult(DexMethod retarget) {
+ if (retarget == null) {
+ return NO_REWRITING;
+ }
+ return new InvokeRetargetingResult(true, ignored -> retarget);
+ }
+
+ private InvokeRetargetingResult(
+ boolean hasNewInvokeTarget,
+ Function<DesugaredLibraryRetargeterEventConsumer, DexMethod> newInvokeTargetSupplier) {
+ this.hasNewInvokeTarget = hasNewInvokeTarget;
+ this.newInvokeTargetSupplier = newInvokeTargetSupplier;
+ }
+
+ public boolean hasNewInvokeTarget() {
+ return hasNewInvokeTarget;
+ }
+
+ public DexMethod getNewInvokeTarget(DesugaredLibraryRetargeterEventConsumer eventConsumer) {
+ assert hasNewInvokeTarget();
+ return newInvokeTargetSupplier.apply(eventConsumer);
+ }
+ }
+
+ public boolean hasNewInvokeTarget(
+ DexMethod invokedMethod, boolean isInterface, boolean isInvokeSuper, ProgramMethod context) {
+ return computeNewInvokeTarget(invokedMethod, isInterface, isInvokeSuper, context)
+ .hasNewInvokeTarget();
+ }
+
+ private InvokeRetargetingResult computeNewInvokeTarget(
+ CfInstruction instruction, ProgramMethod context) {
+ if (retargetLibraryMember.isEmpty() || !instruction.isInvoke()) {
+ return NO_REWRITING;
+ }
+ CfInvoke cfInvoke = instruction.asInvoke();
+ return computeNewInvokeTarget(
+ cfInvoke.getMethod(),
+ cfInvoke.isInterface(),
+ cfInvoke.isInvokeSuper(context.getHolderType()),
+ context);
+ }
+
+ private InvokeRetargetingResult computeNewInvokeTarget(
+ DexMethod invokedMethod, boolean isInterface, boolean isInvokeSuper, ProgramMethod context) {
+ InvokeRetargetingResult retarget = computeRetargetedMethod(invokedMethod, isInterface);
+ if (!retarget.hasNewInvokeTarget()) {
+ return NO_REWRITING;
+ }
+ if (isInvokeSuper && matchesNonFinalHolderRewrite(invokedMethod)) {
+ DexClassAndMethod superTarget =
+ appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, context);
+ // Final methods can be rewritten as a normal invoke.
+ if (superTarget != null && !superTarget.getAccessFlags().isFinal()) {
+ return InvokeRetargetingResult.createInvokeRetargetingResult(
+ appView.options().desugaredLibraryConfiguration.retargetMethod(superTarget, appView));
+ }
+ }
+ return retarget;
+ }
+
+ private InvokeRetargetingResult computeRetargetedMethod(
+ DexMethod invokedMethod, boolean isInterface) {
+ InvokeRetargetingResult invokeRetargetingResult = computeRetargetLibraryMember(invokedMethod);
+ if (!invokeRetargetingResult.hasNewInvokeTarget()) {
if (!matchesNonFinalHolderRewrite(invokedMethod)) {
- return null;
+ return NO_REWRITING;
}
// We need to force resolution, even on d8, to know if the invoke has to be rewritten.
ResolutionResult resolutionResult =
appView.appInfoForDesugaring().resolveMethod(invokedMethod, isInterface);
if (resolutionResult.isFailedResolution()) {
- return null;
+ return NO_REWRITING;
}
DexEncodedMethod singleTarget = resolutionResult.getSingleTarget();
assert singleTarget != null;
- retarget = getRetargetLibraryMember(singleTarget.getReference());
+ invokeRetargetingResult = computeRetargetLibraryMember(singleTarget.getReference());
}
- return retarget;
+ return invokeRetargetingResult;
}
- private DexMethod getRetargetLibraryMember(DexMethod method) {
+ private InvokeRetargetingResult computeRetargetLibraryMember(DexMethod method) {
Map<DexType, DexType> backportCoreLibraryMembers =
appView.options().desugaredLibraryConfiguration.getBackportCoreLibraryMember();
if (backportCoreLibraryMembers.containsKey(method.holder)) {
DexType newHolder = backportCoreLibraryMembers.get(method.holder);
- return appView.dexItemFactory().createMethod(newHolder, method.proto, method.name);
+ DexMethod newMethod =
+ appView.dexItemFactory().createMethod(newHolder, method.proto, method.name);
+ return InvokeRetargetingResult.createInvokeRetargetingResult(newMethod);
}
DexClassAndMethod emulatedMethod = emulatedDispatchMethods.get(method);
if (emulatedMethod != null) {
assert !emulatedMethod.getAccessFlags().isStatic();
- DexType newHolder = ensureEmulatedHolderDispatchMethod(emulatedMethod).type;
- return computeRetargetMethod(method, emulatedMethod.getAccessFlags().isStatic(), newHolder);
+ return new InvokeRetargetingResult(
+ true,
+ eventConsumer -> {
+ DexType newHolder =
+ ensureEmulatedHolderDispatchMethod(emulatedMethod, eventConsumer).type;
+ return computeRetargetMethod(
+ method, emulatedMethod.getAccessFlags().isStatic(), newHolder);
+ });
}
- return retargetLibraryMember.get(method);
+ return InvokeRetargetingResult.createInvokeRetargetingResult(retargetLibraryMember.get(method));
}
private boolean matchesNonFinalHolderRewrite(DexMethod method) {
@@ -427,10 +516,8 @@
}
}
- public void synthesizeRetargetClasses(ExecutorService executorService, IRConverter converter)
- throws ExecutionException {
- new EmulatedDispatchTreeFixer().fixApp(executorService, converter);
- converter.optimizeSynthesizedClasses(programSynthesizedClasses, executorService);
+ public void finalizeDesugaring(DesugaredLibraryRetargeterEventConsumer eventConsumer) {
+ new EmulatedDispatchTreeFixer().fixApp(eventConsumer);
}
private void rewriteType(DexType type) {
@@ -441,8 +528,12 @@
appView.rewritePrefix.rewriteType(type, newType);
}
- public DexClass ensureEmulatedHolderDispatchMethod(DexClassAndMethod emulatedDispatchMethod) {
- DexClass interfaceClass = ensureEmulatedInterfaceDispatchMethod(emulatedDispatchMethod);
+ public DexClass ensureEmulatedHolderDispatchMethod(
+ DexClassAndMethod emulatedDispatchMethod,
+ DesugaredLibraryRetargeterEventConsumer eventConsumer) {
+ assert eventConsumer != null || appView.enableWholeProgramOptimizations();
+ DexClass interfaceClass =
+ ensureEmulatedInterfaceDispatchMethod(emulatedDispatchMethod, eventConsumer);
DexMethod itfMethod =
interfaceClass.lookupMethod(emulatedDispatchMethod.getReference()).getReference();
DexClass holderDispatch;
@@ -455,8 +546,12 @@
emulatedDispatchMethod.getHolder(),
appView,
classBuilder ->
- buildHolderDispatchMethod(classBuilder, emulatedDispatchMethod, itfMethod));
- programSynthesizedClasses.add(holderDispatch.asProgramClass());
+ buildHolderDispatchMethod(classBuilder, emulatedDispatchMethod, itfMethod),
+ clazz -> {
+ if (eventConsumer != null) {
+ eventConsumer.acceptDesugaredLibraryRetargeterDispatchProgramClass(clazz);
+ }
+ });
} else {
ClasspathOrLibraryClass context =
emulatedDispatchMethod.getHolder().asClasspathOrLibraryClass();
@@ -469,13 +564,21 @@
context,
appView,
classBuilder ->
- buildHolderDispatchMethod(classBuilder, emulatedDispatchMethod, itfMethod));
+ buildHolderDispatchMethod(classBuilder, emulatedDispatchMethod, itfMethod),
+ clazz -> {
+ if (eventConsumer != null) {
+ eventConsumer.acceptDesugaredLibraryRetargeterDispatchClasspathClass(clazz);
+ }
+ });
}
rewriteType(holderDispatch.type);
return holderDispatch;
}
- public DexClass ensureEmulatedInterfaceDispatchMethod(DexClassAndMethod emulatedDispatchMethod) {
+ public DexClass ensureEmulatedInterfaceDispatchMethod(
+ DexClassAndMethod emulatedDispatchMethod,
+ DesugaredLibraryRetargeterEventConsumer eventConsumer) {
+ assert eventConsumer != null || appView.enableWholeProgramOptimizations();
DexClass interfaceDispatch;
if (appView.options().isDesugaredLibraryCompilation()) {
interfaceDispatch =
@@ -486,8 +589,12 @@
emulatedDispatchMethod.getHolder(),
appView,
classBuilder ->
- this.buildInterfaceDispatchMethod(classBuilder, emulatedDispatchMethod));
- programSynthesizedClasses.add(interfaceDispatch.asProgramClass());
+ buildInterfaceDispatchMethod(classBuilder, emulatedDispatchMethod),
+ clazz -> {
+ if (eventConsumer != null) {
+ eventConsumer.acceptDesugaredLibraryRetargeterDispatchProgramClass(clazz);
+ }
+ });
} else {
ClasspathOrLibraryClass context =
emulatedDispatchMethod.getHolder().asClasspathOrLibraryClass();
@@ -500,7 +607,12 @@
context,
appView,
classBuilder ->
- this.buildInterfaceDispatchMethod(classBuilder, emulatedDispatchMethod));
+ buildInterfaceDispatchMethod(classBuilder, emulatedDispatchMethod),
+ clazz -> {
+ if (eventConsumer != null) {
+ eventConsumer.acceptDesugaredLibraryRetargeterDispatchClasspathClass(clazz);
+ }
+ });
}
rewriteType(interfaceDispatch.type);
return interfaceDispatch;
@@ -550,32 +662,33 @@
});
}
+ @Deprecated // Use Cf to Cf desugaring.
+ public void synthesizeRetargetClasses() {
+ new EmulatedDispatchTreeFixer().fixApp(null);
+ }
+
// The rewrite of virtual calls requires to go through emulate dispatch. This class is responsible
// for inserting interfaces on library boundaries and forwarding methods in the program, and to
// synthesize the interfaces and emulated dispatch classes in the desugared library.
class EmulatedDispatchTreeFixer {
- void fixApp(ExecutorService executorService, IRConverter converter) throws ExecutionException {
+ void fixApp(DesugaredLibraryRetargeterEventConsumer eventConsumer) {
if (appView.options().isDesugaredLibraryCompilation()) {
- synthesizeEmulatedDispatchMethods();
+ synthesizeEmulatedDispatchMethods(eventConsumer);
} else {
- addInterfacesAndForwardingMethods(executorService, converter);
+ addInterfacesAndForwardingMethods(eventConsumer);
}
}
private void addInterfacesAndForwardingMethods(
- ExecutorService executorService, IRConverter converter) throws ExecutionException {
+ DesugaredLibraryRetargeterEventConsumer eventConsumer) {
assert !appView.options().isDesugaredLibraryCompilation();
Map<DexType, List<DexClassAndMethod>> map = Maps.newIdentityHashMap();
for (DexClassAndMethod emulatedDispatchMethod : emulatedDispatchMethods) {
map.putIfAbsent(emulatedDispatchMethod.getHolderType(), new ArrayList<>(1));
map.get(emulatedDispatchMethod.getHolderType()).add(emulatedDispatchMethod);
}
- SortedProgramMethodSet addedMethods = SortedProgramMethodSet.create();
for (DexProgramClass clazz : appView.appInfo().classes()) {
- if (appView.isAlreadyLibraryDesugared(clazz)) {
- continue;
- }
if (clazz.superType == null) {
assert clazz.type == appView.dexItemFactory().objectType : clazz.type.toSourceString();
continue;
@@ -589,13 +702,11 @@
map.forEach(
(type, methods) -> {
if (inherit(superclass.asLibraryClass(), type, emulatedDispatchMethods)) {
- addInterfacesAndForwardingMethods(
- clazz, methods, method -> addedMethods.createAndAdd(clazz, method));
+ addInterfacesAndForwardingMethods(eventConsumer, clazz, methods);
}
});
}
}
- converter.processMethodsConcurrently(addedMethods, executorService);
}
private boolean inherit(
@@ -619,20 +730,26 @@
}
private void addInterfacesAndForwardingMethods(
+ DesugaredLibraryRetargeterEventConsumer eventConsumer,
DexProgramClass clazz,
- List<DexClassAndMethod> methods,
- Consumer<DexEncodedMethod> newForwardingMethodsConsumer) {
+ List<DexClassAndMethod> methods) {
// DesugaredLibraryRetargeter emulate dispatch: insertion of a marker interface & forwarding
// methods.
// We cannot use the ClassProcessor since this applies up to 26, while the ClassProcessor
// applies up to 24.
for (DexClassAndMethod method : methods) {
- DexClass dexClass = ensureEmulatedInterfaceDispatchMethod(method);
+ DexClass dexClass = ensureEmulatedInterfaceDispatchMethod(method, eventConsumer);
+ if (clazz.interfaces.contains(dexClass.type)) {
+ // The class has already been desugared.
+ continue;
+ }
clazz.addExtraInterfaces(Collections.singletonList(new ClassTypeSignature(dexClass.type)));
if (clazz.lookupVirtualMethod(method.getReference()) == null) {
DexEncodedMethod newMethod = createForwardingMethod(method, clazz);
clazz.addVirtualMethod(newMethod);
- newForwardingMethodsConsumer.accept(newMethod);
+ if (eventConsumer != null) {
+ eventConsumer.acceptForwardingMethod(new ProgramMethod(clazz, newMethod));
+ }
}
}
}
@@ -649,13 +766,14 @@
target, clazz, forwardMethod, appView.dexItemFactory());
}
- private void synthesizeEmulatedDispatchMethods() {
+ private void synthesizeEmulatedDispatchMethods(
+ DesugaredLibraryRetargeterEventConsumer eventConsumer) {
assert appView.options().isDesugaredLibraryCompilation();
if (emulatedDispatchMethods.isEmpty()) {
return;
}
for (DexClassAndMethod emulatedDispatchMethod : emulatedDispatchMethods) {
- ensureEmulatedHolderDispatchMethod(emulatedDispatchMethod);
+ ensureEmulatedHolderDispatchMethod(emulatedDispatchMethod, eventConsumer);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeterEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeterEventConsumer.java
new file mode 100644
index 0000000..3806aa6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeterEventConsumer.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2021, 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;
+
+import com.android.tools.r8.graph.DexClasspathClass;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramMethod;
+
+public interface DesugaredLibraryRetargeterEventConsumer {
+
+ void acceptDesugaredLibraryRetargeterDispatchProgramClass(DexProgramClass clazz);
+
+ void acceptDesugaredLibraryRetargeterDispatchClasspathClass(DexClasspathClass clazz);
+
+ void acceptForwardingMethod(ProgramMethod method);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
index c8b6147..5b25c84 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
@@ -58,6 +58,11 @@
}
@Override
+ public void withDesugaredLibraryRetargeter(Consumer<DesugaredLibraryRetargeter> consumer) {
+ // Intentionally empty.
+ }
+
+ @Override
public void withRecordRewriter(Consumer<RecordRewriter> consumer) {
// Intentionally empty.
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index 0d4a470..bb345a4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -39,11 +39,20 @@
private final NestBasedAccessDesugaring nestBasedAccessDesugaring;
private final RecordRewriter recordRewriter;
+ private final DesugaredLibraryRetargeter desugaredLibraryRetargeter;
NonEmptyCfInstructionDesugaringCollection(AppView<?> appView) {
this.appView = appView;
this.nestBasedAccessDesugaring = NestBasedAccessDesugaring.create(appView);
BackportedMethodRewriter backportedMethodRewriter = null;
+ desugaredLibraryRetargeter =
+ appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()
+ || appView.enableWholeProgramOptimizations()
+ ? null
+ : new DesugaredLibraryRetargeter(appView);
+ if (desugaredLibraryRetargeter != null) {
+ desugarings.add(desugaredLibraryRetargeter);
+ }
if (appView.options().enableBackportedMethodRewriting()) {
backportedMethodRewriter = new BackportedMethodRewriter(appView);
}
@@ -54,7 +63,9 @@
// TODO(b/183998768): Enable interface method rewriter cf to cf also in R8.
if (appView.options().isInterfaceMethodDesugaringEnabled()
&& !appView.enableWholeProgramOptimizations()) {
- desugarings.add(new InterfaceMethodRewriter(appView, backportedMethodRewriter));
+ desugarings.add(
+ new InterfaceMethodRewriter(
+ appView, backportedMethodRewriter, desugaredLibraryRetargeter));
}
desugarings.add(new LambdaInstructionDesugaring(appView));
desugarings.add(new InvokeSpecialToSelfDesugaring(appView));
@@ -80,6 +91,7 @@
AppView<?> appView, InvokeSpecialToSelfDesugaring invokeSpecialToSelfDesugaring) {
this.appView = appView;
this.nestBasedAccessDesugaring = null;
+ this.desugaredLibraryRetargeter = null;
this.recordRewriter = null;
desugarings.add(invokeSpecialToSelfDesugaring);
}
@@ -282,6 +294,13 @@
}
@Override
+ public void withDesugaredLibraryRetargeter(Consumer<DesugaredLibraryRetargeter> consumer) {
+ if (desugaredLibraryRetargeter != null) {
+ consumer.accept(desugaredLibraryRetargeter);
+ }
+ }
+
+ @Override
public void withRecordRewriter(Consumer<RecordRewriter> consumer) {
if (recordRewriter != null) {
consumer.accept(recordRewriter);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java
index 7e029ed..cf86667 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java
@@ -152,7 +152,8 @@
builder.addMethod(
methodBuilder ->
synthesizeEmulatedInterfaceMethod(
- method, emulatedInterface, methodBuilder))));
+ method, emulatedInterface, methodBuilder))),
+ ignored -> {});
emulateInterfaceClass.forEachProgramMethod(synthesizedMethods::add);
assert emulateInterfaceClass.getType()
== InterfaceMethodRewriter.getEmulateLibraryInterfaceClassType(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index 679a099..182dc39 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -59,6 +59,7 @@
import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter;
import com.android.tools.r8.ir.desugar.FreshLocalProvider;
import com.android.tools.r8.ir.desugar.LocalStackAllocator;
import com.android.tools.r8.ir.desugar.lambda.LambdaInstructionDesugaring;
@@ -149,6 +150,7 @@
// This is used to filter out double desugaring on backported methods.
private final BackportedMethodRewriter backportedMethodRewriter;
+ private final DesugaredLibraryRetargeter desugaredLibraryRetargeter;
/** Defines a minor variation in desugaring. */
public enum Flavor {
@@ -159,10 +161,14 @@
}
// Constructor for cf to cf desugaring.
- public InterfaceMethodRewriter(AppView<?> appView, BackportedMethodRewriter rewriter) {
+ public InterfaceMethodRewriter(
+ AppView<?> appView,
+ BackportedMethodRewriter rewriter,
+ DesugaredLibraryRetargeter desugaredLibraryRetargeter) {
this.appView = appView;
this.converter = null;
this.backportedMethodRewriter = rewriter;
+ this.desugaredLibraryRetargeter = desugaredLibraryRetargeter;
this.options = appView.options();
this.factory = appView.dexItemFactory();
this.emulatedInterfaces = options.desugaredLibraryConfiguration.getEmulateLibraryInterface();
@@ -176,6 +182,7 @@
this.appView = appView;
this.converter = converter;
this.backportedMethodRewriter = null;
+ this.desugaredLibraryRetargeter = null;
this.options = appView.options();
this.factory = appView.dexItemFactory();
this.emulatedInterfaces = options.desugaredLibraryConfiguration.getEmulateLibraryInterface();
@@ -281,6 +288,17 @@
return true;
}
+ private boolean isAlreadyRewritten(
+ DexMethod method, boolean itfBit, boolean isSuper, ProgramMethod context) {
+
+ // In Cf to Cf it is forbidden to desugar twice the same instruction, if the backported
+ // method rewriter or the desugared library retargeter already desugar the instruction, they
+ // take precedence and nothing has to be done here.
+ return (backportedMethodRewriter != null && backportedMethodRewriter.methodIsBackport(method))
+ || (desugaredLibraryRetargeter != null
+ && desugaredLibraryRetargeter.hasNewInvokeTarget(method, itfBit, isSuper, context));
+ }
+
@Override
public boolean hasPreciseNeedsDesugaring() {
return false;
@@ -307,7 +325,11 @@
}
if (instruction.isInvoke()) {
CfInvoke cfInvoke = instruction.asInvoke();
- if (backportedMethodRewriter.methodIsBackport(cfInvoke.getMethod())) {
+ if (isAlreadyRewritten(
+ cfInvoke.getMethod(),
+ cfInvoke.isInterface(),
+ cfInvoke.isInvokeSuper(context.getHolderType()),
+ context)) {
continue;
}
if (cfInvoke.isInvokeStatic()) {
@@ -378,6 +400,13 @@
public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
if (instruction.isInvoke()) {
CfInvoke cfInvoke = instruction.asInvoke();
+ if (isAlreadyRewritten(
+ cfInvoke.getMethod(),
+ cfInvoke.isInterface(),
+ cfInvoke.isInvokeSuper(context.getHolderType()),
+ context)) {
+ return false;
+ }
return needsRewriting(cfInvoke.getMethod(), cfInvoke.getInvokeType(context), context);
}
return false;
@@ -396,7 +425,11 @@
return null;
}
CfInvoke invoke = instruction.asInvoke();
- if (backportedMethodRewriter.methodIsBackport(invoke.getMethod())) {
+ if (isAlreadyRewritten(
+ invoke.getMethod(),
+ invoke.isInterface(),
+ invoke.isInvokeSuper(context.getHolderType()),
+ context)) {
return null;
}
@@ -453,11 +486,11 @@
CfInstructionDesugaringEventConsumer eventConsumer,
ProgramMethod context,
MethodProcessingContext methodProcessingContext) {
- if (backportedMethodRewriter != null
- && backportedMethodRewriter.methodIsBackport(invoke.getMethod())) {
- // In Cf to Cf it is not allowed to desugar twice the same instruction, if the backported
- // method rewriter already desugars the instruction, it takes precedence and nothing has
- // to be done here.
+ if (isAlreadyRewritten(
+ invoke.getMethod(),
+ invoke.isInterface(),
+ invoke.isInvokeSuper(context.getHolderType()),
+ context)) {
return null;
}
@@ -769,11 +802,7 @@
// to outline again the invoke-static. Just do nothing instead.
return null;
}
- if (backportedMethodRewriter != null
- && backportedMethodRewriter.methodIsBackport(invokedMethod)) {
- // In Cf to Cf it is not allowed to desugar twice the same instruction, if the backported
- // method rewriter already desugars the instruction, it takes precedence and nothing has
- // to be done here.
+ if (isAlreadyRewritten(invokedMethod, interfaceBit, false, context)) {
return null;
}
ProgramMethod newProgramMethod =
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index 7675a2c..007821c 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -494,7 +494,10 @@
Consumer<SyntheticProgramClassBuilder> fn) {
// Obtain the outer synthesizing context in the case the context itself is synthetic.
// This is to ensure a flat input-type -> synthetic-item mapping.
- SynthesizingContext outerContext = getSynthesizingContext(context, appView);
+ SynthesizingContext outerContext =
+ context.isProgramClass()
+ ? getSynthesizingContext(context.asProgramClass(), appView)
+ : SynthesizingContext.fromNonSyntheticInputContext(context.asClasspathOrLibraryClass());
DexType type = SyntheticNaming.createFixedType(kind, outerContext, appView.dexItemFactory());
return internalCreateClass(kind, fn, outerContext, type, appView.dexItemFactory());
}
@@ -508,7 +511,8 @@
SyntheticKind kind,
DexClass context,
AppView<?> appView,
- Consumer<SyntheticProgramClassBuilder> fn) {
+ Consumer<SyntheticProgramClassBuilder> fn,
+ Consumer<DexProgramClass> onCreationConsumer) {
assert kind.isFixedSuffixSynthetic;
// Obtain the outer synthesizing context in the case the context itself is synthetic.
// This is to ensure a flat input-type -> synthetic-item mapping.
@@ -534,7 +538,10 @@
return clazz.asProgramClass();
}
assert !isSyntheticClass(type);
- return internalCreateClass(kind, fn, outerContext, type, appView.dexItemFactory());
+ DexProgramClass dexProgramClass =
+ internalCreateClass(kind, fn, outerContext, type, appView.dexItemFactory());
+ onCreationConsumer.accept(dexProgramClass);
+ return dexProgramClass;
}
}
@@ -582,7 +589,8 @@
SyntheticKind kind,
ClasspathOrLibraryClass context,
AppView<?> appView,
- Consumer<SyntheticClasspathClassBuilder> classConsumer) {
+ Consumer<SyntheticClasspathClassBuilder> classConsumer,
+ Consumer<DexClasspathClass> onCreationConsumer) {
SynthesizingContext outerContext = SynthesizingContext.fromNonSyntheticInputContext(context);
DexType type = SyntheticNaming.createFixedType(kind, outerContext, appView.dexItemFactory());
DexClass dexClass = appView.appInfo().definitionForWithoutExistenceAssert(type);
@@ -602,6 +610,7 @@
new SyntheticClasspathClassBuilder(type, kind, outerContext, appView.dexItemFactory());
classConsumer.accept(classBuilder);
DexClasspathClass clazz = classBuilder.build();
+ onCreationConsumer.accept(clazz);
addPendingDefinition(new SyntheticClasspathClassDefinition(kind, outerContext, clazz));
return clazz;
}