Towards support for recursive inlining in class inliner
Change-Id: Ifaef9bef01002e1224a8e078a976c4b1d3a99d89
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
index 1a7de86..a24212f 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
@@ -468,6 +468,12 @@
return !getTypeInfo(type).directSubtypes.isEmpty();
}
+ public boolean isRelatedBySubtyping(DexType type, DexType other) {
+ assert type.isClassType();
+ assert other.isClassType();
+ return isSubtype(type, other) || isSubtype(other, type);
+ }
+
@Override
public boolean isSubtype(DexType subtype, DexType supertype) {
if (subtype == supertype || isStrictSubtypeOf(subtype, supertype)) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerEligibilityInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerEligibilityInfo.java
index a80a9ec..bf06bc1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerEligibilityInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerEligibilityInfo.java
@@ -4,10 +4,16 @@
package com.android.tools.r8.ir.optimize.classinliner;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.utils.OptionalBool;
+import com.android.tools.r8.utils.Pair;
+import java.util.List;
public class ClassInlinerEligibilityInfo {
+ final List<Pair<Invoke.Type, DexMethod>> callsReceiver;
+
/**
* Set to {@link OptionalBool#TRUE} if the method is guaranteed to return the receiver, {@link
* OptionalBool#FALSE} if the method is guaranteed not to return the receiver, and {@link
@@ -15,7 +21,9 @@
*/
final OptionalBool returnsReceiver;
- public ClassInlinerEligibilityInfo(OptionalBool returnsReceiver) {
+ public ClassInlinerEligibilityInfo(
+ List<Pair<Invoke.Type, DexMethod>> callsReceiver, OptionalBool returnsReceiver) {
+ this.callsReceiver = callsReceiver;
this.returnsReceiver = returnsReceiver;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index a831b86..c522ff3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -334,8 +334,7 @@
&& !invoke.inValues().isEmpty()
&& root.outValue() == invoke.getReceiver();
if (isCorrespondingConstructorCall) {
- InliningInfo inliningInfo =
- isEligibleConstructorCall(invoke, singleTarget, defaultOracle);
+ InliningInfo inliningInfo = isEligibleConstructorCall(invoke, singleTarget);
if (inliningInfo != null) {
methodCallsOnInstance.put(invoke, inliningInfo);
continue;
@@ -676,7 +675,7 @@
}
private InliningInfo isEligibleConstructorCall(
- InvokeDirect invoke, DexEncodedMethod singleTarget, Supplier<InliningOracle> defaultOracle) {
+ InvokeDirect invoke, DexEncodedMethod singleTarget) {
assert appView.dexItemFactory().isConstructor(invoke.getInvokedMethod());
assert isEligibleSingleTarget(singleTarget);
@@ -702,8 +701,9 @@
}
// Check that the `eligibleInstance` does not escape via the constructor.
- ParameterUsage parameterUsage = singleTarget.getOptimizationInfo().getParameterUsages(0);
- if (!isEligibleParameterUsage(parameterUsage, invoke, defaultOracle)) {
+ InstanceInitializerInfo instanceInitializerInfo =
+ singleTarget.getOptimizationInfo().getInstanceInitializerInfo();
+ if (instanceInitializerInfo.receiverMayEscapeOutsideConstructorChain()) {
return null;
}
@@ -715,7 +715,7 @@
// Check that the entire constructor chain can be inlined into the current context.
DexItemFactory dexItemFactory = appView.dexItemFactory();
- DexMethod parent = singleTarget.getOptimizationInfo().getInstanceInitializerInfo().getParent();
+ DexMethod parent = instanceInitializerInfo.getParent();
while (parent != dexItemFactory.objectMethods.constructor) {
if (parent == null) {
return null;
@@ -734,9 +734,7 @@
parent = encodedParent.getOptimizationInfo().getInstanceInitializerInfo().getParent();
}
- return singleTarget.getOptimizationInfo().getClassInlinerEligibility() != null
- ? new InliningInfo(singleTarget, eligibleClass)
- : null;
+ return new InliningInfo(singleTarget, eligibleClass);
}
// An invoke is eligible for inlining in the following cases:
@@ -876,7 +874,7 @@
MethodOptimizationInfo optimizationInfo = singleTarget.getOptimizationInfo();
ClassInlinerEligibilityInfo eligibility = optimizationInfo.getClassInlinerEligibility();
- if (eligibility == null) {
+ if (eligibility == null || !eligibility.callsReceiver.isEmpty()) {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index 172076a..1fb2e0c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -67,9 +67,11 @@
import com.android.tools.r8.ir.code.InstancePut;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionIterator;
+import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeNewArray;
+import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.NewInstance;
import com.android.tools.r8.ir.code.Return;
import com.android.tools.r8.ir.code.Value;
@@ -86,6 +88,7 @@
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
+import com.android.tools.r8.utils.Pair;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
@@ -161,6 +164,7 @@
return;
}
+ List<Pair<Invoke.Type, DexMethod>> callsReceiver = new ArrayList<>();
boolean seenSuperInitCall = false;
for (Instruction insn : receiver.aliasedUsers()) {
if (insn.isAssume()) {
@@ -208,6 +212,27 @@
return;
}
+ if (insn.isInvokeVirtual()) {
+ InvokeVirtual invoke = insn.asInvokeVirtual();
+ if (invoke.getReceiver().getAliasedValue() != receiver) {
+ return; // Not allowed.
+ }
+ for (int i = 1; i < invoke.arguments().size(); i++) {
+ Value argument = invoke.arguments().get(i);
+ if (argument.getAliasedValue() == receiver) {
+ return; // Not allowed.
+ }
+ }
+ DexMethod invokedMethod = invoke.getInvokedMethod();
+ DexType returnType = invokedMethod.proto.returnType;
+ if (returnType.isClassType()
+ && appView.appInfo().isRelatedBySubtyping(returnType, method.method.holder)) {
+ return; // Not allowed, could introduce an alias of the receiver.
+ }
+ callsReceiver.add(new Pair<>(Invoke.Type.VIRTUAL, invokedMethod));
+ continue;
+ }
+
if (insn.isReturn()) {
continue;
}
@@ -224,6 +249,7 @@
feedback.setClassInlinerEligibility(
method,
new ClassInlinerEligibilityInfo(
+ callsReceiver,
new ClassInlinerReceiverAnalysis(appView, method, code).computeReturnsReceiver()));
}