Refactor api outliner logic to be independent of CfInstruction
This makes it straight-forward to implement api outlining lir-to-lir.
Bug: b/411032206
Change-Id: Ide11aff801bec5910376384719139e62bd18cca2
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 58a2b55..8885eaa 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
@@ -70,7 +70,7 @@
private final NestBasedAccessDesugaring nestBasedAccessDesugaring;
private final CfToCfDesugaredLibraryRetargeter desugaredLibraryRetargeter;
private final CfToCfInterfaceMethodRewriter interfaceMethodRewriter;
- private final CfToCfDesugaredLibraryApiConverter desugaredLibraryAPIConverter;
+ private final CfToCfDesugaredLibraryApiConverter desugaredLibraryApiConverter;
private final CfToCfDesugaredLibraryDisableDesugarer disableDesugarer;
private final CfInstructionDesugaring[][] asmOpcodeOrCompareToIdToDesugaringsMap;
@@ -86,17 +86,14 @@
appView.enableWholeProgramOptimizations()
? new AlwaysThrowingInstructionDesugaring(appView.withClassHierarchy())
: null;
- if (alwaysThrowingInstructionDesugaring != null) {
- desugarings.add(alwaysThrowingInstructionDesugaring);
- }
- if (appView.options().apiModelingOptions().isOutliningOfMethodsEnabled()) {
- yieldingDesugarings.add(new ApiInvokeOutlinerDesugaring(appView, apiLevelCompute));
- }
+ addIfNotNull(desugarings, alwaysThrowingInstructionDesugaring);
+ addIfNotNull(
+ yieldingDesugarings, ApiInvokeOutlinerDesugaring.createCfToCf(appView, apiLevelCompute));
if (appView.options().desugarState.isOff()) {
this.nestBasedAccessDesugaring = null;
this.desugaredLibraryRetargeter = null;
this.interfaceMethodRewriter = null;
- this.desugaredLibraryAPIConverter = null;
+ this.desugaredLibraryApiConverter = null;
this.disableDesugarer = null;
desugarings.add(new InvokeSpecialToSelfDesugaring(appView));
if (appView.options().isGeneratingDex()) {
@@ -116,24 +113,16 @@
// NavType#fromArgType is not kept.
CfToCfDesugaredLibraryLibRewriter desugaredLibRewriter =
DesugaredLibraryLibRewriter.createCfToCf(appView);
- if (desugaredLibRewriter != null) {
- desugarings.add(desugaredLibRewriter);
- }
+ addIfNotNull(desugarings, desugaredLibRewriter);
desugaredLibraryRetargeter = DesugaredLibraryRetargeter.createCfToCf(appView);
- if (desugaredLibraryRetargeter != null) {
- desugarings.add(desugaredLibraryRetargeter);
- }
+ addIfNotNull(desugarings, desugaredLibraryRetargeter);
AutoCloseableRetargeter autoCloseableRetargeter =
appView.options().shouldDesugarAutoCloseable()
? new AutoCloseableRetargeter(appView)
: null;
- if (autoCloseableRetargeter != null) {
- desugarings.add(autoCloseableRetargeter);
- }
+ addIfNotNull(desugarings, autoCloseableRetargeter);
disableDesugarer = DesugaredLibraryDisableDesugarer.createCfToCf(appView);
- if (disableDesugarer != null) {
- desugarings.add(disableDesugarer);
- }
+ addIfNotNull(desugarings, disableDesugarer);
if (appView.options().enableTryWithResourcesDesugaring()) {
desugarings.add(new TwrInstructionDesugaring(appView));
}
@@ -142,9 +131,7 @@
desugarings.add(typeSwitchDesugaring = new TypeSwitchDesugaring(appView));
}
RecordInstructionDesugaring recordRewriter = RecordInstructionDesugaring.create(appView);
- if (recordRewriter != null) {
- desugarings.add(recordRewriter);
- }
+ addIfNotNull(desugarings, recordRewriter);
StringConcatInstructionDesugaring stringConcatDesugaring =
new StringConcatInstructionDesugaring(appView);
desugarings.add(stringConcatDesugaring);
@@ -165,15 +152,13 @@
} else if (appView.options().canHaveArtArrayCloneFromInterfaceMethodBug()) {
desugarings.add(new OutlineArrayCloneFromInterfaceMethodDesugaring(appView));
}
- desugaredLibraryAPIConverter =
+ desugaredLibraryApiConverter =
DesugaredLibraryAPIConverter.createForCfToCf(
appView,
SetUtils.newImmutableSetExcludingNullItems(
interfaceMethodRewriter, desugaredLibraryRetargeter, backportedMethodRewriter),
interfaceMethodRewriter);
- if (desugaredLibraryAPIConverter != null) {
- desugarings.add(desugaredLibraryAPIConverter);
- }
+ addIfNotNull(desugarings, desugaredLibraryApiConverter);
desugarings.add(new ConstantDynamicInstructionDesugaring(appView));
desugarings.add(new InvokeSpecialToSelfDesugaring(appView));
if (appView.options().isGeneratingClassFiles()) {
@@ -187,17 +172,20 @@
if (backportedMethodRewriter.hasBackports()) {
desugarings.add(backportedMethodRewriter);
}
- if (nestBasedAccessDesugaring != null) {
- desugarings.add(nestBasedAccessDesugaring);
- }
+ addIfNotNull(desugarings, nestBasedAccessDesugaring);
VarHandleDesugaring varHandleDesugaring = VarHandleDesugaring.create(appView);
- if (varHandleDesugaring != null) {
- desugarings.add(varHandleDesugaring);
- }
+ addIfNotNull(desugarings, varHandleDesugaring);
yieldingDesugarings.add(new UnrepresentableInDexInstructionRemover(appView));
asmOpcodeOrCompareToIdToDesugaringsMap = createAsmOpcodeOrCompareToIdToDesugaringsMap();
}
+ private static void addIfNotNull(
+ Collection<CfInstructionDesugaring> collection, CfInstructionDesugaring desugaring) {
+ if (desugaring != null) {
+ collection.add(desugaring);
+ }
+ }
+
private CfInstructionDesugaring[][] createAsmOpcodeOrCompareToIdToDesugaringsMap() {
Int2ReferenceMap<List<CfInstructionDesugaring>> map = new Int2ReferenceOpenHashMap<>();
for (CfInstructionDesugaring desugaring : Iterables.concat(desugarings, yieldingDesugarings)) {
@@ -546,8 +534,8 @@
@Override
public void withDesugaredLibraryAPIConverter(Consumer<DesugaredLibraryAPIConverter> consumer) {
- if (desugaredLibraryAPIConverter != null) {
- consumer.accept(desugaredLibraryAPIConverter);
+ if (desugaredLibraryApiConverter != null) {
+ consumer.accept(desugaredLibraryApiConverter);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java
index e7fc2d4..c4dd8d0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java
@@ -6,20 +6,11 @@
import static com.android.tools.r8.utils.AndroidApiLevelUtils.isApiLevelLessThanOrEqualToG;
import static com.android.tools.r8.utils.AndroidApiLevelUtils.isOutlinedAtSameOrLowerLevel;
-import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
import com.android.tools.r8.androidapi.ComputedApiLevel;
-import com.android.tools.r8.cf.code.CfCheckCast;
-import com.android.tools.r8.cf.code.CfConstClass;
-import com.android.tools.r8.cf.code.CfFieldInstruction;
-import com.android.tools.r8.cf.code.CfInstanceOf;
-import com.android.tools.r8.cf.code.CfInstruction;
-import com.android.tools.r8.cf.code.CfInvoke;
-import com.android.tools.r8.cf.code.CfOpcodeUtils;
-import com.android.tools.r8.contexts.CompilationContext.UniqueContext;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMember;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -33,8 +24,7 @@
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
-import com.android.tools.r8.ir.desugar.DesugarDescription;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
import com.android.tools.r8.ir.synthetic.CheckCastSourceCode;
import com.android.tools.r8.ir.synthetic.ConstClassSourceCode;
import com.android.tools.r8.ir.synthetic.FieldAccessorBuilder;
@@ -44,79 +34,55 @@
import com.android.tools.r8.utils.AndroidApiLevelUtils;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.TraversalContinuation;
-import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.List;
import java.util.function.Function;
-import java.util.function.IntConsumer;
-import org.objectweb.asm.Opcodes;
/**
* This desugaring will outline calls to library methods that are introduced after the min-api
* level. For classes introduced after the min-api level see ApiReferenceStubber.
*/
-public class ApiInvokeOutlinerDesugaring implements CfInstructionDesugaring {
+public class ApiInvokeOutlinerDesugaring {
private final AppView<?> appView;
private final AndroidApiLevelCompute apiLevelCompute;
+ private final DexItemFactory factory;
private final DexTypeList objectParams;
- public ApiInvokeOutlinerDesugaring(AppView<?> appView, AndroidApiLevelCompute apiLevelCompute) {
+ ApiInvokeOutlinerDesugaring(AppView<?> appView, AndroidApiLevelCompute apiLevelCompute) {
this.appView = appView;
this.apiLevelCompute = apiLevelCompute;
- this.objectParams = DexTypeList.create(new DexType[] {appView.dexItemFactory().objectType});
+ this.factory = appView.dexItemFactory();
+ this.objectParams = DexTypeList.create(new DexType[] {factory.objectType});
}
- @Override
- public void acceptRelevantAsmOpcodes(IntConsumer consumer) {
- CfOpcodeUtils.acceptCfFieldInstructionOpcodes(consumer);
- CfOpcodeUtils.acceptCfInvokeOpcodes(
- opcode -> {
- if (opcode != Opcodes.INVOKESPECIAL) {
- consumer.accept(opcode);
- }
- });
- consumer.accept(Opcodes.CHECKCAST);
- consumer.accept(Opcodes.INSTANCEOF);
- }
-
- @Override
- public void acceptRelevantCompareToIds(IntConsumer consumer) {
- consumer.accept(CfCompareHelper.CONST_CLASS_COMPARE_ID);
- }
-
- @Override
- public DesugarDescription compute(CfInstruction instruction, ProgramMethod context) {
- ComputedApiLevel computedApiLevel =
- getComputedApiLevelInstructionOnHolderWithMinApi(instruction, context);
- if (appView.computedMinApiLevel().isGreaterThanOrEqualTo(computedApiLevel)) {
- return DesugarDescription.nothing();
+ public static CfToCfApiInvokeOutlinerDesugaring createCfToCf(
+ AppView<?> appView, AndroidApiLevelCompute apiLevelCompute) {
+ if (appView.options().apiModelingOptions().isOutliningOfMethodsEnabled()) {
+ return new CfToCfApiInvokeOutlinerDesugaring(appView, apiLevelCompute);
}
- return DesugarDescription.builder()
- .setDesugarRewrite(
- (position,
- freshLocalProvider,
- localStackAllocator,
- desugaringInfo,
- eventConsumer,
- context1,
- methodProcessingContext,
- desugaringCollection,
- dexItemFactory) ->
- desugarLibraryCall(
- methodProcessingContext.createUniqueContext(),
- instruction,
- computedApiLevel,
- dexItemFactory,
- eventConsumer,
- context))
- .build();
+ return null;
+ }
+
+ RetargetMethodSupplier getRetargetMethodSupplier(
+ InstructionKind instructionKind, DexReference reference, ProgramMethod context) {
+ ComputedApiLevel computedApiLevel =
+ getComputedApiLevelInstructionOnHolderWithMinApi(instructionKind, reference, context);
+ if (appView.computedMinApiLevel().isGreaterThanOrEqualTo(computedApiLevel)) {
+ return null;
+ }
+ return (eventConsumer, methodProcessingContext) -> {
+ ProgramMethod outlinedMethod =
+ ensureOutlineMethod(
+ instructionKind, reference, computedApiLevel, context, methodProcessingContext);
+ eventConsumer.acceptOutlinedMethod(outlinedMethod, context);
+ return outlinedMethod.getReference();
+ };
}
private ComputedApiLevel getComputedApiLevelInstructionOnHolderWithMinApi(
- CfInstruction instruction, ProgramMethod context) {
+ InstructionKind instructionKind, DexReference reference, ProgramMethod context) {
// Some backports will forward to the method/field they backport. For such synthetics run
// outlining. Other synthetics should not need it. And explicitly not API outlines, as that
// would cause infinite outlining.
@@ -126,7 +92,6 @@
.isSyntheticOfKind(context.getHolderType(), k -> k.BACKPORT_WITH_FORWARDING)) {
return appView.computedMinApiLevel();
}
- DexReference reference = getReferenceFromInstruction(instruction);
if (reference == null || !reference.getContextType().isClassType()) {
return appView.computedMinApiLevel();
}
@@ -159,10 +124,7 @@
return appView.computedMinApiLevel();
}
// Check for protected or package private access flags before outlining.
- if (firstLibraryClass.isInterface()
- || instruction.isCheckCast()
- || instruction.isInstanceOf()
- || instruction.isConstClass()) {
+ if (firstLibraryClass.isInterface() || instructionKind.isTypeInstruction()) {
return referenceApiLevel;
} else {
DexEncodedMember<?, ?> definition =
@@ -177,22 +139,6 @@
}
}
- private DexReference getReferenceFromInstruction(CfInstruction instruction) {
- if (instruction.isFieldInstruction()) {
- return instruction.asFieldInstruction().getField();
- } else if (instruction.isCheckCast()) {
- return instruction.asCheckCast().getType();
- } else if (instruction.isInstanceOf()) {
- return instruction.asInstanceOf().getType();
- } else if (instruction.isConstClass()) {
- return instruction.asConstClass().getType();
- } else if (instruction.isInvoke() && !instruction.asInvoke().isInvokeSpecial()) {
- return instruction.asInvoke().getMethod();
- } else {
- return null;
- }
- }
-
private DexEncodedMember<?, ?> simpleLookupInClassHierarchy(
DexLibraryClass holder, Function<DexClass, DexEncodedMember<?, ?>> lookup) {
DexEncodedMember<?, ?> result = lookup.apply(holder);
@@ -214,32 +160,12 @@
return traversalResult.isBreak() ? traversalResult.asBreak().getValue() : null;
}
- private Collection<CfInstruction> desugarLibraryCall(
- UniqueContext uniqueContext,
- CfInstruction instruction,
- ComputedApiLevel computedApiLevel,
- DexItemFactory factory,
- ApiInvokeOutlinerDesugaringEventConsumer eventConsumer,
- ProgramMethod context) {
- assert instruction.isInvoke()
- || instruction.isFieldInstruction()
- || instruction.isCheckCast()
- || instruction.isInstanceOf()
- || instruction.isConstClass()
- : instruction;
- ProgramMethod outlinedMethod =
- ensureOutlineMethod(uniqueContext, instruction, computedApiLevel, factory, context);
- eventConsumer.acceptOutlinedMethod(outlinedMethod, context);
- return ImmutableList.of(new CfInvoke(INVOKESTATIC, outlinedMethod.getReference(), false));
- }
-
private ProgramMethod ensureOutlineMethod(
- UniqueContext context,
- CfInstruction instruction,
+ InstructionKind instructionKind,
+ DexReference reference,
ComputedApiLevel apiLevel,
- DexItemFactory factory,
- ProgramMethod programContext) {
- DexReference reference = getReferenceFromInstruction(instruction);
+ ProgramMethod programContext,
+ MethodProcessingContext methodProcessingContext) {
assert reference != null;
DexClass holder = appView.definitionFor(reference.getContextType());
assert holder != null;
@@ -253,7 +179,7 @@
holder.isPublic()
? kinds.API_MODEL_OUTLINE
: kinds.API_MODEL_OUTLINE_WITHOUT_GLOBAL_MERGING,
- context,
+ methodProcessingContext.createUniqueContext(),
appView,
syntheticMethodBuilder -> {
syntheticMethodBuilder
@@ -266,31 +192,32 @@
.build())
.setApiLevelForDefinition(apiLevel)
.setApiLevelForCode(apiLevel);
- if (instruction.isInvoke()) {
- setCodeForInvoke(syntheticMethodBuilder, instruction.asInvoke(), factory);
- } else if (instruction.isCheckCast()) {
- setCodeForCheckCast(syntheticMethodBuilder, instruction.asCheckCast(), factory);
- } else if (instruction.isInstanceOf()) {
- setCodeForInstanceOf(syntheticMethodBuilder, instruction.asInstanceOf(), factory);
- } else if (instruction.isConstClass()) {
- setCodeForConstClass(syntheticMethodBuilder, instruction.asConstClass(), factory);
+ if (instructionKind.isInvoke()) {
+ setCodeForInvoke(syntheticMethodBuilder, instructionKind, reference.asDexMethod());
+ } else if (instructionKind == InstructionKind.CHECKCAST) {
+ setCodeForCheckCast(syntheticMethodBuilder, reference.asDexType());
+ } else if (instructionKind == InstructionKind.INSTANCEOF) {
+ setCodeForInstanceOf(syntheticMethodBuilder, reference.asDexType());
+ } else if (instructionKind == InstructionKind.CONSTCLASS) {
+ setCodeForConstClass(syntheticMethodBuilder, reference.asDexType());
} else {
- assert instruction.isCfInstruction();
+ assert instructionKind.isFieldInstruction();
setCodeForFieldInstruction(
syntheticMethodBuilder,
- instruction.asFieldInstruction(),
- factory,
+ instructionKind,
+ reference.asDexField(),
programContext);
}
});
}
private void setCodeForInvoke(
- SyntheticMethodBuilder methodBuilder, CfInvoke invoke, DexItemFactory factory) {
- DexMethod method = invoke.getMethod();
+ SyntheticMethodBuilder methodBuilder, InstructionKind instructionKind, DexMethod method) {
DexClass libraryHolder = appView.definitionFor(method.getHolderType());
assert libraryHolder != null;
- boolean isVirtualMethod = invoke.isInvokeVirtual() || invoke.isInvokeInterface();
+ boolean isVirtualMethod =
+ instructionKind == InstructionKind.INVOKEINTERFACE
+ || instructionKind == InstructionKind.INVOKEVIRTUAL;
assert verifyLibraryHolderAndInvoke(libraryHolder, method, isVirtualMethod);
DexProto proto = factory.prependHolderToProtoIf(method, isVirtualMethod);
methodBuilder
@@ -313,22 +240,24 @@
private void setCodeForFieldInstruction(
SyntheticMethodBuilder methodBuilder,
- CfFieldInstruction fieldInstruction,
- DexItemFactory factory,
+ InstructionKind instructionKind,
+ DexField field,
ProgramMethod programContext) {
- DexField field = fieldInstruction.getField();
DexClass libraryHolder = appView.definitionFor(field.getHolderType());
assert libraryHolder != null;
boolean isInstance =
- fieldInstruction.isInstanceFieldPut() || fieldInstruction.isInstanceFieldGet();
+ instructionKind == InstructionKind.IGET || instructionKind == InstructionKind.IPUT;
// Outlined field references will return a value if getter and only takes arguments if
// instance or if put or two arguments if both.
- DexType returnType = fieldInstruction.isFieldGet() ? field.getType() : factory.voidType;
+ boolean isGet =
+ instructionKind == InstructionKind.IGET || instructionKind == InstructionKind.SGET;
+ DexType returnType = isGet ? field.getType() : factory.voidType;
List<DexType> parameters = new ArrayList<>();
if (isInstance) {
parameters.add(libraryHolder.getType());
}
- if (fieldInstruction.isFieldPut()) {
+ boolean isPut = !isGet;
+ if (isPut) {
parameters.add(field.getType());
}
methodBuilder
@@ -341,16 +270,13 @@
thenConsumer -> thenConsumer.setInstanceField(field),
elseConsumer -> elseConsumer.setStaticField(field))
.applyIf(
- fieldInstruction.isFieldGet(),
- FieldAccessorBuilder::setGetter,
- FieldAccessorBuilder::setSetter)
+ isGet, FieldAccessorBuilder::setGetter, FieldAccessorBuilder::setSetter)
.setSourceMethod(programContext.getReference())
.build());
}
- private void setCodeForCheckCast(
- SyntheticMethodBuilder methodBuilder, CfCheckCast instruction, DexItemFactory factory) {
- DexClass target = appView.definitionFor(instruction.getType());
+ private void setCodeForCheckCast(SyntheticMethodBuilder methodBuilder, DexType type) {
+ DexClass target = appView.definitionFor(type);
assert target != null;
methodBuilder
.setProto(factory.createProto(target.getType(), objectParams))
@@ -360,9 +286,8 @@
.generateCfCode());
}
- private void setCodeForInstanceOf(
- SyntheticMethodBuilder methodBuilder, CfInstanceOf instruction, DexItemFactory factory) {
- DexClass target = appView.definitionFor(instruction.getType());
+ private void setCodeForInstanceOf(SyntheticMethodBuilder methodBuilder, DexType type) {
+ DexClass target = appView.definitionFor(type);
assert target != null;
methodBuilder
.setProto(factory.createProto(factory.booleanType, objectParams))
@@ -372,9 +297,8 @@
.generateCfCode());
}
- private void setCodeForConstClass(
- SyntheticMethodBuilder methodBuilder, CfConstClass instruction, DexItemFactory factory) {
- DexClass target = appView.definitionFor(instruction.getType());
+ private void setCodeForConstClass(SyntheticMethodBuilder methodBuilder, DexType type) {
+ DexClass target = appView.definitionFor(type);
assert target != null;
methodBuilder
.setProto(factory.createProto(factory.classType))
@@ -390,4 +314,38 @@
return libraryApiMethodDefinition == null
|| libraryApiMethodDefinition.isVirtualMethod() == isVirtualInvoke;
}
+
+ enum InstructionKind {
+ CHECKCAST,
+ CONSTCLASS,
+ IGET,
+ IPUT,
+ INSTANCEOF,
+ INVOKEINTERFACE,
+ INVOKESTATIC,
+ INVOKEVIRTUAL,
+ SGET,
+ SPUT;
+
+ boolean isFieldInstruction() {
+ return this == IGET || this == IPUT || this == SGET || this == SPUT;
+ }
+
+ boolean isInvoke() {
+ return this == INVOKEINTERFACE || this == INVOKESTATIC || this == INVOKEVIRTUAL;
+ }
+
+ boolean isTypeInstruction() {
+ return this == InstructionKind.CHECKCAST
+ || this == InstructionKind.CONSTCLASS
+ || this == InstructionKind.INSTANCEOF;
+ }
+ }
+
+ interface RetargetMethodSupplier {
+
+ DexMethod getRetargetMethod(
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ MethodProcessingContext methodProcessingContext);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/apimodel/CfToCfApiInvokeOutlinerDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/apimodel/CfToCfApiInvokeOutlinerDesugaring.java
new file mode 100644
index 0000000..c4f9bbe
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/apimodel/CfToCfApiInvokeOutlinerDesugaring.java
@@ -0,0 +1,121 @@
+// 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.apimodel;
+
+import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfOpcodeUtils;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCompareHelper;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.DesugarDescription;
+import java.util.Collections;
+import java.util.function.IntConsumer;
+import org.objectweb.asm.Opcodes;
+
+public class CfToCfApiInvokeOutlinerDesugaring extends ApiInvokeOutlinerDesugaring
+ implements CfInstructionDesugaring {
+
+ CfToCfApiInvokeOutlinerDesugaring(AppView<?> appView, AndroidApiLevelCompute apiLevelCompute) {
+ super(appView, apiLevelCompute);
+ }
+
+ @Override
+ public void acceptRelevantAsmOpcodes(IntConsumer consumer) {
+ CfOpcodeUtils.acceptCfFieldInstructionOpcodes(consumer);
+ CfOpcodeUtils.acceptCfInvokeOpcodes(
+ opcode -> {
+ if (opcode != Opcodes.INVOKESPECIAL) {
+ consumer.accept(opcode);
+ }
+ });
+ consumer.accept(Opcodes.CHECKCAST);
+ consumer.accept(Opcodes.INSTANCEOF);
+ }
+
+ @Override
+ public void acceptRelevantCompareToIds(IntConsumer consumer) {
+ consumer.accept(CfCompareHelper.CONST_CLASS_COMPARE_ID);
+ }
+
+ @Override
+ public DesugarDescription compute(CfInstruction instruction, ProgramMethod context) {
+ InstructionKind instructionKind;
+ DexReference reference;
+ if (instruction.hasAsmOpcode()) {
+ switch (instruction.getAsmOpcode()) {
+ case Opcodes.CHECKCAST:
+ instructionKind = InstructionKind.CHECKCAST;
+ reference = instruction.asCheckCast().getType();
+ break;
+ case Opcodes.GETFIELD:
+ instructionKind = InstructionKind.IGET;
+ reference = instruction.asFieldInstruction().getField();
+ break;
+ case Opcodes.GETSTATIC:
+ instructionKind = InstructionKind.SGET;
+ reference = instruction.asFieldInstruction().getField();
+ break;
+ case Opcodes.INSTANCEOF:
+ instructionKind = InstructionKind.INSTANCEOF;
+ reference = instruction.asInstanceOf().getType();
+ break;
+ case Opcodes.INVOKEINTERFACE:
+ instructionKind = InstructionKind.INVOKEINTERFACE;
+ reference = instruction.asInvoke().getMethod();
+ break;
+ case Opcodes.INVOKESTATIC:
+ instructionKind = InstructionKind.INVOKESTATIC;
+ reference = instruction.asInvoke().getMethod();
+ break;
+ case Opcodes.INVOKEVIRTUAL:
+ instructionKind = InstructionKind.INVOKEVIRTUAL;
+ reference = instruction.asInvoke().getMethod();
+ break;
+ case Opcodes.PUTFIELD:
+ instructionKind = InstructionKind.IPUT;
+ reference = instruction.asFieldInstruction().getField();
+ break;
+ case Opcodes.PUTSTATIC:
+ instructionKind = InstructionKind.SPUT;
+ reference = instruction.asFieldInstruction().getField();
+ break;
+ default:
+ return DesugarDescription.nothing();
+ }
+ } else if (instruction.isConstClass()) {
+ instructionKind = InstructionKind.CONSTCLASS;
+ reference = instruction.asConstClass().getType();
+ } else {
+ return DesugarDescription.nothing();
+ }
+ RetargetMethodSupplier retargetMethodSupplier =
+ getRetargetMethodSupplier(instructionKind, reference, context);
+ if (retargetMethodSupplier != null) {
+ return DesugarDescription.builder()
+ .setDesugarRewrite(
+ (position,
+ freshLocalProvider,
+ localStackAllocator,
+ desugaringInfo,
+ eventConsumer,
+ context1,
+ methodProcessingContext,
+ desugaringCollection,
+ dexItemFactory) -> {
+ DexMethod retargetMethod =
+ retargetMethodSupplier.getRetargetMethod(
+ eventConsumer, methodProcessingContext);
+ return Collections.singletonList(
+ new CfInvoke(Opcodes.INVOKESTATIC, retargetMethod, false));
+ })
+ .build();
+ }
+ return DesugarDescription.nothing();
+ }
+}