blob: 073d6aef3225660737eecde3394e978517dff18b [file] [log] [blame]
// Copyright (c) 2018, 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;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfo.ResolutionResult;
import com.android.tools.r8.graph.DexClass;
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.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import java.util.Collection;
// Computes the inlining constraint for a given instruction.
public class InliningConstraints {
private AppInfoWithLiveness appInfo;
// Currently used only by the vertical class merger (in all other cases this is the identity).
//
// When merging a type A into its subtype B we need to inline A.<init>() into B.<init>().
// Therefore, we need to be sure that A.<init>() can in fact be inlined into B.<init>() *before*
// we merge the two classes. However, at this point, we may reject the method A.<init>() from
// being inlined into B.<init>() only because it is not declared in the same class as B (which
// it would be after merging A and B).
//
// To circumvent this problem, the vertical class merger creates a graph lense that maps the
// type A to B, to create a temporary view of what the world would look like after class merging.
private GraphLense graphLense;
public InliningConstraints(AppInfoWithLiveness appInfo) {
this(appInfo, GraphLense.getIdentityLense());
}
public InliningConstraints(AppInfoWithLiveness appInfo, GraphLense graphLense) {
assert graphLense.isContextFreeForMethods();
this.appInfo = appInfo;
this.graphLense = graphLense;
}
public Constraint forAlwaysMaterializingUser() {
return Constraint.ALWAYS;
}
public Constraint forArgument() {
return Constraint.ALWAYS;
}
public Constraint forArrayGet() {
return Constraint.ALWAYS;
}
public Constraint forArrayLength() {
return Constraint.ALWAYS;
}
public Constraint forArrayPut() {
return Constraint.ALWAYS;
}
public Constraint forBinop() {
return Constraint.ALWAYS;
}
public Constraint forCheckCast(DexType type, DexType invocationContext) {
return Constraint.classIsVisible(invocationContext, type, appInfo);
}
public Constraint forConstClass(DexType type, DexType invocationContext) {
return Constraint.classIsVisible(invocationContext, type, appInfo);
}
public Constraint forConstInstruction() {
return Constraint.ALWAYS;
}
public Constraint forDebugLocalRead() {
return Constraint.ALWAYS;
}
public Constraint forDebugLocalsChange() {
return Constraint.ALWAYS;
}
public Constraint forDebugPosition() {
return Constraint.ALWAYS;
}
public Constraint forInstanceGet(DexField field, DexType invocationContext) {
DexField lookup = graphLense.lookupField(field);
return forFieldInstruction(
lookup, appInfo.lookupInstanceTarget(lookup.clazz, lookup), invocationContext);
}
public Constraint forInstanceOf(DexType type, DexType invocationContext) {
return Constraint.classIsVisible(invocationContext, type, appInfo);
}
public Constraint forInstancePut(DexField field, DexType invocationContext) {
DexField lookup = graphLense.lookupField(field);
return forFieldInstruction(
lookup, appInfo.lookupInstanceTarget(lookup.clazz, lookup), invocationContext);
}
public Constraint forInvoke(DexMethod method, Type type, DexType invocationContext) {
switch (type) {
case DIRECT:
return forInvokeDirect(method, invocationContext);
case INTERFACE:
return forInvokeInterface(method, invocationContext);
case STATIC:
return forInvokeStatic(method, invocationContext);
case SUPER:
return forInvokeSuper(method, invocationContext);
case VIRTUAL:
return forInvokeVirtual(method, invocationContext);
case CUSTOM:
return forInvokeCustom();
case POLYMORPHIC:
return forInvokePolymorphic(method, invocationContext);
default:
throw new Unreachable("Unexpected type: " + type);
}
}
public Constraint forInvokeCustom() {
return Constraint.NEVER;
}
public Constraint forInvokeDirect(DexMethod method, DexType invocationContext) {
DexMethod lookup = graphLense.lookupMethod(method);
return forSingleTargetInvoke(lookup, appInfo.lookupDirectTarget(lookup), invocationContext);
}
public Constraint forInvokeInterface(DexMethod method, DexType invocationContext) {
DexMethod lookup = graphLense.lookupMethod(method);
return forVirtualInvoke(lookup, appInfo.lookupInterfaceTargets(lookup), invocationContext);
}
public Constraint forInvokeMultiNewArray(DexType type, DexType invocationContext) {
return Constraint.classIsVisible(invocationContext, type, appInfo);
}
public Constraint forInvokeNewArray(DexType type, DexType invocationContext) {
return Constraint.classIsVisible(invocationContext, type, appInfo);
}
public Constraint forInvokePolymorphic(DexMethod method, DexType invocationContext) {
return Constraint.NEVER;
}
public Constraint forInvokeStatic(DexMethod method, DexType invocationContext) {
DexMethod lookup = graphLense.lookupMethod(method);
return forSingleTargetInvoke(lookup, appInfo.lookupStaticTarget(lookup), invocationContext);
}
public Constraint forInvokeSuper(DexMethod method, DexType invocationContext) {
// The semantics of invoke super depend on the context.
return Constraint.SAMECLASS;
}
public Constraint forInvokeVirtual(DexMethod method, DexType invocationContext) {
DexMethod lookup = graphLense.lookupMethod(method);
return forVirtualInvoke(lookup, appInfo.lookupVirtualTargets(lookup), invocationContext);
}
public Constraint forJumpInstruction() {
return Constraint.ALWAYS;
}
public Constraint forLoad() {
return Constraint.ALWAYS;
}
public Constraint forMonitor() {
// Conservative choice.
return Constraint.NEVER;
}
public Constraint forMove() {
return Constraint.ALWAYS;
}
public Constraint forMoveException() {
// TODO(64432527): Revisit this constraint.
return Constraint.NEVER;
}
public Constraint forNewArrayEmpty(DexType type, DexType invocationContext) {
return Constraint.classIsVisible(invocationContext, type, appInfo);
}
public Constraint forNewArrayFilledData() {
return Constraint.ALWAYS;
}
public Constraint forNewInstance(DexType type, DexType invocationContext) {
return Constraint.classIsVisible(invocationContext, type, appInfo);
}
public Constraint forNonNull() {
return Constraint.ALWAYS;
}
public Constraint forPop() {
return Constraint.ALWAYS;
}
public Constraint forReturn() {
return Constraint.ALWAYS;
}
public Constraint forStaticGet(DexField field, DexType invocationContext) {
DexField lookup = graphLense.lookupField(field);
return forFieldInstruction(
lookup, appInfo.lookupStaticTarget(lookup.clazz, lookup), invocationContext);
}
public Constraint forStaticPut(DexField field, DexType invocationContext) {
DexField lookup = graphLense.lookupField(field);
return forFieldInstruction(
lookup, appInfo.lookupStaticTarget(lookup.clazz, lookup), invocationContext);
}
public Constraint forStore() {
return Constraint.ALWAYS;
}
public Constraint forThrow() {
return Constraint.ALWAYS;
}
public Constraint forUnop() {
return Constraint.ALWAYS;
}
private Constraint forFieldInstruction(
DexField field, DexEncodedField target, DexType invocationContext) {
// Resolve the field if possible and decide whether the instruction can inlined.
DexType fieldHolder = graphLense.lookupType(field.clazz);
DexClass fieldClass = appInfo.definitionFor(fieldHolder);
if (target != null && fieldClass != null) {
Constraint fieldConstraint =
Constraint.deriveConstraint(invocationContext, fieldHolder, target.accessFlags, appInfo);
Constraint classConstraint =
Constraint.deriveConstraint(
invocationContext, fieldHolder, fieldClass.accessFlags, appInfo);
return Constraint.min(fieldConstraint, classConstraint);
}
return Constraint.NEVER;
}
private Constraint forSingleTargetInvoke(
DexMethod method, DexEncodedMethod target, DexType invocationContext) {
if (method.holder.isArrayType()) {
return Constraint.ALWAYS;
}
if (target != null) {
DexType methodHolder = graphLense.lookupType(target.method.holder);
DexClass methodClass = appInfo.definitionFor(methodHolder);
if (methodClass != null) {
Constraint methodConstraint =
Constraint.deriveConstraint(
invocationContext, methodHolder, target.accessFlags, appInfo);
// We also have to take the constraint of the enclosing class into account.
Constraint classConstraint =
Constraint.deriveConstraint(
invocationContext, methodHolder, methodClass.accessFlags, appInfo);
return Constraint.min(methodConstraint, classConstraint);
}
}
return Constraint.NEVER;
}
private Constraint forVirtualInvoke(
DexMethod method, Collection<DexEncodedMethod> targets, DexType invocationContext) {
if (method.holder.isArrayType()) {
return Constraint.ALWAYS;
}
if (targets == null) {
return Constraint.NEVER;
}
// Perform resolution and derive inlining constraints based on the accessibility of the
// resolution result.
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
DexEncodedMethod resolutionTarget = resolutionResult.asResultOfResolve();
if (resolutionTarget == null) {
// This will fail at runtime.
return Constraint.NEVER;
}
DexType methodHolder = graphLense.lookupType(resolutionTarget.method.holder);
DexClass methodClass = appInfo.definitionFor(methodHolder);
assert methodClass != null;
Constraint methodConstraint =
Constraint.deriveConstraint(
invocationContext, methodHolder, resolutionTarget.accessFlags, appInfo);
// We also have to take the constraint of the enclosing class of the resolution result
// into account. We do not allow inlining this method if it is calling something that
// is inaccessible. Inlining in that case could move the code to another package making a
// call succeed that should not succeed. Conversely, if the resolution result is accessible,
// we have to make sure that inlining cannot make it inaccessible.
Constraint classConstraint =
Constraint.deriveConstraint(
invocationContext, methodHolder, methodClass.accessFlags, appInfo);
Constraint result = Constraint.min(methodConstraint, classConstraint);
if (result == Constraint.NEVER) {
return result;
}
// For each of the actual potential targets, derive constraints based on the accessibility
// of the method itself.
for (DexEncodedMethod target : targets) {
methodHolder = graphLense.lookupType(target.method.holder);
assert appInfo.definitionFor(methodHolder) != null;
methodConstraint =
Constraint.deriveConstraint(invocationContext, methodHolder, target.accessFlags, appInfo);
result = Constraint.min(result, methodConstraint);
if (result == Constraint.NEVER) {
return result;
}
}
return result;
}
}