Only run class inliner analysis for parameters that may be class inlined
Change-Id: Ib4250d633944bb105400c89f809078fa3ce945bf
diff --git a/src/main/java/com/android/tools/r8/graph/ClassDefinition.java b/src/main/java/com/android/tools/r8/graph/ClassDefinition.java
index f4f1c31..da0f128 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassDefinition.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassDefinition.java
@@ -21,6 +21,8 @@
DexType getType();
+ boolean isInterface();
+
@Override
default boolean isClass() {
return true;
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index f0745c8..130dbcf 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -598,6 +598,7 @@
return isFinal();
}
+ @Override
public boolean isInterface() {
return accessFlags.isInterface();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index cbc8c6b..506e8af 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -1096,6 +1096,14 @@
return arguments;
}
+ public Argument getLastArgument() {
+ InstructionIterator instructionIterator = entryBlock().iterator(getNumberOfArguments() - 1);
+ Argument lastArgument = instructionIterator.next().asArgument();
+ assert lastArgument != null;
+ assert !instructionIterator.peekNext().isArgument();
+ return lastArgument;
+ }
+
public Value getThis() {
if (method().accessFlags.isStatic()) {
return null;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ClassInlinerMethodConstraintAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ClassInlinerMethodConstraintAnalysis.java
index a0ce6b0..b8f406a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ClassInlinerMethodConstraintAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ClassInlinerMethodConstraintAnalysis.java
@@ -25,7 +25,7 @@
// Analyze code.
IntraproceduralDataflowAnalysis<ParameterUsages> analysis =
new IntraproceduralDataflowAnalysis<>(
- ParameterUsages.bottom(), new TransferFunction(appView, method));
+ ParameterUsages.bottom(), new TransferFunction(appView, method, code));
SuccessfulDataflowAnalysisResult<ParameterUsages> result =
analysis.run(code.entryBlock()).asSuccessfulAnalysisResult();
if (result == null) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsages.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsages.java
index 8878976..f7b2cae 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsages.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsages.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.utils.ArrayUtils;
import com.android.tools.r8.utils.Int2ObjectMapUtils;
import com.android.tools.r8.utils.IntObjConsumer;
+import com.android.tools.r8.utils.IntObjPredicate;
import com.android.tools.r8.utils.IntObjToObjFunction;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
@@ -77,6 +78,15 @@
return backing.isEmpty() ? bottom() : new NonEmptyParameterUsages(backing);
}
+ public boolean allMatch(IntObjPredicate<ParameterUsagePerContext> predicate) {
+ for (Int2ObjectMap.Entry<ParameterUsagePerContext> entry : backing.int2ObjectEntrySet()) {
+ if (!predicate.test(entry.getIntKey(), entry.getValue())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
@Override
public NonEmptyParameterUsages asNonEmpty() {
return this;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java
index 740a8cd..8cbade6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java
@@ -17,17 +17,25 @@
import static com.android.tools.r8.ir.code.Opcodes.RETURN;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ClasspathOrLibraryClass;
+import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.FieldResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.framework.intraprocedural.AbstractTransferFunction;
+import com.android.tools.r8.ir.analysis.framework.intraprocedural.FailedTransferFunctionResult;
+import com.android.tools.r8.ir.analysis.framework.intraprocedural.TransferFunctionResult;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.AliasedValueConfiguration;
import com.android.tools.r8.ir.code.Argument;
import com.android.tools.r8.ir.code.Assume;
import com.android.tools.r8.ir.code.AssumeAndCheckCastAliasedValueConfiguration;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.CheckCast;
+import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.InstanceGet;
import com.android.tools.r8.ir.code.InstancePut;
@@ -53,6 +61,9 @@
private final DexItemFactory dexItemFactory;
private final ProgramMethod method;
+ // The last argument instruction.
+ private final Argument lastArgument;
+
// Caches the parent or forwarding constructor call (only used in constructors).
private InvokeDirect constructorInvoke;
@@ -65,16 +76,26 @@
// skipped.
private Set<Instruction> instructionsOfInterest = Sets.newIdentityHashSet();
- TransferFunction(AppView<AppInfoWithLiveness> appView, ProgramMethod method) {
+ TransferFunction(AppView<AppInfoWithLiveness> appView, ProgramMethod method, IRCode code) {
this.appView = appView;
this.dexItemFactory = appView.dexItemFactory();
this.method = method;
+ this.lastArgument = code.getLastArgument();
}
@Override
- public ParameterUsages apply(Instruction instruction, ParameterUsages state) {
+ public TransferFunctionResult<ParameterUsages> apply(
+ Instruction instruction, ParameterUsages state) {
if (instruction.isArgument()) {
- return analyzeArgument(instruction.asArgument(), state);
+ Argument argument = instruction.asArgument();
+ ParameterUsages result = analyzeArgument(argument, state);
+ // After analyzing the last argument instruction, only proceed if there is at least one
+ // argument that may be eligible for class inlining.
+ if (argument == lastArgument
+ && result.asNonEmpty().allMatch((context, usagePerContext) -> usagePerContext.isTop())) {
+ return new FailedTransferFunctionResult<>();
+ }
+ return result;
}
if (!instructionsOfInterest.contains(instruction)) {
// The instruction does not use any of the argument values that we are analyzing, so for the
@@ -131,7 +152,7 @@
// we can't ignore parameters with a library type, since instances of program classes could
// still flow into such parameters.
Value value = argument.outValue();
- if (!value.getType().isClassType() || value.hasPhiUsers()) {
+ if (!isMaybeEligibleForClassInlining(value.getType()) || value.hasPhiUsers()) {
return state.put(argument.getIndex(), ParameterUsagePerContext.top());
}
@@ -326,4 +347,40 @@
assert value.getAliasedValue(aliasedValueConfiguration) == value;
return value.isArgument() && argumentsOfInterest.contains(value);
}
+
+ private boolean isMaybeEligibleForClassInlining(TypeElement type) {
+ if (!type.isClassType()) {
+ // Primitives and arrays will never be class inlined.
+ return false;
+ }
+ DexClass clazz = appView.definitionFor(type.asClassType().getClassType());
+ if (clazz == null) {
+ // We cannot class inline in presence of missing classes.
+ return false;
+ }
+ return clazz.isProgramClass()
+ ? isMaybeEligibleForClassInlining(clazz.asProgramClass())
+ : isMaybeEligibleForClassInlining(clazz.asClasspathOrLibraryClass());
+ }
+
+ private boolean isMaybeEligibleForClassInlining(DexProgramClass clazz) {
+ // We can only class inline parameters that does not inherit from other classpath or library
+ // classes than java.lang.Object.
+ DexType superType = clazz.getSuperType();
+ do {
+ DexClass superClass = appView.definitionFor(superType);
+ if (superClass == null) {
+ return false;
+ }
+ if (!superClass.isProgramClass()) {
+ return superClass.getType() == dexItemFactory.objectType;
+ }
+ superType = superClass.getSuperType();
+ } while (true);
+ }
+
+ private boolean isMaybeEligibleForClassInlining(ClasspathOrLibraryClass clazz) {
+ // We can only class inline a parameter that is either java.lang.Object or an interface type.
+ return clazz.getType() == dexItemFactory.objectType || clazz.isInterface();
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/IntObjPredicate.java b/src/main/java/com/android/tools/r8/utils/IntObjPredicate.java
new file mode 100644
index 0000000..a73a7be
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/IntObjPredicate.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2019, 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.utils;
+
+public interface IntObjPredicate<T> {
+
+ boolean test(int i, T obj);
+}