Lir-to-lir api outlining in R8 partial
Bug: b/411032206
Change-Id: If53a0100787f8b661f1488309f53f6c0aa4c4d1f
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiModelingOptions.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiModelingOptions.java
index 1c2f763..50c4862 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiModelingOptions.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiModelingOptions.java
@@ -117,6 +117,20 @@
return isApiModelingEnabled() && options.isGeneratingDex() && enableOutliningOfMethods;
}
+ public boolean isCfToCfApiOutliningEnabled() {
+ if (isOutliningOfMethodsEnabled()) {
+ // Enable cf-to-cf when running normal D8/R8. When running R8 partial, enable cf-to-cf
+ // desugaring when there is no library desugaring.
+ return options.partialSubCompilationConfiguration == null
+ || !options.getSubCompilationLibraryDesugaringOptions().isEnabled();
+ }
+ return false;
+ }
+
+ public boolean isLirToLirApiOutliningEnabled() {
+ return isOutliningOfMethodsEnabled() && !isCfToCfApiOutliningEnabled();
+ }
+
public boolean isCheckAllApiReferencesAreSet() {
return isApiModelingEnabled() && checkAllApiReferencesAreSet;
}
diff --git a/src/main/java/com/android/tools/r8/graph/lens/GraphLens.java b/src/main/java/com/android/tools/r8/graph/lens/GraphLens.java
index 61e2697..0319e2a 100644
--- a/src/main/java/com/android/tools/r8/graph/lens/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/lens/GraphLens.java
@@ -23,6 +23,7 @@
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMergerGraphLens;
import com.android.tools.r8.ir.code.InvokeType;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.R8LibraryDesugaringGraphLens;
import com.android.tools.r8.ir.optimize.CustomLensCodeRewriter;
import com.android.tools.r8.ir.optimize.enums.EnumUnboxingLens;
import com.android.tools.r8.optimize.MemberRebindingIdentityLens;
@@ -442,6 +443,14 @@
return null;
}
+ public boolean isLirToLirDesugaringLens() {
+ return false;
+ }
+
+ public R8LibraryDesugaringGraphLens asLirToLirDesugaringLens() {
+ return null;
+ }
+
public boolean isNumberUnboxerLens() {
return false;
}
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 4d96a56..113dced 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
@@ -930,6 +930,11 @@
}
@Override
+ public void acceptOutlinedMethod(ProgramMethod outlinedMethod, ProgramMethod context) {
+ synthesizedMethods.add(outlinedMethod);
+ }
+
+ @Override
public void acceptWrapperClasspathClass(DexClasspathClass clazz) {
// Intentionally empty.
}
@@ -949,11 +954,6 @@
}
@Override
- public void acceptOutlinedMethod(ProgramMethod outlinedMethod, ProgramMethod context) {
- assert false;
- }
-
- @Override
public void acceptBackportedMethod(ProgramMethod backportedMethod, ProgramMethod context) {
assert false;
}
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 c4dd8d0..3bd73cf 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
@@ -42,7 +42,7 @@
* 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 {
+public abstract class ApiInvokeOutlinerDesugaring {
private final AppView<?> appView;
private final AndroidApiLevelCompute apiLevelCompute;
@@ -59,13 +59,23 @@
public static CfToCfApiInvokeOutlinerDesugaring createCfToCf(
AppView<?> appView, AndroidApiLevelCompute apiLevelCompute) {
- if (appView.options().apiModelingOptions().isOutliningOfMethodsEnabled()) {
+ if (appView.options().apiModelingOptions().isCfToCfApiOutliningEnabled()) {
return new CfToCfApiInvokeOutlinerDesugaring(appView, apiLevelCompute);
}
return null;
}
- RetargetMethodSupplier getRetargetMethodSupplier(
+ public static LirToLirApiInvokeOutlinerDesugaring createLirToLir(
+ AppView<?> appView,
+ AndroidApiLevelCompute apiLevelCompute,
+ CfInstructionDesugaringEventConsumer eventConsumer) {
+ if (appView.options().apiModelingOptions().isLirToLirApiOutliningEnabled()) {
+ return new LirToLirApiInvokeOutlinerDesugaring(appView, apiLevelCompute, eventConsumer);
+ }
+ return null;
+ }
+
+ public RetargetMethodSupplier getRetargetMethodSupplier(
InstructionKind instructionKind, DexReference reference, ProgramMethod context) {
ComputedApiLevel computedApiLevel =
getComputedApiLevelInstructionOnHolderWithMinApi(instructionKind, reference, context);
@@ -276,36 +286,24 @@
}
private void setCodeForCheckCast(SyntheticMethodBuilder methodBuilder, DexType type) {
- DexClass target = appView.definitionFor(type);
- assert target != null;
methodBuilder
- .setProto(factory.createProto(target.getType(), objectParams))
+ .setProto(factory.createProto(type, objectParams))
.setCode(
- m ->
- CheckCastSourceCode.create(appView, m.getHolderType(), target.getType())
- .generateCfCode());
+ m -> CheckCastSourceCode.create(appView, m.getHolderType(), type).generateCfCode());
}
private void setCodeForInstanceOf(SyntheticMethodBuilder methodBuilder, DexType type) {
- DexClass target = appView.definitionFor(type);
- assert target != null;
methodBuilder
.setProto(factory.createProto(factory.booleanType, objectParams))
.setCode(
- m ->
- InstanceOfSourceCode.create(appView, m.getHolderType(), target.getType())
- .generateCfCode());
+ m -> InstanceOfSourceCode.create(appView, m.getHolderType(), type).generateCfCode());
}
private void setCodeForConstClass(SyntheticMethodBuilder methodBuilder, DexType type) {
- DexClass target = appView.definitionFor(type);
- assert target != null;
methodBuilder
.setProto(factory.createProto(factory.classType))
.setCode(
- m ->
- ConstClassSourceCode.create(appView, m.getHolderType(), target.getType())
- .generateCfCode());
+ m -> ConstClassSourceCode.create(appView, m.getHolderType(), type).generateCfCode());
}
private boolean verifyLibraryHolderAndInvoke(
@@ -315,7 +313,7 @@
|| libraryApiMethodDefinition.isVirtualMethod() == isVirtualInvoke;
}
- enum InstructionKind {
+ public enum InstructionKind {
CHECKCAST,
CONSTCLASS,
IGET,
@@ -327,11 +325,11 @@
SGET,
SPUT;
- boolean isFieldInstruction() {
+ public boolean isFieldInstruction() {
return this == IGET || this == IPUT || this == SGET || this == SPUT;
}
- boolean isInvoke() {
+ public boolean isInvoke() {
return this == INVOKEINTERFACE || this == INVOKESTATIC || this == INVOKEVIRTUAL;
}
@@ -342,7 +340,7 @@
}
}
- interface RetargetMethodSupplier {
+ public interface RetargetMethodSupplier {
DexMethod getRetargetMethod(
CfInstructionDesugaringEventConsumer eventConsumer,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/apimodel/LirToLirApiInvokeOutlinerDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/apimodel/LirToLirApiInvokeOutlinerDesugaring.java
new file mode 100644
index 0000000..69b909a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/apimodel/LirToLirApiInvokeOutlinerDesugaring.java
@@ -0,0 +1,73 @@
+// 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.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.graph.AppView;
+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.graph.lens.MethodLookupResult;
+import com.android.tools.r8.ir.code.InvokeType;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.R8LibraryDesugaringGraphLens;
+
+public class LirToLirApiInvokeOutlinerDesugaring extends ApiInvokeOutlinerDesugaring {
+
+ private final CfInstructionDesugaringEventConsumer eventConsumer;
+
+ LirToLirApiInvokeOutlinerDesugaring(
+ AppView<?> appView,
+ AndroidApiLevelCompute apiLevelCompute,
+ CfInstructionDesugaringEventConsumer eventConsumer) {
+ super(appView, apiLevelCompute);
+ this.eventConsumer = eventConsumer;
+ }
+
+ public MethodLookupResult lookupMethod(
+ MethodLookupResult previous,
+ ProgramMethod context,
+ MethodProcessingContext methodProcessingContext,
+ R8LibraryDesugaringGraphLens lens) {
+ InstructionKind instructionKind;
+ switch (previous.getType()) {
+ case INTERFACE:
+ instructionKind = InstructionKind.INVOKEINTERFACE;
+ break;
+ case STATIC:
+ instructionKind = InstructionKind.INVOKESTATIC;
+ break;
+ case VIRTUAL:
+ instructionKind = InstructionKind.INVOKEVIRTUAL;
+ break;
+ default:
+ return previous;
+ }
+ DexMethod retargetMethod =
+ getRetargetMethod(
+ instructionKind, previous.getReference(), context, methodProcessingContext);
+ if (retargetMethod != null) {
+ return MethodLookupResult.builder(lens, lens.getPrevious())
+ .setReference(retargetMethod)
+ .setReboundReference(retargetMethod)
+ .setIsInterface(false)
+ .setType(InvokeType.STATIC)
+ .build();
+ }
+ return previous;
+ }
+
+ public DexMethod getRetargetMethod(
+ InstructionKind instructionKind,
+ DexReference reference,
+ ProgramMethod context,
+ MethodProcessingContext methodProcessingContext) {
+ RetargetMethodSupplier retargetMethodSupplier =
+ getRetargetMethodSupplier(instructionKind, reference, context);
+ return retargetMethodSupplier != null
+ ? retargetMethodSupplier.getRetargetMethod(eventConsumer, methodProcessingContext)
+ : null;
+ }
+}
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
index bcda2e1..2cdb601 100644
--- 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
@@ -18,6 +18,8 @@
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.apimodel.ApiInvokeOutlinerDesugaring;
+import com.android.tools.r8.ir.desugar.apimodel.LirToLirApiInvokeOutlinerDesugaring;
import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPIConverter;
import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.LirToLirDesugaredLibraryApiConverter;
import com.android.tools.r8.ir.desugar.desugaredlibrary.disabledesugarer.DesugaredLibraryDisableDesugarer;
@@ -60,6 +62,8 @@
&& options.getLibraryDesugaringOptions().isLirToLirLibraryDesugaringEnabled()
&& options.isGeneratingDex()) {
new R8LibraryDesugaring(appView).run(executorService, timing);
+ } else {
+ assert !appView.options().apiModelingOptions().isLirToLirApiOutliningEnabled();
}
}
@@ -101,6 +105,9 @@
CfInstructionDesugaringEventConsumer eventConsumer =
CfInstructionDesugaringEventConsumer.createForR8LibraryDesugaring(
appView, profileCollectionAdditions, synthesizedMethods);
+ LirToLirApiInvokeOutlinerDesugaring apiOutliner =
+ ApiInvokeOutlinerDesugaring.createLirToLir(
+ appView, appView.apiLevelCompute(), eventConsumer);
LirToLirDesugaredLibraryDisableDesugarer desugaredLibraryDisableDesugarer =
DesugaredLibraryDisableDesugarer.createLirToLir(appView);
LirToLirDesugaredLibraryLibRewriter desugaredLibraryLibRewriter =
@@ -125,6 +132,7 @@
R8LibraryDesugaringGraphLens libraryDesugaringGraphLens =
new R8LibraryDesugaringGraphLens(
appView,
+ apiOutliner,
desugaredLibraryApiConverter,
desugaredLibraryDisableDesugarer,
desugaredLibraryLibRewriter,
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
index 6fb3ba5..89a0460 100644
--- 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
@@ -6,7 +6,9 @@
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.DexField;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.lens.DefaultNonIdentityGraphLens;
@@ -27,10 +29,14 @@
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.InvokeVirtual;
+import com.android.tools.r8.ir.code.Opcodes;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.apimodel.ApiInvokeOutlinerDesugaring;
+import com.android.tools.r8.ir.desugar.apimodel.ApiInvokeOutlinerDesugaring.InstructionKind;
+import com.android.tools.r8.ir.desugar.apimodel.LirToLirApiInvokeOutlinerDesugaring;
import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryConversionCfProvider;
import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.LirToLirDesugaredLibraryApiConverter;
import com.android.tools.r8.ir.desugar.desugaredlibrary.disabledesugarer.LirToLirDesugaredLibraryDisableDesugarer;
@@ -46,6 +52,7 @@
// TODO(b/391572031): Apply type instruction rewriting from CfToCfDesugaredLibraryDisableDesugarer.
public class R8LibraryDesugaringGraphLens extends DefaultNonIdentityGraphLens {
+ private final LirToLirApiInvokeOutlinerDesugaring apiOutliner;
private final LirToLirDesugaredLibraryApiConverter desugaredLibraryApiConverter;
private final LirToLirDesugaredLibraryDisableDesugarer desugaredLibraryDisableDesugarer;
private final LirToLirDesugaredLibraryLibRewriter desugaredLibraryLibRewriter;
@@ -58,6 +65,7 @@
public R8LibraryDesugaringGraphLens(
AppView<? extends AppInfoWithClassHierarchy> appView,
+ LirToLirApiInvokeOutlinerDesugaring apiOutliner,
LirToLirDesugaredLibraryApiConverter desugaredLibraryApiConverter,
LirToLirDesugaredLibraryDisableDesugarer desugaredLibraryDisableDesugarer,
LirToLirDesugaredLibraryLibRewriter desugaredLibraryLibRewriter,
@@ -67,6 +75,7 @@
ProgramMethod method,
MethodProcessingContext methodProcessingContext) {
super(appView);
+ this.apiOutliner = apiOutliner;
this.desugaredLibraryApiConverter = desugaredLibraryApiConverter;
this.desugaredLibraryDisableDesugarer = desugaredLibraryDisableDesugarer;
this.desugaredLibraryLibRewriter = desugaredLibraryLibRewriter;
@@ -77,6 +86,45 @@
this.methodProcessingContext = methodProcessingContext;
}
+ public boolean needsApiOutlining(
+ ApiInvokeOutlinerDesugaring.InstructionKind instructionKind,
+ DexReference reference,
+ ProgramMethod context) {
+ return apiOutliner != null
+ && apiOutliner.getRetargetMethodSupplier(instructionKind, reference, context) != null
+ && !needsLibraryDesugaring(instructionKind, reference);
+ }
+
+ private DexMethod lookupApiOutline(
+ ApiInvokeOutlinerDesugaring.InstructionKind instructionKind,
+ DexReference reference,
+ ProgramMethod context) {
+ return apiOutliner != null && !needsLibraryDesugaring(instructionKind, reference)
+ ? apiOutliner.getRetargetMethod(
+ instructionKind, reference, context, methodProcessingContext)
+ : null;
+ }
+
+ private boolean needsLibraryDesugaring(
+ ApiInvokeOutlinerDesugaring.InstructionKind instructionKind, DexReference reference) {
+ if (instructionKind.isFieldInstruction()) {
+ DexField field = reference.asDexField();
+ DexField lookupResult = lookupField(field, appView.codeLens());
+ return lookupResult.isNotIdenticalTo(field);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isLirToLirDesugaringLens() {
+ return true;
+ }
+
+ @Override
+ public R8LibraryDesugaringGraphLens asLirToLirDesugaringLens() {
+ return this;
+ }
+
@Override
public boolean hasCustomLensCodeRewriter() {
return true;
@@ -149,6 +197,10 @@
previous, method, methodProcessingContext, this);
}
+ if (apiOutliner != null) {
+ return apiOutliner.lookupMethod(previous, method, methodProcessingContext, this);
+ }
+
return previous;
}
@@ -162,26 +214,84 @@
NonIdentityGraphLens lens) {
boolean changed = false;
BasicBlockIterator blocks = code.listIterator();
- GraphLens codeLens = code.context().getDefinition().getCode().getCodeLens(appView);
+ ProgramMethod context = code.context();
+ GraphLens codeLens = 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;
+ Instruction instruction = instructions.next();
+ DexMethod apiOutline = null;
+ switch (instruction.opcode()) {
+ case Opcodes.CHECK_CAST:
+ apiOutline =
+ lookupApiOutline(
+ InstructionKind.CHECKCAST, instruction.asCheckCast().getType(), context);
+ break;
+ case Opcodes.CONST_CLASS:
+ apiOutline =
+ lookupApiOutline(
+ InstructionKind.CONSTCLASS, instruction.asConstClass().getType(), context);
+ break;
+ case Opcodes.INSTANCE_GET:
+ apiOutline =
+ lookupApiOutline(
+ InstructionKind.IGET, instruction.asInstanceGet().getField(), context);
+ break;
+ case Opcodes.INSTANCE_OF:
+ apiOutline =
+ lookupApiOutline(
+ InstructionKind.INSTANCEOF, instruction.asInstanceOf().type(), context);
+ break;
+ case Opcodes.INSTANCE_PUT:
+ apiOutline =
+ lookupApiOutline(
+ InstructionKind.INSTANCEOF, instruction.asInstancePut().getField(), context);
+ break;
+ case Opcodes.INVOKE_DIRECT:
+ case Opcodes.INVOKE_INTERFACE:
+ case Opcodes.INVOKE_STATIC:
+ case Opcodes.INVOKE_SUPER:
+ case Opcodes.INVOKE_VIRTUAL:
+ {
+ InvokeMethod invoke = instruction.asInvokeMethod();
+ MethodLookupResult lookupResult =
+ lookupMethod(
+ invoke.getInvokedMethod(),
+ context.getReference(),
+ invoke.getType(),
+ codeLens,
+ invoke.getInterfaceBit());
+ if (lookupResult.isNeedsDesugaredLibraryApiConversionSet()) {
+ rewriteInvoke(code, blocks, instructions, invoke);
+ changed = true;
+ }
+ break;
+ }
+ case Opcodes.STATIC_GET:
+ apiOutline =
+ lookupApiOutline(
+ InstructionKind.SGET, instruction.asStaticGet().getField(), context);
+ break;
+ case Opcodes.STATIC_PUT:
+ apiOutline =
+ lookupApiOutline(
+ InstructionKind.SPUT, instruction.asStaticPut().getField(), context);
+ break;
+ default:
+ break;
}
- MethodLookupResult lookupResult =
- lookupMethod(
- invoke.getInvokedMethod(),
- code.context().getReference(),
- invoke.getType(),
- codeLens,
- invoke.getInterfaceBit());
- if (lookupResult.isNeedsDesugaredLibraryApiConversionSet()) {
- rewriteInvoke(code, blocks, instructions, invoke);
+ if (apiOutline != null) {
+ instructions.replaceCurrentInstruction(
+ InvokeStatic.builder()
+ .setArguments(instruction.inValues())
+ .setMethod(apiOutline)
+ .setOutValue(instruction.outValue())
+ .setIsInterface(false)
+ .setPosition(instruction)
+ .build());
+ changed = true;
}
- changed = true;
}
}
assert changed;
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 ae31471..b951514 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirLensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirLensCodeRewriter.java
@@ -32,7 +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.desugar.apimodel.ApiInvokeOutlinerDesugaring.InstructionKind;
import com.android.tools.r8.ir.optimize.AffectedValues;
import com.android.tools.r8.ir.optimize.DeadCodeRemover;
import com.android.tools.r8.lightir.LirBuilder.NameComputationPayload;
@@ -43,6 +43,7 @@
import com.android.tools.r8.utils.timing.Timing;
import com.android.tools.r8.verticalclassmerging.VerticalClassMergerGraphLens;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
import java.util.ArrayList;
@@ -54,7 +55,9 @@
public class LirLensCodeRewriter<EV> extends LirParsedInstructionCallback<EV> {
- private static final Set<DexMethod> NO_INVOKES_TO_REWRITE = ImmutableSet.of();
+ private static final Set<DexField> NO_FIELD_INSTRUCTIONS_TO_REWRITE = ImmutableSet.of();
+ private static final Set<DexMethod> NO_INVOKE_INSTRUCTIONS_TO_REWRITE = ImmutableSet.of();
+ private static final Set<DexType> NO_TYPE_INSTRUCTIONS_TO_REWRITE = ImmutableSet.of();
private final AppView<?> appView;
private final ProgramMethod context;
@@ -66,9 +69,12 @@
private final boolean isNonStartupInStartupOutlinerLens;
private int numberOfInvokeOpcodeChanges = 0;
- private Set<DexMethod> invokesToRewrite = NO_INVOKES_TO_REWRITE;
+ private Set<DexMethod> invokeInstructionsToRewrite = NO_INVOKE_INSTRUCTIONS_TO_REWRITE;
private Map<LirConstant, LirConstant> constantPoolMapping = null;
+ private Set<DexField> fieldInstructionsToRewrite = NO_FIELD_INSTRUCTIONS_TO_REWRITE;
+ private Set<DexType> typeInstructionsToRewrite = NO_TYPE_INSTRUCTIONS_TO_REWRITE;
+
private boolean hasNonTrivialRewritings = false;
public LirLensCodeRewriter(
@@ -181,7 +187,7 @@
}
}
}
- if (graphLens instanceof R8LibraryDesugaringGraphLens) {
+ if (graphLens.isLirToLirDesugaringLens()) {
if (result.isNeedsDesugaredLibraryApiConversionSet()) {
return true;
}
@@ -192,6 +198,13 @@
return false;
}
+ private void addFieldInstructionToRewrite(DexField field) {
+ if (fieldInstructionsToRewrite == NO_FIELD_INSTRUCTIONS_TO_REWRITE) {
+ fieldInstructionsToRewrite = Sets.newIdentityHashSet();
+ }
+ fieldInstructionsToRewrite.add(field);
+ }
+
private void addRewrittenMethodMapping(DexMethod method, DexMethod rewrittenMethod) {
getOrCreateConstantPoolMapping()
.compute(
@@ -203,15 +216,22 @@
// Two invokes with the same symbolic method reference but different invoke types
// are rewritten to two different symbolic method references. Record that the
// invokes need to be processed.
- if (invokesToRewrite == NO_INVOKES_TO_REWRITE) {
- invokesToRewrite = new HashSet<>();
+ if (invokeInstructionsToRewrite == NO_INVOKE_INSTRUCTIONS_TO_REWRITE) {
+ invokeInstructionsToRewrite = new HashSet<>();
}
- invokesToRewrite.add(method);
+ invokeInstructionsToRewrite.add(method);
return method;
}
});
}
+ private void addTypeInstructionToRewrite(DexType type) {
+ if (typeInstructionsToRewrite == NO_TYPE_INSTRUCTIONS_TO_REWRITE) {
+ typeInstructionsToRewrite = Sets.newIdentityHashSet();
+ }
+ typeInstructionsToRewrite.add(type);
+ }
+
private void addRewrittenMapping(LirConstant item, LirConstant rewrittenItem) {
if (item == rewrittenItem) {
return;
@@ -239,6 +259,46 @@
}
@Override
+ public void onCheckCast(DexType type, EV value, boolean ignoreCompatRules) {
+ if (graphLens.isLirToLirDesugaringLens()
+ && graphLens
+ .asLirToLirDesugaringLens()
+ .needsApiOutlining(InstructionKind.CHECKCAST, type, context)) {
+ addTypeInstructionToRewrite(type);
+ }
+ }
+
+ @Override
+ public void onSafeCheckCast(DexType type, EV value) {
+ if (graphLens.isLirToLirDesugaringLens()
+ && graphLens
+ .asLirToLirDesugaringLens()
+ .needsApiOutlining(InstructionKind.CHECKCAST, type, context)) {
+ addTypeInstructionToRewrite(type);
+ }
+ }
+
+ @Override
+ public void onConstClass(DexType type, boolean ignoreCompatRules) {
+ if (graphLens.isLirToLirDesugaringLens()
+ && graphLens
+ .asLirToLirDesugaringLens()
+ .needsApiOutlining(InstructionKind.CONSTCLASS, type, context)) {
+ addTypeInstructionToRewrite(type);
+ }
+ }
+
+ @Override
+ public void onInstanceOf(DexType type, EV value) {
+ if (graphLens.isLirToLirDesugaringLens()
+ && graphLens
+ .asLirToLirDesugaringLens()
+ .needsApiOutlining(InstructionKind.INSTANCEOF, type, context)) {
+ addTypeInstructionToRewrite(type);
+ }
+ }
+
+ @Override
public void onDexItemBasedConstString(
DexReference item, NameComputationInfo<?> nameComputationInfo) {
addRewrittenMapping(item, graphLens.getRenamedReference(item, codeLens));
@@ -246,56 +306,83 @@
@Override
public void onInstanceGet(DexField field, EV object) {
- onFieldGet(field);
+ if (setHasPotentialNonTrivialFieldGetRewriting(field)) {
+ return;
+ }
+ if (graphLens.isLirToLirDesugaringLens()
+ && graphLens
+ .asLirToLirDesugaringLens()
+ .needsApiOutlining(InstructionKind.IGET, field, context)) {
+ addFieldInstructionToRewrite(field);
+ }
}
@Override
public void onStaticGet(DexField field) {
- onFieldGet(field);
- }
-
- private void onFieldGet(DexField field) {
- if (hasPotentialNonTrivialFieldGetRewriting(field)) {
- hasNonTrivialRewritings = true;
+ if (setHasPotentialNonTrivialFieldGetRewriting(field)) {
+ return;
+ }
+ if (graphLens.isLirToLirDesugaringLens()
+ && graphLens
+ .asLirToLirDesugaringLens()
+ .needsApiOutlining(InstructionKind.SGET, field, context)) {
+ addFieldInstructionToRewrite(field);
}
}
- private boolean hasPotentialNonTrivialFieldGetRewriting(DexField field) {
+ private boolean setHasPotentialNonTrivialFieldGetRewriting(DexField field) {
HorizontalClassMergerGraphLens horizontalClassMergerLens =
graphLens.asHorizontalClassMergerGraphLens();
if (horizontalClassMergerLens != null) {
FieldLookupResult result = horizontalClassMergerLens.lookupFieldResult(field, codeLens);
- return result.hasReadCastType();
+ if (result.hasReadCastType()) {
+ hasNonTrivialRewritings = true;
+ return true;
+ }
}
return false;
}
@Override
public void onInstancePut(DexField field, EV object, EV value) {
- onFieldPut(field);
+ if (setHasPotentialNonTrivialFieldPutRewriting(field)) {
+ return;
+ }
+ if (graphLens.isLirToLirDesugaringLens()
+ && graphLens
+ .asLirToLirDesugaringLens()
+ .needsApiOutlining(InstructionKind.IPUT, field, context)) {
+ addFieldInstructionToRewrite(field);
+ }
}
@Override
public void onStaticPut(DexField field, EV value) {
- onFieldPut(field);
- }
-
- private void onFieldPut(DexField field) {
- if (hasPotentialNonTrivialFieldPutRewriting(field)) {
- hasNonTrivialRewritings = true;
+ if (setHasPotentialNonTrivialFieldPutRewriting(field)) {
+ return;
+ }
+ if (graphLens.isLirToLirDesugaringLens()
+ && graphLens
+ .asLirToLirDesugaringLens()
+ .needsApiOutlining(InstructionKind.SPUT, field, context)) {
+ addFieldInstructionToRewrite(field);
}
}
- private boolean hasPotentialNonTrivialFieldPutRewriting(DexField field) {
+ private boolean setHasPotentialNonTrivialFieldPutRewriting(DexField field) {
HorizontalClassMergerGraphLens horizontalClassMergerLens =
graphLens.asHorizontalClassMergerGraphLens();
if (horizontalClassMergerLens != null) {
FieldLookupResult result = horizontalClassMergerLens.lookupFieldResult(field, codeLens);
- return result.hasWriteCastType();
+ if (result.hasWriteCastType()) {
+ hasNonTrivialRewritings = true;
+ return true;
+ }
}
VerticalClassMergerGraphLens verticalClassMergerLens = graphLens.asVerticalClassMergerLens();
if (verticalClassMergerLens != null
&& verticalClassMergerLens.hasInterfaceBeenMergedIntoClass(field.getType())) {
+ hasNonTrivialRewritings = true;
return true;
}
return false;
@@ -328,7 +415,7 @@
private boolean isInvokeThatMaybeRequiresRewriting(int opcode) {
assert LirOpcodeUtils.isInvokeMethod(opcode);
- if (!invokesToRewrite.isEmpty()) {
+ if (!invokeInstructionsToRewrite.isEmpty()) {
return true;
}
if (codeLens.isIdentityLens() && LirOpcodeUtils.isInvokeMethod(opcode)) {
@@ -362,7 +449,7 @@
return true;
}
}
- if (graphLens instanceof R8LibraryDesugaringGraphLens) {
+ if (graphLens.isLirToLirDesugaringLens()) {
return LirOpcodeUtils.isInvokeMethod(opcode);
}
return false;
@@ -381,7 +468,9 @@
}
assert !hasNonTrivialRewritings;
LirCode<EV> rewritten = rewriteConstantPoolAndScanForTypeChanges(getCode());
- if (hasNonTrivialRewritings) {
+ if (hasNonTrivialRewritings
+ || fieldInstructionsToRewrite != NO_FIELD_INSTRUCTIONS_TO_REWRITE
+ || typeInstructionsToRewrite != NO_TYPE_INSTRUCTIONS_TO_REWRITE) {
return rewriteWithLensCodeRewriter();
}
rewritten = rewriteInstructionsWithInvokeTypeChanges(rewritten);
@@ -481,9 +570,11 @@
boolean hasDexItemBasedConstString = false;
boolean hasFieldReference = false;
boolean hasPotentialRewrittenMethod = false;
+ boolean hasTypeReference = false;
for (LirConstant constant : code.getConstantPool()) {
if (constant instanceof DexType) {
onTypeReference((DexType) constant);
+ hasTypeReference = true;
} else if (constant instanceof DexField) {
onFieldReference((DexField) constant);
hasFieldReference = true;
@@ -505,11 +596,15 @@
// If there are potential method rewritings then we need to iterate the instructions as the
// rewriting is instruction-sensitive (i.e., may be dependent on the invoke type).
- boolean hasPotentialNonTrivialFieldAccessRewriting =
- hasFieldReference && graphLens.isClassMergerLens();
+ boolean hasPotentialRewrittenFieldInstruction =
+ hasFieldReference
+ && (graphLens.isClassMergerLens() || graphLens.isLirToLirDesugaringLens());
+ boolean hasPotentialRewrittenTypeInstruction =
+ hasTypeReference && graphLens.isLirToLirDesugaringLens();
if (hasDexItemBasedConstString
- || hasPotentialNonTrivialFieldAccessRewriting
- || hasPotentialRewrittenMethod) {
+ || hasPotentialRewrittenFieldInstruction
+ || hasPotentialRewrittenMethod
+ || hasPotentialRewrittenTypeInstruction) {
for (LirInstructionView view : code) {
view.accept(this);
if (hasNonTrivialRewritings) {
@@ -527,7 +622,7 @@
}
private LirCode<EV> rewriteInstructionsWithInvokeTypeChanges(LirCode<EV> code) {
- if (numberOfInvokeOpcodeChanges == 0 && invokesToRewrite.isEmpty()) {
+ if (numberOfInvokeOpcodeChanges == 0 && invokeInstructionsToRewrite.isEmpty()) {
return code;
}
// Build a small map from method refs to index in case the type-dependent methods are already
@@ -573,7 +668,7 @@
boolean newIsInterface = lookupIsInterface(method, opcode, result);
InvokeType newType = result.getType();
int newOpcode = newType.getLirOpcode(newIsInterface);
- if (newOpcode != opcode || invokesToRewrite.contains(method)) {
+ if (newOpcode != opcode || invokeInstructionsToRewrite.contains(method)) {
constantIndex =
methodIndices.computeIfAbsent(
result.getReference(),