Refactor class inliner to use new constraint analysis
Bug: 173337498
Bug: 181746071
Change-Id: I691e06b3bcf371fbe4c7c0b8d86bba51a3087fad
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/DataflowAnalysisResult.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/DataflowAnalysisResult.java
index 701df38..a8a1832 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/DataflowAnalysisResult.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/DataflowAnalysisResult.java
@@ -42,6 +42,14 @@
this.blockExitStates = blockExitStates;
}
+ public StateType join() {
+ StateType result = null;
+ for (StateType blockExitState : blockExitStates.values()) {
+ result = result != null ? result.join(blockExitState) : blockExitState;
+ }
+ return result;
+ }
+
@Override
public boolean isSuccessfulAnalysisResult() {
return true;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
index 0cdf967..9dfe2e3 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
@@ -13,9 +13,7 @@
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
-import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
-import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -64,15 +62,11 @@
void setClassInlinerMethodConstraint(
ProgramMethod method, ClassInlinerMethodConstraint classInlinerConstraint);
- void setClassInlinerEligibility(DexEncodedMethod method, ClassInlinerEligibilityInfo eligibility);
-
void setInstanceInitializerInfoCollection(
DexEncodedMethod method, InstanceInitializerInfoCollection instanceInitializerInfoCollection);
void setInitializerEnablingJavaVmAssertions(DexEncodedMethod method);
- void setParameterUsages(DexEncodedMethod method, ParameterUsagesInfo parameterUsagesInfo);
-
void setNonNullParamOrThrow(DexEncodedMethod method, BitSet facts);
void setNonNullParamOnNormalExits(DexEncodedMethod method, BitSet facts);
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
deleted file mode 100644
index 78e756d..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerEligibilityInfo.java
+++ /dev/null
@@ -1,37 +0,0 @@
-// 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.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
- * OptionalBool#UNKNOWN} if the method may return the receiver.
- */
- final OptionalBool returnsReceiver;
-
- public final boolean hasMonitorOnReceiver;
- public final boolean modifiesInstanceFields;
-
- public ClassInlinerEligibilityInfo(
- List<Pair<Invoke.Type, DexMethod>> callsReceiver,
- OptionalBool returnsReceiver,
- boolean hasMonitorOnReceiver,
- boolean modifiesInstanceFields) {
- this.callsReceiver = callsReceiver;
- this.returnsReceiver = returnsReceiver;
- this.hasMonitorOnReceiver = hasMonitorOnReceiver;
- this.modifiesInstanceFields = modifiesInstanceFields;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerReceiverAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerReceiverAnalysis.java
deleted file mode 100644
index e0f991d..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerReceiverAnalysis.java
+++ /dev/null
@@ -1,135 +0,0 @@
-// 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.ir.optimize.classinliner;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.utils.BooleanLatticeElement;
-import com.android.tools.r8.utils.OptionalBool;
-import java.util.IdentityHashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * This analysis determines whether a method returns the receiver. The analysis is specific to the
- * class inliner, and the result is therefore not sound in general.
- *
- * <p>The analysis makes the following assumptions.
- *
- * <ul>
- * <li>None of the given method's arguments is an alias of the receiver (except for the receiver
- * itself).
- * <li>The receiver is not stored in the heap. Thus, it is guaranteed that (i) all field-get
- * instructions do not return an alias of the receiver, and (ii) invoke instructions can only
- * return an alias of the receiver if the receiver is given as an argument.
- * </ul>
- */
-public class ClassInlinerReceiverAnalysis {
-
- private final AppView<?> appView;
- private final DexEncodedMethod method;
- private final IRCode code;
- private final Value receiver;
-
- private final Map<Value, OptionalBool> isReceiverAliasCache = new IdentityHashMap<>();
-
- public ClassInlinerReceiverAnalysis(AppView<?> appView, DexEncodedMethod method, IRCode code) {
- this.appView = appView;
- this.method = method;
- this.code = code;
- this.receiver = code.getThis();
- assert !receiver.hasAliasedValue();
- assert receiver.getType().isClassType();
- }
-
- public OptionalBool computeReturnsReceiver() {
- if (method.method.proto.returnType.isVoidType()) {
- return OptionalBool.FALSE;
- }
-
- List<BasicBlock> normalExitBlocks = code.computeNormalExitBlocks();
- if (normalExitBlocks.isEmpty()) {
- return OptionalBool.FALSE;
- }
-
- BooleanLatticeElement result = OptionalBool.BOTTOM;
- for (BasicBlock block : normalExitBlocks) {
- Value returnValue = block.exit().asReturn().returnValue();
- result = result.join(getOrComputeIsReceiverAlias(returnValue));
-
- // Stop as soon as we reach unknown.
- if (result.isUnknown()) {
- return OptionalBool.UNKNOWN;
- }
- }
- assert !result.isBottom();
- return result.asOptionalBool();
- }
-
- private OptionalBool getOrComputeIsReceiverAlias(Value value) {
- Value root = value.getAliasedValue();
- return isReceiverAliasCache.computeIfAbsent(root, this::computeIsReceiverAlias);
- }
-
- private OptionalBool computeIsReceiverAlias(Value value) {
- assert !value.hasAliasedValue();
-
- if (value == receiver) {
- // Guaranteed to return the receiver.
- return OptionalBool.TRUE;
- }
-
- ClassTypeElement valueType = value.getType().asClassType();
- if (valueType == null) {
- return OptionalBool.FALSE;
- }
-
- ClassTypeElement receiverType = receiver.getType().asClassType();
- if (!valueType.isRelatedTo(receiverType, appView)) {
- // Guaranteed not to return the receiver.
- return OptionalBool.FALSE;
- }
-
- if (value.isPhi()) {
- // Not sure what is returned.
- return OptionalBool.UNKNOWN;
- }
-
- Instruction definition = value.definition;
- if (definition.isArrayGet() || definition.isFieldGet()) {
- // Guaranteed not to return the receiver, since the class inliner does not allow the
- // receiver to flow into any arrays or fields.
- return OptionalBool.FALSE;
- }
-
- if (definition.isConstInstruction() || definition.isCreatingInstanceOrArray()) {
- // Guaranteed not to return the receiver.
- return OptionalBool.FALSE;
- }
-
- if (definition.isInvokeMethod()) {
- // Since the class inliner does not allow the receiver to flow into the heap, the only way for
- // the invoked method to return the receiver is if one of the given arguments is an alias of
- // the receiver.
- InvokeMethod invoke = definition.asInvokeMethod();
- for (Value argument : invoke.arguments()) {
- if (getOrComputeIsReceiverAlias(argument).isPossiblyTrue()) {
- return OptionalBool.UNKNOWN;
- }
- }
-
- return OptionalBool.FALSE;
- }
-
- // Not sure what is returned.
- return OptionalBool.UNKNOWN;
- }
-}
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 38b0b5f..4b15332 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
@@ -4,7 +4,6 @@
package com.android.tools.r8.ir.optimize.classinliner;
-import static com.android.tools.r8.graph.DexEncodedMethod.asProgramMethodOrNull;
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import com.android.tools.r8.errors.InternalCompilerError;
@@ -37,8 +36,6 @@
import com.android.tools.r8.ir.code.InstancePut;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionOrPhi;
-import com.android.tools.r8.ir.code.Invoke;
-import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
@@ -51,10 +48,11 @@
import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.ir.optimize.InliningOracle;
import com.android.tools.r8.ir.optimize.classinliner.ClassInliner.EligibilityStatus;
+import com.android.tools.r8.ir.optimize.classinliner.analysis.NonEmptyParameterUsage;
+import com.android.tools.r8.ir.optimize.classinliner.analysis.ParameterUsage;
import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
import com.android.tools.r8.ir.optimize.info.FieldOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
-import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
import com.android.tools.r8.ir.optimize.inliner.InliningIRProvider;
import com.android.tools.r8.ir.optimize.inliner.NopWhyAreYouNotInliningReporter;
@@ -69,7 +67,6 @@
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Comparator;
@@ -83,8 +80,6 @@
final class InlineCandidateProcessor {
- private static final ImmutableSet<If.Type> ALLOWED_ZERO_TEST_TYPES =
- ImmutableSet.of(If.Type.EQ, If.Type.NE);
private static final AliasedValueConfiguration aliasesThroughAssumeAndCheckCasts =
AssumeAndCheckCastAliasedValueConfiguration.getInstance();
@@ -357,8 +352,6 @@
// Eligible usage as an invocation argument.
if (isExtraMethodCall(invokeMethod)) {
- assert !invokeMethod.isInvokeSuper();
- assert !invokeMethod.isInvokePolymorphic();
if (isExtraMethodCallEligible(
invokeMethod, resolutionResult, singleProgramTarget, defaultOracle)) {
continue;
@@ -999,42 +992,60 @@
}
}
- if (!isEligibleVirtualMethodCall(
- invoke.getInvokedMethod(),
- singleTarget,
- invoke.getType())) {
+ if (!isEligibleVirtualMethodCall(invoke.getInvokedMethod(), singleTarget)) {
return null;
}
- ClassInlinerEligibilityInfo eligibility =
- singleTarget.getDefinition().getOptimizationInfo().getClassInlinerEligibility();
- if (!isEligibleInvokeWithAllUsersAsReceivers(
- invoke, eligibility.returnsReceiver, indirectUsers)) {
+ ParameterUsage usage =
+ singleTarget
+ .getDefinition()
+ .getOptimizationInfo()
+ .getClassInlinerMethodConstraint()
+ .getParameterUsage(0);
+ assert !usage.isTop();
+
+ // If the method returns receiver and the return value is actually used in the code we need to
+ // make some additional checks.
+ OptionalBool isReceiverReturned;
+ if (usage.isParameterReturned()) {
+ if (singleTarget.getDefinition().getOptimizationInfo().returnsArgument()) {
+ assert singleTarget.getDefinition().getOptimizationInfo().getReturnedArgument() == 0;
+ isReceiverReturned = OptionalBool.TRUE;
+ } else {
+ isReceiverReturned = OptionalBool.UNKNOWN;
+ }
+ } else {
+ isReceiverReturned = OptionalBool.FALSE;
+ }
+
+ if (!isEligibleInvokeWithAllUsersAsReceivers(invoke, isReceiverReturned, indirectUsers)) {
return null;
}
- if (eligibility.callsReceiver.size() > 1) {
- return null;
- }
- if (!eligibility.callsReceiver.isEmpty()) {
- assert eligibility.callsReceiver.get(0).getFirst() == Invoke.Type.VIRTUAL;
- Pair<Type, DexMethod> invokeInfo = eligibility.callsReceiver.get(0);
- Type invokeType = invokeInfo.getFirst();
- DexMethod indirectlyInvokedMethod = invokeInfo.getSecond();
- SingleResolutionResult indirectResolutionResult =
- appView
- .appInfo()
- .resolveMethodOn(eligibleClass, indirectlyInvokedMethod)
- .asSingleResolution();
- if (indirectResolutionResult == null) {
+
+ if (!usage.isBottom()) {
+ NonEmptyParameterUsage nonEmptyUsage = usage.asNonEmpty();
+ if (nonEmptyUsage.getMethodCallsWithParameterAsReceiver().size() > 1) {
return null;
}
- ProgramMethod indirectSingleTarget =
- indirectResolutionResult.getResolutionPair().asProgramMethod();
- if (!isEligibleIndirectVirtualMethodCall(
- indirectlyInvokedMethod, invokeType, indirectSingleTarget)) {
- return null;
+ if (!nonEmptyUsage.getMethodCallsWithParameterAsReceiver().isEmpty()) {
+ DexMethod indirectlyInvokedMethod =
+ nonEmptyUsage.getMethodCallsWithParameterAsReceiver().iterator().next();
+ SingleResolutionResult indirectResolutionResult =
+ appView
+ .appInfo()
+ .resolveMethodOn(eligibleClass, indirectlyInvokedMethod)
+ .asSingleResolution();
+ if (indirectResolutionResult == null) {
+ return null;
+ }
+ ProgramMethod indirectSingleTarget =
+ indirectResolutionResult.getResolutionPair().asProgramMethod();
+ if (!isEligibleIndirectVirtualMethodCall(indirectlyInvokedMethod, indirectSingleTarget)) {
+ return null;
+ }
+ markSizeOfIndirectTargetForInlining(indirectSingleTarget);
+ indirectMethodCallsOnInstance.add(indirectSingleTarget);
}
- indirectMethodCallsOnInstance.add(indirectSingleTarget);
}
invoke.getNonReceiverArguments().forEach(receivers::addIllegalReceiverAlias);
@@ -1042,45 +1053,41 @@
return new InliningInfo(singleTarget, eligibleClass.type);
}
- private boolean isEligibleIndirectVirtualMethodCall(DexMethod invokedMethod, Type type) {
- ProgramMethod singleTarget =
- asProgramMethodOrNull(
- appView.appInfo().resolveMethodOn(eligibleClass, invokedMethod).getSingleTarget(),
- appView);
- return isEligibleIndirectVirtualMethodCall(invokedMethod, type, singleTarget);
- }
-
private boolean isEligibleIndirectVirtualMethodCall(
- DexMethod invokedMethod, Type type, ProgramMethod singleTarget) {
+ DexMethod invokedMethod, ProgramMethod singleTarget) {
if (!isEligibleSingleTarget(singleTarget)) {
return false;
}
if (singleTarget.getDefinition().isLibraryMethodOverride().isTrue()) {
return false;
}
- if (!isEligibleVirtualMethodCall(invokedMethod, singleTarget, type)) {
+ if (!isEligibleVirtualMethodCall(invokedMethod, singleTarget)) {
return false;
}
- ClassInlinerEligibilityInfo eligibility =
- singleTarget.getDefinition().getOptimizationInfo().getClassInlinerEligibility();
- if (!eligibility.callsReceiver.isEmpty() || eligibility.returnsReceiver.isPossiblyTrue()) {
- return false;
+
+ ParameterUsage usage =
+ singleTarget
+ .getDefinition()
+ .getOptimizationInfo()
+ .getClassInlinerMethodConstraint()
+ .getParameterUsage(0);
+ assert !usage.isTop();
+ if (usage.isBottom()) {
+ return true;
}
- markSizeOfIndirectTargetForInlining(singleTarget);
- return true;
+ NonEmptyParameterUsage nonEmptyUsage = usage.asNonEmpty();
+ return nonEmptyUsage.getMethodCallsWithParameterAsReceiver().isEmpty()
+ && !nonEmptyUsage.isParameterReturned();
}
- private boolean isEligibleVirtualMethodCall(
- DexMethod callee,
- ProgramMethod singleTarget,
- Type type) {
+ private boolean isEligibleVirtualMethodCall(DexMethod callee, ProgramMethod singleTarget) {
assert isEligibleSingleTarget(singleTarget);
// We should not inline a method if the invocation has type interface or virtual and the
// signature of the invocation resolves to a private or static method.
// TODO(b/147212189): Why not inline private methods? If access is permitted it is valid.
ResolutionResult resolutionResult =
- appView.appInfo().resolveMethod(callee, type == Type.INTERFACE);
+ appView.appInfo().resolveMethodOnClass(callee, eligibleClass);
if (resolutionResult.isSingleResolution()
&& !resolutionResult.getSingleTarget().isNonPrivateVirtualMethod()) {
return false;
@@ -1096,17 +1103,15 @@
MethodOptimizationInfo optimizationInfo = singleTarget.getDefinition().getOptimizationInfo();
ClassInlinerMethodConstraint classInlinerMethodConstraint =
optimizationInfo.getClassInlinerMethodConstraint();
+ int parameter = 0;
if (root.isNewInstance()) {
- if (!classInlinerMethodConstraint.isEligibleForNewInstanceClassInlining(singleTarget)) {
- return false;
- }
- } else {
- assert root.isStaticGet();
- if (!classInlinerMethodConstraint.isEligibleForStaticGetClassInlining(singleTarget)) {
- return false;
- }
+ return classInlinerMethodConstraint.isEligibleForNewInstanceClassInlining(
+ singleTarget, parameter);
}
- return true;
+
+ assert root.isStaticGet();
+ return classInlinerMethodConstraint.isEligibleForStaticGetClassInlining(
+ singleTarget, parameter);
}
private boolean isExtraMethodCall(InvokeMethod invoke) {
@@ -1151,8 +1156,6 @@
SingleResolutionResult resolutionResult,
ProgramMethod singleTarget,
Supplier<InliningOracle> defaultOracle) {
- List<Value> arguments = Lists.newArrayList(invoke.inValues());
-
// If we got here with invocation on receiver the user is ineligible.
if (invoke.isInvokeMethodWithReceiver()) {
InvokeMethodWithReceiver invokeMethodWithReceiver = invoke.asInvokeMethodWithReceiver();
@@ -1179,22 +1182,10 @@
}
// Go through all arguments, see if all usages of eligibleInstance are good.
- if (!isEligibleParameterUsages(invoke, arguments, singleTarget.getDefinition())) {
+ if (!isEligibleParameterUsages(invoke, singleTarget.getDefinition())) {
return false;
}
- MethodOptimizationInfo optimizationInfo = singleTarget.getDefinition().getOptimizationInfo();
- for (int argIndex = 0; argIndex < arguments.size(); argIndex++) {
- Value argument = arguments.get(argIndex).getAliasedValue();
- ParameterUsage parameterUsage = optimizationInfo.getParameterUsages(argIndex);
- if (receivers.isDefiniteReceiverAlias(argument)
- && parameterUsage != null
- && parameterUsage.notUsed()) {
- // Reference can be removed since it's not used.
- unusedArguments.add(new Pair<>(invoke, argIndex));
- }
- }
-
extraMethodCalls.put(invoke, new InliningInfo(singleTarget, null));
// Looks good.
@@ -1214,102 +1205,72 @@
return false;
}
- private boolean isEligibleParameterUsages(
- InvokeMethod invoke, List<Value> arguments, DexEncodedMethod singleTarget) {
+ private boolean isEligibleParameterUsages(InvokeMethod invoke, DexEncodedMethod singleTarget) {
// Go through all arguments, see if all usages of eligibleInstance are good.
- for (int argIndex = 0; argIndex < arguments.size(); argIndex++) {
+ for (int parameter = 0; parameter < invoke.arguments().size(); parameter++) {
MethodOptimizationInfo optimizationInfo = singleTarget.getOptimizationInfo();
- ParameterUsage parameterUsage = optimizationInfo.getParameterUsages(argIndex);
+ ParameterUsage usage =
+ optimizationInfo.getClassInlinerMethodConstraint().getParameterUsage(parameter);
- Value argument = arguments.get(argIndex);
+ Value argument = invoke.getArgument(parameter);
if (receivers.isReceiverAlias(argument)) {
// Have parameter usage info?
- if (!isEligibleParameterUsage(parameterUsage, invoke)) {
+ if (!isEligibleParameterUsage(usage, invoke)) {
return false;
}
} else {
// Nothing to worry about, unless `argument` becomes an alias of the receiver later.
receivers.addDeferredAliasValidityCheck(
- argument, () -> isEligibleParameterUsage(parameterUsage, invoke));
+ argument, () -> isEligibleParameterUsage(usage, invoke));
}
}
return true;
}
- private boolean isEligibleParameterUsage(ParameterUsage parameterUsage, InvokeMethod invoke) {
- if (parameterUsage == null) {
- return false; // Don't know anything.
- }
-
- if (parameterUsage.notUsed()) {
+ private boolean isEligibleParameterUsage(ParameterUsage usage, InvokeMethod invoke) {
+ if (usage.isBottom()) {
return true;
}
- if (parameterUsage.isAssignedToField) {
+ if (usage.isTop()) {
return false;
}
+ NonEmptyParameterUsage nonEmptyUsage = usage.asNonEmpty();
+
if (root.isStaticGet()) {
// If we are class inlining a singleton instance from a static-get, then we don't know
// the value of the fields, and we also can't optimize away instance-field assignments, as
// they have global side effects.
- if (parameterUsage.hasFieldAssignment || parameterUsage.hasFieldRead) {
+ if (nonEmptyUsage.isParameterMutated()
+ || usage.isParameterUsedAsLock()
+ || nonEmptyUsage.hasFieldsReadFromParameter()) {
return false;
}
}
- if (parameterUsage.isReturned) {
- if (invoke.outValue() != null && invoke.outValue().hasAnyUsers()) {
- // Used as return value which is not ignored.
- return false;
- }
- }
-
- if (parameterUsage.isUsedInMonitor) {
- return !root.isStaticGet();
- }
-
- if (!Sets.difference(parameterUsage.ifZeroTest, ALLOWED_ZERO_TEST_TYPES).isEmpty()) {
- // Used in unsupported zero-check-if kinds.
+ if (usage.isParameterReturned() && invoke.hasUsedOutValue()) {
+ // Used as return value which is not ignored.
return false;
}
- for (Pair<Type, DexMethod> call : parameterUsage.callsReceiver) {
- Type type = call.getFirst();
- DexMethod target = call.getSecond();
-
- if (type == Type.VIRTUAL || type == Type.INTERFACE) {
- // Is the method called indirectly still eligible?
- if (!isEligibleIndirectVirtualMethodCall(target, type)) {
- return false;
- }
- } else if (type == Type.DIRECT) {
- if (!isInstanceInitializerEligibleForClassInlining(target)) {
- // Only calls to trivial instance initializers are supported at this point.
- return false;
- }
- } else {
- // Static and super calls are not yet supported.
+ for (DexMethod invokedMethod : nonEmptyUsage.getMethodCallsWithParameterAsReceiver()) {
+ SingleResolutionResult resolutionResult =
+ appView.appInfo().resolveMethodOn(eligibleClass, invokedMethod).asSingleResolution();
+ if (resolutionResult == null || !resolutionResult.getResolvedHolder().isProgramClass()) {
return false;
}
+
+ // Is the method called indirectly still eligible?
+ ProgramMethod singleTarget = resolutionResult.getResolutionPair().asProgramMethod();
+ if (!isEligibleIndirectVirtualMethodCall(invokedMethod, singleTarget)) {
+ return false;
+ }
+ markSizeOfIndirectTargetForInlining(singleTarget);
}
return true;
}
- private boolean isInstanceInitializerEligibleForClassInlining(DexMethod method) {
- if (method == dexItemFactory.objectMembers.constructor) {
- return true;
- }
- DexProgramClass holder = asProgramClassOrNull(appView.definitionForHolder(method));
- DexEncodedMethod definition = method.lookupOnClass(holder);
- if (definition == null) {
- return false;
- }
- InstanceInitializerInfo initializerInfo =
- definition.getOptimizationInfo().getContextInsensitiveInstanceInitializerInfo();
- return initializerInfo.receiverNeverEscapesOutsideConstructorChain();
- }
-
private boolean exemptFromInstructionLimit(ProgramMethod inlinee) {
KotlinClassLevelInfo kotlinInfo = inlinee.getHolder().getKotlinInfo();
return kotlinInfo.isSyntheticClass() && kotlinInfo.asSyntheticClass().isLambda();
@@ -1361,5 +1322,10 @@
instruction.getBlock().removeInstruction(instruction);
}
- static class IllegalClassInlinerStateException extends Exception {}
+ static class IllegalClassInlinerStateException extends Exception {
+
+ IllegalClassInlinerStateException() {
+ assert false;
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/AnalysisState.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/AnalysisState.java
index 90b5ec8..a09ada2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/AnalysisState.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/AnalysisState.java
@@ -84,10 +84,21 @@
return BOTTOM;
}
- static AnalysisState create(Int2ObjectMap<ParameterUsagePerContext> backing) {
+ public static AnalysisState create(Int2ObjectMap<ParameterUsagePerContext> backing) {
return backing.isEmpty() ? bottom() : new AnalysisState(backing);
}
+ /**
+ * Converts instances of {@link InternalNonEmptyParameterUsage} to {@link NonEmptyParameterUsage}.
+ *
+ * <p>This is needed because {@link InternalNonEmptyParameterUsage} is not suitable for being
+ * stored in {@link com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo}, since it
+ * contains references to IR instructions.
+ */
+ AnalysisState externalize() {
+ return rebuildParameters((parameter, usagePerContext) -> usagePerContext.externalize());
+ }
+
AnalysisState put(int parameter, ParameterUsagePerContext parameterUsagePerContext) {
Int2ObjectOpenHashMap<ParameterUsagePerContext> newBacking =
new Int2ObjectOpenHashMap<>(backing);
@@ -95,7 +106,7 @@
return create(newBacking);
}
- void forEach(IntObjConsumer<ParameterUsagePerContext> consumer) {
+ public void forEach(IntObjConsumer<ParameterUsagePerContext> consumer) {
Int2ObjectMapUtils.forEach(backing, consumer);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/BottomParameterUsage.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/BottomParameterUsage.java
index 777c95e..930edc5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/BottomParameterUsage.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/BottomParameterUsage.java
@@ -30,6 +30,11 @@
}
@Override
+ ParameterUsage externalize() {
+ return this;
+ }
+
+ @Override
public boolean isBottom() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/BottomParameterUsagePerContext.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/BottomParameterUsagePerContext.java
index a4c6704..b535038 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/BottomParameterUsagePerContext.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/BottomParameterUsagePerContext.java
@@ -18,6 +18,11 @@
}
@Override
+ ParameterUsagePerContext externalize() {
+ return this;
+ }
+
+ @Override
public ParameterUsage get(AnalysisContext context) {
return ParameterUsage.bottom();
}
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
new file mode 100644
index 0000000..fee1048
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ClassInlinerMethodConstraintAnalysis.java
@@ -0,0 +1,36 @@
+// Copyright (c) 2021, 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.optimize.classinliner.analysis;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.framework.intraprocedural.DataflowAnalysisResult.SuccessfulDataflowAnalysisResult;
+import com.android.tools.r8.ir.analysis.framework.intraprocedural.IntraproceduralDataflowAnalysis;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
+import com.android.tools.r8.ir.optimize.classinliner.constraint.ConditionalClassInlinerMethodConstraint;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+
+public class ClassInlinerMethodConstraintAnalysis {
+
+ public static ClassInlinerMethodConstraint analyze(
+ AppView<AppInfoWithLiveness> appView, ProgramMethod method, IRCode code) {
+ if (method.getDefinition().isClassInitializer()) {
+ return ClassInlinerMethodConstraint.alwaysFalse();
+ }
+
+ // Analyze code.
+ IntraproceduralDataflowAnalysis<AnalysisState> analysis =
+ new IntraproceduralDataflowAnalysis<>(
+ AnalysisState.bottom(), new TransferFunction(appView, method));
+ SuccessfulDataflowAnalysisResult<AnalysisState> result =
+ analysis.run(code.entryBlock()).asSuccessfulAnalysisResult();
+ if (result == null) {
+ return ClassInlinerMethodConstraint.alwaysFalse();
+ }
+
+ return new ConditionalClassInlinerMethodConstraint(result.join().externalize());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/InternalNonEmptyParameterUsage.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/InternalNonEmptyParameterUsage.java
index fb21140..4bba181 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/InternalNonEmptyParameterUsage.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/InternalNonEmptyParameterUsage.java
@@ -5,8 +5,10 @@
package com.android.tools.r8.ir.optimize.classinliner.analysis;
import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
import com.android.tools.r8.utils.BooleanUtils;
+import com.google.common.collect.ImmutableMultiset;
import com.google.common.collect.ImmutableSet;
import java.util.Collection;
import java.util.Objects;
@@ -88,6 +90,20 @@
}
@Override
+ ParameterUsage externalize() {
+ ImmutableMultiset.Builder<DexMethod> methodCallsWithParameterAsReceiverBuilder =
+ ImmutableMultiset.builder();
+ methodCallsWithParameterAsReceiver.forEach(
+ invoke -> methodCallsWithParameterAsReceiverBuilder.add(invoke.getInvokedMethod()));
+ return new NonEmptyParameterUsage(
+ fieldsReadFromParameter,
+ methodCallsWithParameterAsReceiverBuilder.build(),
+ isParameterMutated,
+ isParameterReturned,
+ isParameterUsedAsLock);
+ }
+
+ @Override
public boolean isParameterMutated() {
return isParameterMutated;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsage.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsage.java
index ea57edc..d377305 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsage.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsage.java
@@ -55,6 +55,11 @@
return this;
}
+ @Override
+ ParameterUsage externalize() {
+ throw new Unreachable();
+ }
+
public boolean hasFieldsReadFromParameter() {
return !getFieldsReadFromParameter().isEmpty();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsagePerContext.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsagePerContext.java
index 1ea2d2d..e57fc5c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsagePerContext.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsagePerContext.java
@@ -54,6 +54,11 @@
}
@Override
+ ParameterUsagePerContext externalize() {
+ return rebuild((context, usage) -> usage.externalize());
+ }
+
+ @Override
public ParameterUsage get(AnalysisContext context) {
assert backing.containsKey(context);
return backing.get(context);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsage.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsage.java
index 423f039..4c5c2c2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsage.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsage.java
@@ -21,6 +21,8 @@
return null;
}
+ abstract ParameterUsage externalize();
+
/**
* Returns true if this is an instanceof {@link BottomParameterUsage}.
*
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsagePerContext.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsagePerContext.java
index b81d65b..f294ea2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsagePerContext.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsagePerContext.java
@@ -12,6 +12,8 @@
return null;
}
+ abstract ParameterUsagePerContext externalize();
+
/** Returns the usage information for this parameter in the given context. */
public abstract ParameterUsage get(AnalysisContext context);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/UnknownParameterUsage.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/UnknownParameterUsage.java
index 30f0c1e..93577ad 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/UnknownParameterUsage.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/UnknownParameterUsage.java
@@ -28,6 +28,11 @@
}
@Override
+ ParameterUsage externalize() {
+ return this;
+ }
+
+ @Override
public boolean isParameterMutated() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/UnknownParameterUsagePerContext.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/UnknownParameterUsagePerContext.java
index 7d5e6f1..4ead51d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/UnknownParameterUsagePerContext.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/UnknownParameterUsagePerContext.java
@@ -18,6 +18,11 @@
}
@Override
+ ParameterUsagePerContext externalize() {
+ return this;
+ }
+
+ @Override
public ParameterUsage get(AnalysisContext context) {
return ParameterUsage.top();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysFalseClassInlinerMethodConstraint.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysFalseClassInlinerMethodConstraint.java
index 0ac793a..0c6d335e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysFalseClassInlinerMethodConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysFalseClassInlinerMethodConstraint.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.optimize.classinliner.constraint;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.optimize.classinliner.analysis.ParameterUsage;
public class AlwaysFalseClassInlinerMethodConstraint implements ClassInlinerMethodConstraint {
@@ -18,12 +19,22 @@
}
@Override
- public boolean isEligibleForNewInstanceClassInlining(ProgramMethod method) {
+ public ClassInlinerMethodConstraint fixupAfterRemovingThisParameter() {
+ return this;
+ }
+
+ @Override
+ public ParameterUsage getParameterUsage(int parameter) {
+ return ParameterUsage.top();
+ }
+
+ @Override
+ public boolean isEligibleForNewInstanceClassInlining(ProgramMethod method, int parameter) {
return false;
}
@Override
- public boolean isEligibleForStaticGetClassInlining(ProgramMethod method) {
+ public boolean isEligibleForStaticGetClassInlining(ProgramMethod method, int parameter) {
return false;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysTrueClassInlinerMethodConstraint.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysTrueClassInlinerMethodConstraint.java
deleted file mode 100644
index 56e1226..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysTrueClassInlinerMethodConstraint.java
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 2021, 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.optimize.classinliner.constraint;
-
-import com.android.tools.r8.graph.ProgramMethod;
-
-public class AlwaysTrueClassInlinerMethodConstraint implements ClassInlinerMethodConstraint {
-
- private static final AlwaysTrueClassInlinerMethodConstraint INSTANCE =
- new AlwaysTrueClassInlinerMethodConstraint();
-
- private AlwaysTrueClassInlinerMethodConstraint() {}
-
- static AlwaysTrueClassInlinerMethodConstraint getInstance() {
- return INSTANCE;
- }
-
- @Override
- public boolean isEligibleForNewInstanceClassInlining(ProgramMethod method) {
- return true;
- }
-
- @Override
- public boolean isEligibleForStaticGetClassInlining(ProgramMethod method) {
- return true;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraint.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraint.java
index 640c4c7..c86f373 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraint.java
@@ -5,22 +5,19 @@
package com.android.tools.r8.ir.optimize.classinliner.constraint;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.optimize.classinliner.analysis.ParameterUsage;
public interface ClassInlinerMethodConstraint {
- boolean isEligibleForNewInstanceClassInlining(ProgramMethod method);
+ ClassInlinerMethodConstraint fixupAfterRemovingThisParameter();
- boolean isEligibleForStaticGetClassInlining(ProgramMethod method);
+ ParameterUsage getParameterUsage(int parameter);
+
+ boolean isEligibleForNewInstanceClassInlining(ProgramMethod method, int parameter);
+
+ boolean isEligibleForStaticGetClassInlining(ProgramMethod method, int parameter);
static AlwaysFalseClassInlinerMethodConstraint alwaysFalse() {
return AlwaysFalseClassInlinerMethodConstraint.getInstance();
}
-
- static AlwaysTrueClassInlinerMethodConstraint alwaysTrue() {
- return AlwaysTrueClassInlinerMethodConstraint.getInstance();
- }
-
- static OnlyNewInstanceClassInlinerMethodConstraint onlyNewInstanceClassInlining() {
- return OnlyNewInstanceClassInlinerMethodConstraint.getInstance();
- }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraintAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraintAnalysis.java
deleted file mode 100644
index d36a76c..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraintAnalysis.java
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (c) 2021, 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.optimize.classinliner.constraint;
-
-import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
-import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo;
-import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
-
-public class ClassInlinerMethodConstraintAnalysis {
-
- public static ClassInlinerMethodConstraint analyze(
- ClassInlinerEligibilityInfo classInlinerEligibilityInfo,
- ParameterUsagesInfo parameterUsagesInfo) {
- boolean isEligibleForNewInstanceClassInlining =
- isEligibleForNewInstanceClassInlining(classInlinerEligibilityInfo);
- boolean isEligibleForStaticGetClassInlining =
- isEligibleForStaticGetClassInlining(classInlinerEligibilityInfo, parameterUsagesInfo);
- if (isEligibleForNewInstanceClassInlining) {
- if (isEligibleForStaticGetClassInlining) {
- return ClassInlinerMethodConstraint.alwaysTrue();
- }
- return ClassInlinerMethodConstraint.onlyNewInstanceClassInlining();
- }
- assert !isEligibleForStaticGetClassInlining;
- return ClassInlinerMethodConstraint.alwaysFalse();
- }
-
- private static boolean isEligibleForNewInstanceClassInlining(
- ClassInlinerEligibilityInfo classInlinerEligibilityInfo) {
- return classInlinerEligibilityInfo != null;
- }
-
- private static boolean isEligibleForStaticGetClassInlining(
- ClassInlinerEligibilityInfo classInlinerEligibilityInfo,
- ParameterUsagesInfo parameterUsagesInfo) {
- if (classInlinerEligibilityInfo == null || parameterUsagesInfo == null) {
- return false;
- }
- if (classInlinerEligibilityInfo.hasMonitorOnReceiver) {
- // We will not be able to remove the monitor instruction afterwards.
- return false;
- }
- if (classInlinerEligibilityInfo.modifiesInstanceFields) {
- // The static instance could be accessed from elsewhere. Therefore, we cannot allow
- // side-effects to be removed and therefore cannot class inline method calls that modifies the
- // instance.
- return false;
- }
- ParameterUsage receiverUsage = parameterUsagesInfo.getParameterUsage(0);
- if (receiverUsage == null) {
- return false;
- }
- if (receiverUsage.hasFieldRead) {
- // We don't know the value of the field.
- return false;
- }
- return true;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ConditionalClassInlinerMethodConstraint.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ConditionalClassInlinerMethodConstraint.java
new file mode 100644
index 0000000..5c384e8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ConditionalClassInlinerMethodConstraint.java
@@ -0,0 +1,81 @@
+// Copyright (c) 2021, 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.optimize.classinliner.constraint;
+
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.optimize.classinliner.analysis.AnalysisContext;
+import com.android.tools.r8.ir.optimize.classinliner.analysis.AnalysisState;
+import com.android.tools.r8.ir.optimize.classinliner.analysis.NonEmptyParameterUsage;
+import com.android.tools.r8.ir.optimize.classinliner.analysis.ParameterUsage;
+import com.android.tools.r8.ir.optimize.classinliner.analysis.ParameterUsagePerContext;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
+
+public class ConditionalClassInlinerMethodConstraint implements ClassInlinerMethodConstraint {
+
+ private final AnalysisState usages;
+
+ public ConditionalClassInlinerMethodConstraint(AnalysisState usages) {
+ this.usages = usages;
+ }
+
+ @Override
+ public ClassInlinerMethodConstraint fixupAfterRemovingThisParameter() {
+ // TODO(b/181746071): Introduce a 'TOP' variant to reduce memory usage and add check here.
+ if (usages.isBottom()) {
+ return this;
+ }
+ Int2ObjectMap<ParameterUsagePerContext> backing = new Int2ObjectOpenHashMap<>();
+ usages.forEach(
+ (parameter, usagePerContext) -> {
+ if (parameter > 0) {
+ backing.put(parameter - 1, usagePerContext);
+ }
+ });
+ return new ConditionalClassInlinerMethodConstraint(AnalysisState.create(backing));
+ }
+
+ @Override
+ public ParameterUsage getParameterUsage(int parameter) {
+ AnalysisContext defaultContext = AnalysisContext.getDefaultContext();
+ return usages.get(parameter).get(defaultContext);
+ }
+
+ @Override
+ public boolean isEligibleForNewInstanceClassInlining(ProgramMethod method, int parameter) {
+ AnalysisContext defaultContext = AnalysisContext.getDefaultContext();
+ ParameterUsage usage = usages.get(parameter).get(defaultContext);
+ return !usage.isTop();
+ }
+
+ @Override
+ public boolean isEligibleForStaticGetClassInlining(ProgramMethod method, int parameter) {
+ AnalysisContext defaultContext = AnalysisContext.getDefaultContext();
+ ParameterUsage usage = usages.get(parameter).get(defaultContext);
+ if (usage.isBottom()) {
+ return true;
+ }
+ if (usage.isTop()) {
+ return false;
+ }
+
+ NonEmptyParameterUsage knownUsage = usage.asNonEmpty();
+ if (knownUsage.isParameterMutated()) {
+ // The static instance could be accessed from elsewhere. Therefore, we cannot allow
+ // side-effects to be removed and therefore cannot class inline method calls that modifies the
+ // instance.
+ return false;
+ }
+ if (knownUsage.isParameterUsedAsLock()) {
+ // We will not be able to remove the monitor instruction afterwards.
+ return false;
+ }
+ if (!knownUsage.getFieldsReadFromParameter().isEmpty()) {
+ // We don't know the value of the field.
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/OnlyNewInstanceClassInlinerMethodConstraint.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/OnlyNewInstanceClassInlinerMethodConstraint.java
deleted file mode 100644
index 9bb1702..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/OnlyNewInstanceClassInlinerMethodConstraint.java
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 2021, 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.optimize.classinliner.constraint;
-
-import com.android.tools.r8.graph.ProgramMethod;
-
-public class OnlyNewInstanceClassInlinerMethodConstraint implements ClassInlinerMethodConstraint {
-
- private static final OnlyNewInstanceClassInlinerMethodConstraint INSTANCE =
- new OnlyNewInstanceClassInlinerMethodConstraint();
-
- private OnlyNewInstanceClassInlinerMethodConstraint() {}
-
- static OnlyNewInstanceClassInlinerMethodConstraint getInstance() {
- return INSTANCE;
- }
-
- @Override
- public boolean isEligibleForNewInstanceClassInlining(ProgramMethod method) {
- return true;
- }
-
- @Override
- public boolean isEligibleForStaticGetClassInlining(ProgramMethod method) {
- return false;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
index ded60a3..78c9f46 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
@@ -15,7 +15,6 @@
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
@@ -94,11 +93,6 @@
public boolean hasUsefulOptimizationInfo(AppView<?> appView, DexEncodedMethod method) {
TypeElement[] staticTypes = getStaticTypes(appView, method);
for (int i = 0; i < size; i++) {
- ParameterUsage parameterUsage = method.getOptimizationInfo().getParameterUsages(i);
- // If the parameter is not used, passing accurate argument info doesn't matter.
- if (parameterUsage != null && parameterUsage.notUsed()) {
- continue;
- }
AbstractValue abstractValue = getAbstractArgumentValue(i);
if (abstractValue.isNonTrivial()) {
assert appView.options().callSiteOptimizationOptions().isConstantPropagationEnabled();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
index 46b5a70..d49189f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
@@ -12,9 +12,7 @@
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
import com.android.tools.r8.ir.code.InvokeDirect;
-import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
-import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
import com.android.tools.r8.ir.optimize.info.initializer.DefaultInstanceInitializerInfo;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
@@ -35,9 +33,7 @@
static ClassTypeElement UNKNOWN_CLASS_TYPE = null;
static boolean UNKNOWN_CHECKS_NULL_RECEIVER_BEFORE_ANY_SIDE_EFFECT = false;
static boolean UNKNOWN_TRIGGERS_CLASS_INIT_BEFORE_ANY_SIDE_EFFECT = false;
- static ClassInlinerEligibilityInfo UNKNOWN_CLASS_INLINER_ELIGIBILITY = null;
static boolean UNKNOWN_INITIALIZER_ENABLING_JAVA_ASSERTIONS = false;
- static ParameterUsagesInfo UNKNOWN_PARAMETER_USAGE_INFO = null;
static boolean UNKNOWN_MAY_HAVE_SIDE_EFFECTS = true;
static boolean UNKNOWN_RETURN_VALUE_ONLY_DEPENDS_ON_ARGUMENTS = false;
static BitSet NO_NULL_PARAMETER_OR_THROW_FACTS = null;
@@ -105,12 +101,6 @@
}
@Override
- public ParameterUsage getParameterUsages(int parameter) {
- assert UNKNOWN_PARAMETER_USAGE_INFO == null;
- return null;
- }
-
- @Override
public BitSet getNonNullParamOrThrow() {
return NO_NULL_PARAMETER_OR_THROW_FACTS;
}
@@ -152,11 +142,6 @@
}
@Override
- public ClassInlinerEligibilityInfo getClassInlinerEligibility() {
- return UNKNOWN_CLASS_INLINER_ELIGIBILITY;
- }
-
- @Override
public AbstractValue getAbstractReturnValue() {
return UNKNOWN_ABSTRACT_RETURN_VALUE;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
index 7cced9d..bdc35f3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
@@ -10,9 +10,7 @@
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.code.InvokeDirect;
-import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
-import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
import java.util.BitSet;
@@ -47,8 +45,6 @@
public abstract ClassTypeElement getDynamicLowerBoundType();
- public abstract ParameterUsage getParameterUsages(int parameter);
-
public final boolean hasNonNullParamOrThrow() {
return getNonNullParamOrThrow() != null;
}
@@ -73,8 +69,6 @@
public abstract BridgeInfo getBridgeInfo();
- public abstract ClassInlinerEligibilityInfo getClassInlinerEligibility();
-
public abstract Set<DexType> getInitializedClassesOnNormalExit();
public abstract InstanceInitializerInfo getContextInsensitiveInstanceInitializerInfo();
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 804b6b3..aa28923 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
@@ -29,7 +29,6 @@
import static com.android.tools.r8.ir.code.Opcodes.INVOKE_NEW_ARRAY;
import static com.android.tools.r8.ir.code.Opcodes.INVOKE_STATIC;
import static com.android.tools.r8.ir.code.Opcodes.INVOKE_VIRTUAL;
-import static com.android.tools.r8.ir.code.Opcodes.MONITOR;
import static com.android.tools.r8.ir.code.Opcodes.MUL;
import static com.android.tools.r8.ir.code.Opcodes.NEW_ARRAY_EMPTY;
import static com.android.tools.r8.ir.code.Opcodes.NEW_INSTANCE;
@@ -47,10 +46,8 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
@@ -77,23 +74,17 @@
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.InvokeStatic;
-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;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.optimize.DynamicTypeOptimization;
-import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
-import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerReceiverAnalysis;
+import com.android.tools.r8.ir.optimize.classinliner.analysis.ClassInlinerMethodConstraintAnalysis;
import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
-import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraintAnalysis;
-import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
-import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsageBuilder;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeAnalyzer;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
@@ -103,19 +94,14 @@
import com.android.tools.r8.kotlin.Kotlin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.ListUtils;
-import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.Timing;
-import com.android.tools.r8.utils.WorkList;
import com.google.common.collect.Sets;
import java.util.ArrayDeque;
-import java.util.ArrayList;
import java.util.BitSet;
import java.util.Deque;
import java.util.List;
import java.util.Set;
import java.util.function.BiFunction;
-import java.util.function.Predicate;
public class MethodOptimizationInfoCollector {
@@ -145,16 +131,11 @@
Timing timing) {
DexEncodedMethod definition = method.getDefinition();
identifyBridgeInfo(definition, code, feedback, timing);
- ClassInlinerEligibilityInfo classInlinerEligibilityInfo =
- identifyClassInlinerEligibility(code, feedback, timing);
- ParameterUsagesInfo parameterUsagesInfo =
- identifyParameterUsages(definition, code, feedback, timing);
analyzeReturns(code, feedback, timing);
if (options.enableInlining) {
identifyInvokeSemanticsForInlining(definition, code, feedback, timing);
}
- computeClassInlinerMethodConstraint(
- method, code, feedback, classInlinerEligibilityInfo, parameterUsagesInfo, timing);
+ computeClassInlinerMethodConstraint(method, code, feedback, timing);
computeSimpleInliningConstraint(method, code, feedback, timing);
computeDynamicReturnType(dynamicTypeOptimization, feedback, definition, code, timing);
computeInitializedClassesOnNormalExit(feedback, definition, code, timing);
@@ -173,201 +154,6 @@
timing.end();
}
- private ClassInlinerEligibilityInfo identifyClassInlinerEligibility(
- IRCode code, OptimizationFeedback feedback, Timing timing) {
- timing.begin("Identify class inliner eligibility");
- ClassInlinerEligibilityInfo classInlinerEligibilityInfo =
- identifyClassInlinerEligibility(code, feedback);
- timing.end();
- return classInlinerEligibilityInfo;
- }
-
- private ClassInlinerEligibilityInfo identifyClassInlinerEligibility(
- IRCode code, OptimizationFeedback feedback) {
- // Method eligibility is calculated in similar way for regular method
- // and for the constructor. To be eligible method should only be using its
- // receiver in the following ways:
- //
- // (1) as a receiver of reads/writes of instance fields of the holder class,
- // (2) as a return value,
- // (3) as a receiver of a call to the superclass initializer. Note that we don't
- // check what is passed to superclass initializer as arguments, only check
- // that it is not the instance being initialized,
- // (4) as argument to a monitor instruction.
- //
- // Note that (4) can safely be removed as the receiver is guaranteed not to escape when we class
- // inline it, and hence any monitor instructions are no-ops.
- ProgramMethod context = code.context();
- DexEncodedMethod definition = context.getDefinition();
- boolean instanceInitializer = definition.isInstanceInitializer();
- if (definition.isNative()
- || (!definition.isNonAbstractVirtualMethod() && !instanceInitializer)) {
- return null;
- }
-
- feedback.setClassInlinerEligibility(definition, null); // To allow returns below.
-
- Value receiver = code.getThis();
- if (receiver.numberOfPhiUsers() > 0) {
- return null;
- }
-
- List<Pair<Invoke.Type, DexMethod>> callsReceiver = new ArrayList<>();
- boolean seenSuperInitCall = false;
- boolean seenMonitor = false;
- boolean modifiesInstanceFields = false;
-
- AliasedValueConfiguration configuration =
- AssumeAndCheckCastAliasedValueConfiguration.getInstance();
- Predicate<Value> isReceiverAlias = value -> value.getAliasedValue(configuration) == receiver;
- for (Instruction insn : receiver.aliasedUsers(configuration)) {
- switch (insn.opcode()) {
- case ASSUME:
- case CHECK_CAST:
- case RETURN:
- break;
-
- case MONITOR:
- seenMonitor = true;
- break;
-
- case INSTANCE_GET:
- case INSTANCE_PUT:
- {
- if (insn.isInstancePut()) {
- InstancePut instancePutInstruction = insn.asInstancePut();
- // Only allow field writes to the receiver.
- if (!isReceiverAlias.test(instancePutInstruction.object())) {
- return null;
- }
- // Do not allow the receiver to escape via a field write.
- if (isReceiverAlias.test(instancePutInstruction.value())) {
- return null;
- }
- modifiesInstanceFields = true;
- }
- DexField field = insn.asFieldInstruction().getField();
- if (appView.appInfo().resolveField(field).isFailedOrUnknownResolution()) {
- return null;
- }
- break;
- }
-
- case INVOKE_DIRECT:
- {
- InvokeDirect invoke = insn.asInvokeDirect();
- DexMethod invokedMethod = invoke.getInvokedMethod();
- if (dexItemFactory.isConstructor(invokedMethod)
- && invokedMethod.holder == context.getHolder().superType
- && ListUtils.lastIndexMatching(invoke.arguments(), isReceiverAlias) == 0
- && !seenSuperInitCall
- && instanceInitializer) {
- seenSuperInitCall = true;
- break;
- }
- // We don't support other direct calls yet.
- return null;
- }
-
- case INVOKE_STATIC:
- {
- InvokeStatic invoke = insn.asInvokeStatic();
- DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, context);
- if (singleTarget == null) {
- return null; // Not allowed.
- }
- if (singleTarget.getReference() == dexItemFactory.objectsMethods.requireNonNull) {
- if (!invoke.hasOutValue() || !invoke.outValue().hasAnyUsers()) {
- continue;
- }
- }
- return null;
- }
-
- case INVOKE_VIRTUAL:
- {
- InvokeVirtual invoke = insn.asInvokeVirtual();
- if (ListUtils.lastIndexMatching(invoke.arguments(), isReceiverAlias) != 0) {
- return null; // Not allowed.
- }
- DexMethod invokedMethod = invoke.getInvokedMethod();
- DexType returnType = invokedMethod.proto.returnType;
- if (returnType.isClassType()
- && appView.appInfo().inSameHierarchy(returnType, context.getHolderType())) {
- return null; // Not allowed, could introduce an alias of the receiver.
- }
- callsReceiver.add(new Pair<>(Invoke.Type.VIRTUAL, invokedMethod));
- }
- break;
-
- default:
- // Other receiver usages make the method not eligible.
- return null;
- }
- }
-
- if (instanceInitializer && !seenSuperInitCall) {
- // Call to super constructor not found?
- return null;
- }
-
- boolean synchronizedVirtualMethod = definition.isSynchronized() && definition.isVirtualMethod();
- ClassInlinerEligibilityInfo classInlinerEligibilityInfo =
- new ClassInlinerEligibilityInfo(
- callsReceiver,
- new ClassInlinerReceiverAnalysis(appView, definition, code).computeReturnsReceiver(),
- seenMonitor || synchronizedVirtualMethod,
- modifiesInstanceFields);
- feedback.setClassInlinerEligibility(definition, classInlinerEligibilityInfo);
- return classInlinerEligibilityInfo;
- }
-
- private ParameterUsagesInfo identifyParameterUsages(
- DexEncodedMethod method, IRCode code, OptimizationFeedback feedback, Timing timing) {
- timing.begin("Identify parameter usages");
- ParameterUsagesInfo parameterUsagesInfo = identifyParameterUsages(method, code, feedback);
- timing.end();
- return parameterUsagesInfo;
- }
-
- private ParameterUsagesInfo identifyParameterUsages(
- DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) {
- List<ParameterUsage> usages = new ArrayList<>();
- List<Value> values = code.collectArguments();
- for (int i = 0; i < values.size(); i++) {
- Value value = values.get(i);
- ParameterUsage usage = collectParameterUsages(i, value);
- if (usage != null) {
- usages.add(usage);
- }
- }
- ParameterUsagesInfo parameterUsagesInfo =
- !usages.isEmpty() ? new ParameterUsagesInfo(usages) : null;
- feedback.setParameterUsages(method, parameterUsagesInfo);
- return parameterUsagesInfo;
- }
-
- private ParameterUsage collectParameterUsages(int i, Value root) {
- ParameterUsageBuilder builder = new ParameterUsageBuilder(root, i, dexItemFactory);
- WorkList<Value> worklist = WorkList.newIdentityWorkList();
- worklist.addIfNotSeen(root);
- while (worklist.hasNext()) {
- Value value = worklist.next();
- if (value.hasPhiUsers()) {
- return null;
- }
- for (Instruction user : value.uniqueUsers()) {
- if (!builder.note(user)) {
- return null;
- }
- if (user.isAssume()) {
- worklist.addIfNotSeen(user.outValue());
- }
- }
- }
- return builder.build();
- }
-
private void analyzeReturns(IRCode code, OptimizationFeedback feedback, Timing timing) {
timing.begin("Identify returns argument");
analyzeReturns(code, feedback);
@@ -982,24 +768,16 @@
ProgramMethod method,
IRCode code,
OptimizationFeedback feedback,
- ClassInlinerEligibilityInfo classInlinerEligibilityInfo,
- ParameterUsagesInfo parameterUsagesInfo,
Timing timing) {
timing.begin("Compute class inlining constraint");
- computeClassInlinerMethodConstraint(
- method, code, feedback, classInlinerEligibilityInfo, parameterUsagesInfo);
+ computeClassInlinerMethodConstraint(method, code, feedback);
timing.end();
}
private void computeClassInlinerMethodConstraint(
- ProgramMethod method,
- IRCode code,
- OptimizationFeedback feedback,
- ClassInlinerEligibilityInfo classInlinerEligibilityInfo,
- ParameterUsagesInfo parameterUsagesInfo) {
+ ProgramMethod method, IRCode code, OptimizationFeedback feedback) {
ClassInlinerMethodConstraint classInlinerMethodConstraint =
- ClassInlinerMethodConstraintAnalysis.analyze(
- classInlinerEligibilityInfo, parameterUsagesInfo);
+ ClassInlinerMethodConstraintAnalysis.analyze(appView, method, code);
feedback.setClassInlinerMethodConstraint(method, classInlinerMethodConstraint);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
index 39f725d..df06045 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
@@ -14,7 +14,6 @@
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
-import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
@@ -263,12 +262,6 @@
}
@Override
- public synchronized void setClassInlinerEligibility(
- DexEncodedMethod method, ClassInlinerEligibilityInfo eligibility) {
- getMethodOptimizationInfoForUpdating(method).setClassInlinerEligibility(eligibility);
- }
-
- @Override
public synchronized void setInstanceInitializerInfoCollection(
DexEncodedMethod method,
InstanceInitializerInfoCollection instanceInitializerInfoCollection) {
@@ -282,12 +275,6 @@
}
@Override
- public synchronized void setParameterUsages(
- DexEncodedMethod method, ParameterUsagesInfo parameterUsagesInfo) {
- getMethodOptimizationInfoForUpdating(method).setParameterUsages(parameterUsagesInfo);
- }
-
- @Override
public synchronized void setNonNullParamOrThrow(DexEncodedMethod method, BitSet facts) {
getMethodOptimizationInfoForUpdating(method).setNonNullParamOrThrow(facts);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
index 3b46f4e..e862824 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
@@ -14,7 +14,6 @@
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
-import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
@@ -118,10 +117,6 @@
ProgramMethod method, ClassInlinerMethodConstraint classInlinerConstraint) {}
@Override
- public void setClassInlinerEligibility(
- DexEncodedMethod method, ClassInlinerEligibilityInfo eligibility) {}
-
- @Override
public void setInstanceInitializerInfoCollection(
DexEncodedMethod method,
InstanceInitializerInfoCollection instanceInitializerInfoCollection) {}
@@ -130,10 +125,6 @@
public void setInitializerEnablingJavaVmAssertions(DexEncodedMethod method) {}
@Override
- public void setParameterUsages(DexEncodedMethod method, ParameterUsagesInfo parameterUsagesInfo) {
- }
-
- @Override
public void setNonNullParamOrThrow(DexEncodedMethod method, BitSet facts) {}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
index 2624486..3fb77d9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -14,7 +14,6 @@
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
-import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
@@ -170,12 +169,6 @@
}
@Override
- public void setClassInlinerEligibility(
- DexEncodedMethod method, ClassInlinerEligibilityInfo eligibility) {
- // Ignored.
- }
-
- @Override
public void setInstanceInitializerInfoCollection(
DexEncodedMethod method,
InstanceInitializerInfoCollection instanceInitializerInfoCollection) {
@@ -190,11 +183,6 @@
}
@Override
- public void setParameterUsages(DexEncodedMethod method, ParameterUsagesInfo parameterUsagesInfo) {
- // Ignored.
- }
-
- @Override
public void setNonNullParamOrThrow(DexEncodedMethod method, BitSet facts) {
method.getMutableOptimizationInfo().setNonNullParamOrThrow(facts);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/ParameterUsagesInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/ParameterUsagesInfo.java
deleted file mode 100644
index 4389193..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/ParameterUsagesInfo.java
+++ /dev/null
@@ -1,273 +0,0 @@
-// 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.ir.optimize.info;
-
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.ir.code.If;
-import com.android.tools.r8.ir.code.If.Type;
-import com.android.tools.r8.ir.code.InstanceGet;
-import com.android.tools.r8.ir.code.InstancePut;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.Invoke;
-import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
-import com.android.tools.r8.ir.code.InvokeStatic;
-import com.android.tools.r8.ir.code.Monitor;
-import com.android.tools.r8.ir.code.Return;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.utils.ListUtils;
-import com.android.tools.r8.utils.Pair;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-public final class ParameterUsagesInfo {
-
- private ImmutableList<ParameterUsage> parametersUsages;
-
- public ParameterUsagesInfo(List<ParameterUsage> usages) {
- assert !usages.isEmpty();
- parametersUsages = ImmutableList.copyOf(usages);
- assert parametersUsages.size() ==
- parametersUsages.stream().map(usage -> usage.index).collect(Collectors.toSet()).size();
- }
-
- public ParameterUsage getParameterUsage(int index) {
- for (ParameterUsage usage : parametersUsages) {
- if (usage.index == index) {
- return usage;
- }
- }
- return null;
- }
-
- ParameterUsagesInfo remove(int index) {
- assert parametersUsages.size() > 0;
- assert 0 <= index && index <= ListUtils.last(parametersUsages).index;
- ImmutableList.Builder<ParameterUsage> builder = ImmutableList.builder();
- for (ParameterUsage usage : parametersUsages) {
- // Once we remove or pass the designated index, copy-n-shift the remaining usages.
- if (index < usage.index) {
- builder.add(ParameterUsage.copyAndShift(usage, 1));
- } else if (index == usage.index) {
- // Do not add the parameter usage with the matched index.
- } else {
- // Until we meet the `parameter` of interest, keep copying.
- assert usage.index < index;
- builder.add(usage);
- }
- }
- ImmutableList<ParameterUsage> adjustedUsages = builder.build();
- if (adjustedUsages.isEmpty()) {
- return DefaultMethodOptimizationInfo.UNKNOWN_PARAMETER_USAGE_INFO;
- }
- return new ParameterUsagesInfo(adjustedUsages);
- }
-
- public final static class ParameterUsage {
-
- public final int index;
- public final Set<Type> ifZeroTest;
- public final List<Pair<Invoke.Type, DexMethod>> callsReceiver;
-
- // If a field of this argument is assigned: arg.f = x.
- public final boolean hasFieldAssignment;
-
- // If a field of this argument is read: x = arg.f.
- public final boolean hasFieldRead;
-
- // If this argument is assigned to a field: x.f = arg.
- public final boolean isAssignedToField;
-
- // If this argument is returned: return arg.
- public final boolean isReturned;
-
- // If this argument is used in a monitor instruction.
- public final boolean isUsedInMonitor;
-
- ParameterUsage(
- int index,
- Set<Type> ifZeroTest,
- List<Pair<Invoke.Type, DexMethod>> callsReceiver,
- boolean hasFieldAssignment,
- boolean hasFieldRead,
- boolean isAssignedToField,
- boolean isReturned,
- boolean isUsedInMonitor) {
- this.index = index;
- this.ifZeroTest =
- ifZeroTest.isEmpty() ? Collections.emptySet() : ImmutableSet.copyOf(ifZeroTest);
- this.callsReceiver =
- callsReceiver.isEmpty() ? Collections.emptyList() : ImmutableList.copyOf(callsReceiver);
- this.hasFieldAssignment = hasFieldAssignment;
- this.hasFieldRead = hasFieldRead;
- this.isAssignedToField = isAssignedToField;
- this.isReturned = isReturned;
- this.isUsedInMonitor = isUsedInMonitor;
- }
-
- static ParameterUsage copyAndShift(ParameterUsage original, int shift) {
- assert original.index >= shift;
- return new ParameterUsage(
- original.index - shift,
- original.ifZeroTest,
- original.callsReceiver,
- original.hasFieldAssignment,
- original.hasFieldRead,
- original.isAssignedToField,
- original.isReturned,
- original.isUsedInMonitor);
- }
-
- public boolean notUsed() {
- return (ifZeroTest == null || ifZeroTest.isEmpty())
- && (callsReceiver == null || callsReceiver.isEmpty())
- && !hasFieldAssignment
- && !hasFieldRead
- && !isAssignedToField
- && !isReturned
- && !isUsedInMonitor;
- }
- }
-
- public static class ParameterUsageBuilder {
-
- private final int index;
- private final Value arg;
- private final DexItemFactory dexItemFactory;
-
- private final Set<Type> ifZeroTestTypes = new HashSet<>();
- private final List<Pair<Invoke.Type, DexMethod>> callsOnReceiver = new ArrayList<>();
-
- private boolean hasFieldAssignment = false;
- private boolean hasFieldRead = false;
- private boolean isAssignedToField = false;
- private boolean isReturned = false;
- private boolean isUsedInMonitor = false;
-
- ParameterUsageBuilder(Value arg, int index, DexItemFactory dexItemFactory) {
- this.arg = arg;
- this.index = index;
- this.dexItemFactory = dexItemFactory;
- }
-
- // Returns false if the instruction is not supported.
- public boolean note(Instruction instruction) {
- if (instruction.isAssume()) {
- // Keep examining other users if there are no phi users, but the param usage builder should
- // consider aliased users.
- return !instruction.outValue().hasPhiUsers();
- }
- if (instruction.isIf()) {
- return note(instruction.asIf());
- }
- if (instruction.isInstanceGet()) {
- return note(instruction.asInstanceGet());
- }
- if (instruction.isInstancePut()) {
- return note(instruction.asInstancePut());
- }
- if (instruction.isInvokeMethodWithReceiver()) {
- return note(instruction.asInvokeMethodWithReceiver());
- }
- if (instruction.isInvokeStatic()) {
- return note(instruction.asInvokeStatic());
- }
- if (instruction.isReturn()) {
- return note(instruction.asReturn());
- }
- if (instruction.isMonitor()) {
- return note(instruction.asMonitor());
- }
- return false;
- }
-
- public ParameterUsage build() {
- return new ParameterUsage(
- index,
- ifZeroTestTypes,
- callsOnReceiver,
- hasFieldAssignment,
- hasFieldRead,
- isAssignedToField,
- isReturned,
- isUsedInMonitor);
- }
-
- private boolean note(If ifInstruction) {
- if (ifInstruction.asIf().isZeroTest()) {
- assert ifInstruction.inValues().size() == 1
- && ifInstruction.inValues().get(0).getAliasedValue() == arg;
- ifZeroTestTypes.add(ifInstruction.asIf().getType());
- return true;
- }
- return false;
- }
-
- private boolean note(InstanceGet instanceGetInstruction) {
- assert arg != instanceGetInstruction.outValue();
- if (instanceGetInstruction.object().getAliasedValue() == arg) {
- hasFieldRead = true;
- return true;
- }
- return false;
- }
-
- private boolean note(InstancePut instancePutInstruction) {
- assert arg != instancePutInstruction.outValue();
- if (instancePutInstruction.object().getAliasedValue() == arg) {
- hasFieldAssignment = true;
- isAssignedToField |= instancePutInstruction.value().getAliasedValue() == arg;
- return true;
- }
- if (instancePutInstruction.value().getAliasedValue() == arg) {
- isAssignedToField = true;
- return true;
- }
- return false;
- }
-
- private boolean note(InvokeMethodWithReceiver invokeInstruction) {
- if (ListUtils.lastIndexMatching(
- invokeInstruction.inValues(), v -> v.getAliasedValue() == arg) == 0) {
- callsOnReceiver.add(
- new Pair<>(
- invokeInstruction.asInvokeMethodWithReceiver().getType(),
- invokeInstruction.asInvokeMethodWithReceiver().getInvokedMethod()));
- return true;
- }
- return false;
- }
-
- private boolean note(InvokeStatic invokeInstruction) {
- if (invokeInstruction.getInvokedMethod() == dexItemFactory.objectsMethods.requireNonNull) {
- if (!invokeInstruction.hasOutValue() || !invokeInstruction.outValue().hasAnyUsers()) {
- ifZeroTestTypes.add(Type.EQ);
- return true;
- }
- }
- return false;
- }
-
- private boolean note(Return returnInstruction) {
- assert returnInstruction.inValues().size() == 1
- && returnInstruction.inValues().get(0).getAliasedValue() == arg;
- isReturned = true;
- return true;
- }
-
- private boolean note(Monitor monitorInstruction) {
- assert monitorInstruction.inValues().size() == 1;
- assert monitorInstruction.inValues().get(0).getAliasedValue() == arg;
- isUsedInMonitor = true;
- return true;
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
index bdcaae4..d6bdd6b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
@@ -15,9 +15,7 @@
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
import com.android.tools.r8.ir.code.InvokeDirect;
-import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
-import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
@@ -43,12 +41,8 @@
// Stores information about instance methods and constructors for
// class inliner, null value indicates that the method is not eligible.
private BridgeInfo bridgeInfo = null;
- private ClassInlinerEligibilityInfo classInlinerEligibility =
- DefaultMethodOptimizationInfo.UNKNOWN_CLASS_INLINER_ELIGIBILITY;
private InstanceInitializerInfoCollection instanceInitializerInfoCollection =
InstanceInitializerInfoCollection.empty();
- private ParameterUsagesInfo parametersUsages =
- DefaultMethodOptimizationInfo.UNKNOWN_PARAMETER_USAGE_INFO;
// Stores information about nullability hint per parameter. If set, that means, the method
// somehow (e.g., null check, such as arg != null, or using checkParameterIsNotNull) ensures
// the corresponding parameter is not null, or throws NPE before any other side effects.
@@ -147,9 +141,7 @@
inlining = template.inlining;
simpleInliningConstraint = template.simpleInliningConstraint;
bridgeInfo = template.bridgeInfo;
- classInlinerEligibility = template.classInlinerEligibility;
instanceInitializerInfoCollection = template.instanceInitializerInfoCollection;
- parametersUsages = template.parametersUsages;
nonNullParamOrThrow = template.nonNullParamOrThrow;
nonNullParamOnNormalExits = template.nonNullParamOnNormalExits;
}
@@ -276,11 +268,6 @@
}
@Override
- public ParameterUsage getParameterUsages(int parameter) {
- return parametersUsages == null ? null : parametersUsages.getParameterUsage(parameter);
- }
-
- @Override
public BitSet getNonNullParamOrThrow() {
return nonNullParamOrThrow;
}
@@ -330,11 +317,6 @@
}
@Override
- public ClassInlinerEligibilityInfo getClassInlinerEligibility() {
- return classInlinerEligibility;
- }
-
- @Override
public AbstractValue getAbstractReturnValue() {
return abstractReturnValue;
}
@@ -379,10 +361,6 @@
return isFlagSet(RETURN_VALUE_ONLY_DEPENDS_ON_ARGUMENTS_FLAG);
}
- void setParameterUsages(ParameterUsagesInfo parametersUsages) {
- this.parametersUsages = parametersUsages;
- }
-
void setNonNullParamOrThrow(BitSet facts) {
this.nonNullParamOrThrow = facts;
}
@@ -399,10 +377,6 @@
this.simpleInliningConstraint = constraint;
}
- void setClassInlinerEligibility(ClassInlinerEligibilityInfo eligibility) {
- this.classInlinerEligibility = eligibility;
- }
-
void setInstanceInitializerInfoCollection(
InstanceInitializerInfoCollection instanceInitializerInfoCollection) {
this.instanceInitializerInfoCollection = instanceInitializerInfoCollection;
@@ -517,6 +491,7 @@
}
public void adjustOptimizationInfoAfterRemovingThisParameter() {
+ classInlinerConstraint = classInlinerConstraint.fixupAfterRemovingThisParameter();
// cannotBeKept: doesn't depend on `this`
// classInitializerMayBePostponed: `this` could trigger <clinit> of the previous holder.
clearFlag(CLASS_INITIALIZER_MAY_BE_POSTPONED_FLAG);
@@ -549,18 +524,12 @@
// triggersClassInitBeforeAnySideEffect: code is not changed.
markTriggerClassInitBeforeAnySideEffect(
DefaultMethodOptimizationInfo.UNKNOWN_TRIGGERS_CLASS_INIT_BEFORE_ANY_SIDE_EFFECT);
- // classInlinerEligibility: chances are the method is not an instance method anymore.
- classInlinerEligibility = DefaultMethodOptimizationInfo.UNKNOWN_CLASS_INLINER_ELIGIBILITY;
// initializerInfo: the computed initializer info may become invalid.
instanceInitializerInfoCollection = InstanceInitializerInfoCollection.empty();
// initializerEnablingJavaAssertions: `this` could trigger <clinit> of the previous holder.
setFlag(
INITIALIZER_ENABLING_JAVA_ASSERTIONS_FLAG,
DefaultMethodOptimizationInfo.UNKNOWN_INITIALIZER_ENABLING_JAVA_ASSERTIONS);
- parametersUsages =
- parametersUsages == DefaultMethodOptimizationInfo.UNKNOWN_PARAMETER_USAGE_INFO
- ? DefaultMethodOptimizationInfo.UNKNOWN_PARAMETER_USAGE_INFO
- : parametersUsages.remove(0);
nonNullParamOrThrow =
nonNullParamOrThrow == DefaultMethodOptimizationInfo.NO_NULL_PARAMETER_OR_THROW_FACTS
? DefaultMethodOptimizationInfo.NO_NULL_PARAMETER_OR_THROW_FACTS
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index e169df0..3ba2c7b 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.OffOrAuto;
+import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
@@ -204,14 +205,14 @@
}
private void checkLambdaCount(CodeInspector inspector, int maxExpectedCount, String prefix) {
- int count = 0;
+ List<String> found = new ArrayList<>();
for (FoundClassSubject clazz : inspector.allClasses()) {
if (clazz.isSynthesizedJavaLambdaClass() &&
clazz.getOriginalName().startsWith(prefix)) {
- count++;
+ found.add(clazz.getOriginalName());
}
}
- assertEquals(maxExpectedCount, count);
+ assertEquals(StringUtils.lines(found), maxExpectedCount, found.size());
}
private void checkTestMultipleInterfacesCheckCastCount(
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java b/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java
index b5ecddc..61b0fd7 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java
@@ -15,10 +15,8 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ThrowableConsumer;
-import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import com.google.common.collect.ImmutableSet;
@@ -50,12 +48,6 @@
@Test
public void testDistribution() throws Exception {
assumeTrue(parameters.isDexRuntime());
- ThrowableConsumer<R8FullTestBuilder> configurator =
- r8FullTestBuilder ->
- r8FullTestBuilder
- .noMinification()
- .enableInliningAnnotations()
- .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.O));
ThrowableConsumer<R8TestCompileResult> ensureLambdaNotInBase =
r8TestCompileResult ->
r8TestCompileResult.inspect(
@@ -72,7 +64,7 @@
ImmutableSet.of(FeatureClass.class),
FeatureClass.class,
ensureLambdaNotInBase,
- configurator);
+ this::configure);
assertEquals(0, processResult.exitCode);
assertEquals(StringUtils.lines("42foobar"), processResult.stdout);
}
@@ -86,13 +78,7 @@
.addFeatureSplit(FeatureClass.class)
.addFeatureSplit(Feature2Class.class)
.addKeepFeatureMainRules(BaseSuperClass.class, FeatureClass.class, Feature2Class.class)
- .addKeepMethodRules(
- Reference.methodFromMethod(
- BaseSuperClass.class.getDeclaredMethod(
- "keptApplyLambda", MyFunction.class, String.class)))
- .noMinification()
- .enableInliningAnnotations()
- .setMinApi(parameters.getApiLevel())
+ .apply(this::configure)
.compile();
compileResult
@@ -104,6 +90,17 @@
.assertSuccessWithOutputLines("43barfoo");
}
+ private void configure(R8FullTestBuilder testBuilder) throws NoSuchMethodException {
+ testBuilder
+ .addKeepMethodRules(
+ Reference.methodFromMethod(
+ BaseSuperClass.class.getDeclaredMethod(
+ "keptApplyLambda", MyFunction.class, String.class)))
+ .enableInliningAnnotations()
+ .noMinification()
+ .setMinApi(parameters.getApiLevel());
+ }
+
public abstract static class BaseSuperClass implements RunInterface {
@Override
public void run() {
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9TreeShakeJarVerificationTest.java
index 3a09469..a9e28c9 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9TreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9TreeShakeJarVerificationTest.java
@@ -5,7 +5,6 @@
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.StringConsumer.FileConsumer;
-import com.android.tools.r8.utils.AndroidApp;
import java.io.File;
import java.nio.file.Path;
import org.junit.Test;
@@ -15,12 +14,11 @@
@Test
public void buildAndTreeShakeFromDeployJar() throws Exception {
Path proguardMapPath = File.createTempFile("mapping", ".txt", temp.getRoot()).toPath();
- AndroidApp app =
- buildAndTreeShakeFromDeployJar(
- CompilationMode.RELEASE,
- GMSCORE_V9_DIR,
- true,
- GMSCORE_V9_MAX_SIZE,
- options -> options.proguardMapConsumer = new FileConsumer(proguardMapPath));
+ buildAndTreeShakeFromDeployJar(
+ CompilationMode.RELEASE,
+ GMSCORE_V9_DIR,
+ true,
+ GMSCORE_V9_MAX_SIZE,
+ options -> options.proguardMapConsumer = new FileConsumer(proguardMapPath));
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
index 16413e0..e6258e9 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -20,7 +20,6 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.Lists;
@@ -63,15 +62,6 @@
&& clazz.getInterfaces().size() == 1;
}
- private static Predicate<DexType> createLambdaCheck(CodeInspector inspector) {
- Set<DexType> lambdaClasses =
- inspector.allClasses().stream()
- .filter(clazz -> isLambda(clazz.getDexProgramClass()))
- .map(clazz -> clazz.getDexProgramClass().type)
- .collect(Collectors.toSet());
- return lambdaClasses::contains;
- }
-
@Test
public void testJStyleLambdas() throws Exception {
String mainClassName = "class_inliner_lambda_j_style.MainKt";