Extend startup outlining to nowinandroid
Bug: b/275292237
Change-Id: Ib353e80a8bae66f10bfa2f70df8f53ce5d5bc0b2
diff --git a/src/main/java/com/android/tools/r8/graph/lens/NestedGraphLens.java b/src/main/java/com/android/tools/r8/graph/lens/NestedGraphLens.java
index fd7a46d..4363bdf 100644
--- a/src/main/java/com/android/tools/r8/graph/lens/NestedGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/lens/NestedGraphLens.java
@@ -27,8 +27,8 @@
* <p>Subclasses can override the lookup methods.
*
* <p>For method mapping where invocation type can change just override {@link
- * #mapInvocationType(DexMethod, DexMethod, InvokeType)} if the default name mapping applies, and
- * only invocation type might need to change.
+ * #mapInvocationType(DexMethod, DexMethod, DexMethod, InvokeType)} if the default name mapping
+ * applies, and only invocation type might need to change.
*/
public class NestedGraphLens extends DefaultNonIdentityGraphLens {
@@ -199,7 +199,10 @@
rewrittenReboundReference))
.setType(
mapInvocationType(
- rewrittenReboundReference, previous.getReference(), previous.getType()))
+ rewrittenReference,
+ rewrittenReboundReference,
+ previous.getReference(),
+ previous.getType()))
.build();
} else {
// TODO(b/168282032): We should always have the rebound reference, so this should become
@@ -220,7 +223,8 @@
return MethodLookupResult.builder(this, codeLens)
.setReference(newMethod)
.setPrototypeChanges(newPrototypeChanges)
- .setType(mapInvocationType(newMethod, previous.getReference(), previous.getType()))
+ .setType(
+ mapInvocationType(newMethod, newMethod, previous.getReference(), previous.getType()))
.build();
}
}
@@ -271,7 +275,7 @@
* method or {@link #lookupMethod(DexMethod, DexMethod, InvokeType)}
*/
protected InvokeType mapInvocationType(
- DexMethod newMethod, DexMethod originalMethod, InvokeType type) {
+ DexMethod newMethod, DexMethod newReboundMethod, DexMethod originalMethod, InvokeType type) {
return type;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
index 5d60881..ca1d499 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
@@ -196,7 +196,7 @@
.setPrototypeChanges(
internalDescribePrototypeChanges(
previous.getPrototypeChanges(), previous.getReference(), result))
- .setType(mapInvocationType(result, previous.getReference(), previous.getType()))
+ .setType(mapInvocationType(result, result, previous.getReference(), previous.getType()))
.build();
}
@@ -244,7 +244,7 @@
@Override
@SuppressWarnings("ReferenceEquality")
protected InvokeType mapInvocationType(
- DexMethod newMethod, DexMethod originalMethod, InvokeType type) {
+ DexMethod newMethod, DexMethod newReboundMethod, DexMethod originalMethod, InvokeType type) {
if (typeMap.containsKey(originalMethod.getHolderType())) {
// Methods moved from unboxed enums to the utility class are either static or statified.
assert newMethod != originalMethod;
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 d14a8bd..0b52815 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirLensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirLensCodeRewriter.java
@@ -138,7 +138,7 @@
|| (type.isDirect()
&& (newType.isInterface() || newType.isStatic() || newType.isVirtual()))
|| (type.isInterface() && (newType.isStatic() || newType.isVirtual()))
- || (type.isSuper() && newType.isVirtual())
+ || (type.isSuper() && (newType.isStatic() || newType.isVirtual()))
|| (type.isVirtual() && (newType.isInterface() || newType.isStatic()))
: type + " -> " + newType;
numberOfInvokeOpcodeChanges++;
@@ -301,6 +301,9 @@
if (LirOpcodeUtils.isInvokeInterface(opcode)) {
return InvokeType.INTERFACE;
}
+ if (LirOpcodeUtils.isInvokeSuper(opcode)) {
+ return InvokeType.SUPER;
+ }
if (LirOpcodeUtils.isInvokeVirtual(opcode)) {
return InvokeType.VIRTUAL;
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LirOpcodeUtils.java b/src/main/java/com/android/tools/r8/lightir/LirOpcodeUtils.java
index ec788d1..d9efe9b 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirOpcodeUtils.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirOpcodeUtils.java
@@ -78,6 +78,10 @@
}
}
+ public static boolean isInvokeSuper(int opcode) {
+ return opcode == INVOKESUPER || opcode == INVOKESUPER_ITF;
+ }
+
public static boolean isInvokeVirtual(int opcode) {
return opcode == INVOKEVIRTUAL;
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
index a5a4a61..87a811b 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
@@ -89,13 +89,13 @@
@Override
protected InvokeType mapInvocationType(
- DexMethod newMethod, DexMethod originalMethod, InvokeType type) {
- return hasPrototypeChanges(newMethod)
- && getPrototypeChanges(newMethod)
+ DexMethod newMethod, DexMethod newReboundMethod, DexMethod originalMethod, InvokeType type) {
+ return hasPrototypeChanges(newReboundMethod)
+ && getPrototypeChanges(newReboundMethod)
.getArgumentInfoCollection()
.isConvertedToStaticMethod()
? InvokeType.STATIC
- : super.mapInvocationType(newMethod, originalMethod, type);
+ : super.mapInvocationType(newMethod, newReboundMethod, originalMethod, type);
}
public static class Builder {
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
index e524b2a..990c260 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
@@ -440,13 +440,16 @@
assert !info.isPinned(options)
|| info.isMinificationAllowed(options)
|| newMethod.name.isIdenticalTo(method.name);
- assert !info.isPinned(options) || newMethod.getArity() == method.getArity();
+ assert !info.isPinned(options)
+ || newMethod.getArity() == method.getArity()
+ || (info.isShrinkingAllowed(options) && lens.isNonStartupInStartupOutlinerLens());
assert !info.isPinned(options)
|| Streams.zip(
newMethod.getParameters().stream(),
method.getParameters().stream().map(lens::lookupType),
Object::equals)
- .allMatch(x -> x);
+ .allMatch(x -> x)
+ || (info.isShrinkingAllowed(options) && lens.isNonStartupInStartupOutlinerLens());
assert !info.isPinned(options)
|| newMethod.getReturnType().isIdenticalTo(lens.lookupType(method.getReturnType()));
KeepMethodInfo previous = newMethodInfo.put(newMethod, info);
diff --git a/src/main/java/com/android/tools/r8/startup/NonStartupInStartupOutliner.java b/src/main/java/com/android/tools/r8/startup/NonStartupInStartupOutliner.java
index 627c46b..5e5f3bd 100644
--- a/src/main/java/com/android/tools/r8/startup/NonStartupInStartupOutliner.java
+++ b/src/main/java/com/android/tools/r8/startup/NonStartupInStartupOutliner.java
@@ -41,6 +41,8 @@
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
+// TODO(b/275292237): Preserve class initialization side effects using InitClass.
+// TODO(b/275292237): Preserve receiver NPE semantics by inserting null checks.
public class NonStartupInStartupOutliner {
private final AppView<? extends AppInfoWithClassHierarchy> appView;
@@ -103,7 +105,7 @@
method -> {
if (!startupProfile.containsMethodRule(method.getReference())
&& !method.getDefinition().isInitializer()
- && !hasPrivateOrSuperAccess(method)) {
+ && canCodeBeMoved(method)) {
fn.accept(method);
}
});
@@ -111,20 +113,16 @@
// TODO(b/275292237): Extend to cover all possible accesses to private items (e.g., consider
// method handles).
- private boolean hasPrivateOrSuperAccess(ProgramMethod method) {
+ private boolean canCodeBeMoved(ProgramMethod method) {
return method.registerCodeReferencesWithResult(
- new DefaultUseRegistryWithResult<>(appView, method, false) {
+ new DefaultUseRegistryWithResult<>(appView, method, true) {
private AppInfoWithClassHierarchy appInfo() {
return NonStartupInStartupOutliner.this.appView.appInfo();
}
- private void setHasPrivateAccess() {
- setResult(true);
- }
-
- private void setHasSuperAccess() {
- setResult(true);
+ private void setCodeCannotBeMoved() {
+ setResult(false);
}
// Field accesses.
@@ -152,8 +150,14 @@
private void registerFieldAccess(DexField field) {
DexClassAndField resolvedField =
appInfo().resolveField(field, getContext()).getResolutionPair();
- if (resolvedField != null && resolvedField.getAccessFlags().isPrivate()) {
- setHasPrivateAccess();
+ if (resolvedField == null) {
+ return;
+ }
+ if (resolvedField.getAccessFlags().isPrivate()) {
+ setCodeCannotBeMoved();
+ } else if (resolvedField.getAccessFlags().isProtected()
+ && !resolvedField.isSamePackage(getContext())) {
+ setCodeCannotBeMoved();
}
}
@@ -176,7 +180,7 @@
@Override
public void registerInvokeSuper(DexMethod method) {
- setHasSuperAccess();
+ setCodeCannotBeMoved();
}
@Override
@@ -186,8 +190,14 @@
private void registerInvokeMethod(MethodResolutionResult resolutionResult) {
DexClassAndMethod resolvedMethod = resolutionResult.getResolutionPair();
- if (resolvedMethod != null && resolvedMethod.getAccessFlags().isPrivate()) {
- setHasPrivateAccess();
+ if (resolvedMethod == null) {
+ return;
+ }
+ if (resolvedMethod.getAccessFlags().isPrivate()) {
+ setCodeCannotBeMoved();
+ } else if (resolvedMethod.getAccessFlags().isProtected()
+ && !resolvedMethod.isSamePackage(getContext())) {
+ setCodeCannotBeMoved();
}
}
});
@@ -267,7 +277,8 @@
}
// Virtual methods can only be staticized and moved if they are monomorphic.
assert method.getAccessFlags().belongsToVirtualPool();
- return monomorphicVirtualMethods.contains(method);
+ return method.getDefinition().isLibraryMethodOverride().isFalse()
+ && monomorphicVirtualMethods.contains(method);
}
private ProgramMethod performMove(
@@ -324,16 +335,23 @@
method.getReference(), !method.getAccessFlags().isStatic()))
.setCode(
syntheticMethod -> {
- Code code =
- method
- .getDefinition()
- .getCode()
- .getCodeAsInlining(
- syntheticMethod,
- true,
- method.getReference(),
- method.getDefinition().isD8R8Synthesized(),
- factory);
+ Code code;
+ if (method.getDefinition().getCode().supportsPendingInlineFrame()) {
+ code = method.getDefinition().getCode();
+ builder.setInlineFrame(
+ method.getReference(), method.getDefinition().isD8R8Synthesized());
+ } else {
+ code =
+ method
+ .getDefinition()
+ .getCode()
+ .getCodeAsInlining(
+ syntheticMethod,
+ true,
+ method.getReference(),
+ method.getDefinition().isD8R8Synthesized(),
+ factory);
+ }
if (!method.getAccessFlags().isStatic()) {
DexEncodedMethod.setDebugInfoWithFakeThisParameter(
code, syntheticMethod.getArity(), appView);
diff --git a/src/main/java/com/android/tools/r8/startup/NonStartupInStartupOutlinerLens.java b/src/main/java/com/android/tools/r8/startup/NonStartupInStartupOutlinerLens.java
index 086eab6..61ee4ec 100644
--- a/src/main/java/com/android/tools/r8/startup/NonStartupInStartupOutlinerLens.java
+++ b/src/main/java/com/android/tools/r8/startup/NonStartupInStartupOutlinerLens.java
@@ -31,11 +31,8 @@
@Override
protected InvokeType mapInvocationType(
- DexMethod newMethod, DexMethod previousMethod, InvokeType type) {
- if (newMethod.isIdenticalTo(previousMethod)) {
- return type;
- }
- return InvokeType.STATIC;
+ DexMethod newMethod, DexMethod newReboundMethod, DexMethod previousMethod, InvokeType type) {
+ return newMethod.isIdenticalTo(previousMethod) ? type : InvokeType.STATIC;
}
public static class Builder {
@@ -50,7 +47,6 @@
public synchronized void recordMove(ProgramMethod from, ProgramMethod to) {
newMethodSignatures.put(from.getReference(), to.getReference());
}
-
public NonStartupInStartupOutlinerLens build(
AppView<? extends AppInfoWithClassHierarchy> appView) {
return new NonStartupInStartupOutlinerLens(appView, newMethodSignatures);
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
index b758859..27ae067 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
@@ -35,6 +35,7 @@
private DexProto proto = null;
private CfVersion classFileVersion;
private SyntheticCodeGenerator codeGenerator = null;
+ private DexMethod pendingInlineFrame = null;
private MethodAccessFlags accessFlags = null;
private MethodTypeSignature genericSignature = MethodTypeSignature.noSignature();
private DexAnnotationSet annotations = DexAnnotationSet.empty();
@@ -95,6 +96,14 @@
return this;
}
+ public SyntheticMethodBuilder setInlineFrame(DexMethod callee, boolean calleeIsD8R8Synthesized) {
+ if (!calleeIsD8R8Synthesized) {
+ // // No need to record a compiler synthesized inline frame.
+ this.pendingInlineFrame = callee;
+ }
+ return this;
+ }
+
public SyntheticMethodBuilder setAccessFlags(MethodAccessFlags accessFlags) {
this.accessFlags = accessFlags;
return this;
@@ -144,6 +153,7 @@
.setAnnotations(annotations)
.setParameterAnnotations(parameterAnnotationsList)
.setCode(code)
+ .applyIf(pendingInlineFrame != null, b -> b.setInlineFrame(pendingInlineFrame, false))
.setClassFileVersion(classFileVersion)
.setApiLevelForDefinition(apiLevelForDefinition)
.setApiLevelForCode(apiLevelForCode)
diff --git a/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java b/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
index 7f78da8..516241e 100644
--- a/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
@@ -462,7 +462,11 @@
assert residualIsD8R8Synthesized
|| originalMethod.isIdenticalTo(lensOriginalMethod)
// TODO(b/326562454): In some case the lens is mapping two methods to a common original.
- || originalMethod.getHolderType().isIdenticalTo(lensOriginalMethod.getHolderType());
+ || originalMethod.getHolderType().isIdenticalTo(lensOriginalMethod.getHolderType())
+ || appView
+ .getSyntheticItems()
+ .isSyntheticOfKind(
+ method.getHolderType(), kinds -> kinds.NON_STARTUP_IN_STARTUP_OUTLINE);
return true;
}
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerGraphLens.java
index f6583d2..7860fed 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerGraphLens.java
@@ -222,7 +222,12 @@
MethodLookupResult.builder(this, codeLens)
.setReboundReference(newReboundReference)
.setReference(newReference)
- .setType(mapInvocationType(newReference, previous.getReference(), previous.getType()))
+ .setType(
+ mapInvocationType(
+ newReference,
+ newReboundReference,
+ previous.getReference(),
+ previous.getType()))
.setPrototypeChanges(
internalDescribePrototypeChanges(
previous.getPrototypeChanges(),
@@ -287,8 +292,8 @@
@Override
protected InvokeType mapInvocationType(
- DexMethod newMethod, DexMethod previousMethod, InvokeType type) {
- if (isStaticized(newMethod)) {
+ DexMethod newMethod, DexMethod newReboundMethod, DexMethod previousMethod, InvokeType type) {
+ if (isStaticized(newReboundMethod)) {
return InvokeType.STATIC;
}
if (type.isInterface()