blob: efe5cb6b9ec116b222f5099e5c6649943cc11d7d [file] [log] [blame]
// 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 static java.util.Collections.emptySet;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.ir.analysis.inlining.NeverSimpleInliningConstraint;
import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
import com.android.tools.r8.ir.analysis.type.TypeElement;
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.code.InvokeMethod;
import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
import com.android.tools.r8.ir.optimize.enums.classification.EnumUnboxerMethodClassification;
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;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.AbstractFunction;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.MaximumRemovedAndroidLogLevelRule;
import com.android.tools.r8.utils.BitSetUtils;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.OptionalBool;
import java.util.BitSet;
import java.util.Set;
import java.util.function.Consumer;
public class MutableMethodOptimizationInfo extends MethodOptimizationInfo
implements MutableOptimizationInfo {
private CallSiteOptimizationInfo argumentInfos = CallSiteOptimizationInfo.top();
private Set<DexType> initializedClassesOnNormalExit =
DefaultMethodOptimizationInfo.UNKNOWN_INITIALIZED_CLASSES_ON_NORMAL_EXIT;
private int returnedArgument = DefaultMethodOptimizationInfo.UNKNOWN_RETURNED_ARGUMENT;
private AbstractFunction abstractFunction = AbstractFunction.unknown();
private AbstractValue abstractReturnValue =
DefaultMethodOptimizationInfo.UNKNOWN_ABSTRACT_RETURN_VALUE;
private ClassInlinerMethodConstraint classInlinerConstraint =
ClassInlinerMethodConstraint.alwaysFalse();
private boolean convertCheckNotNull = false;
private EnumUnboxerMethodClassification enumUnboxerMethodClassification =
EnumUnboxerMethodClassification.unknown();
private DynamicType dynamicType = DynamicType.unknown();
private InlinePreference inlining = InlinePreference.Default;
private OptionalBool isReturnValueUsed = OptionalBool.unknown();
// Stores information about instance methods and constructors for
// class inliner, null value indicates that the method is not eligible.
private BridgeInfo bridgeInfo = null;
private InstanceInitializerInfoCollection instanceInitializerInfoCollection =
InstanceInitializerInfoCollection.empty();
// 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.
// This info is used by {@link UninstantiatedTypeOptimization#rewriteInvoke} that replaces an
// invocation with null throwing code if an always-null argument is passed. Also used by Inliner
// to give a credit to null-safe code, e.g., Kotlin's null safe argument.
// Note that this bit set takes into account the receiver for instance methods.
private BitSet nonNullParamOrThrow =
DefaultMethodOptimizationInfo.NO_NULL_PARAMETER_OR_THROW_FACTS;
// Stores information about nullability facts per parameter. If set, that means, the method
// somehow (e.g., null check, such as arg != null, or NPE-throwing instructions such as array
// access or another invocation) ensures the corresponding parameter is not null, and that is
// guaranteed until the normal exits. That is, if the invocation of this method is finished
// normally, the recorded parameter is definitely not null. These facts are used to propagate
// non-null information through {@link NonNullTracker}.
// Note that this bit set takes into account the receiver for instance methods.
private BitSet nonNullParamOnNormalExits =
DefaultMethodOptimizationInfo.NO_NULL_PARAMETER_ON_NORMAL_EXITS_FACTS;
private SimpleInliningConstraint nopInliningConstraint =
NeverSimpleInliningConstraint.getInstance();
private SimpleInliningConstraint simpleInliningConstraint =
NeverSimpleInliningConstraint.getInstance();
private int maxRemovedAndroidLogLevel = MaximumRemovedAndroidLogLevelRule.NOT_SET;
private BitSet parametersWithBitwiseOperations = null;
private BitSet unusedArguments = null;
// To reduce the memory footprint of UpdatableMethodOptimizationInfo, all the boolean fields are
// merged into a flag int field. The various static final FLAG fields indicate which bit is
// used by each boolean. DEFAULT_FLAGS encodes the default value for efficient instantiation and
// is computed during class initialization from the default method optimization info. The
// methods setFlag, clearFlag and isFlagSet are used to access the booleans.
private static final int CANNOT_BE_KEPT_FLAG = 0x1;
private static final int CLASS_INITIALIZER_MAY_BE_POSTPONED_FLAG = 0x2;
private static final int HAS_BEEN_INLINED_INTO_SINGLE_CALL_SITE_FLAG = 0x4;
private static final int MAY_HAVE_SIDE_EFFECT_FLAG = 0x8;
private static final int RETURN_VALUE_ONLY_DEPENDS_ON_ARGUMENTS_FLAG = 0x10;
private static final int NEVER_RETURNS_NORMALLY_FLAG = 0x20;
private static final int INITIALIZER_ENABLING_JAVA_ASSERTIONS_FLAG = 0x80;
private static final int RETURN_VALUE_HAS_BEEN_PROPAGATED_FLAG = 0x100;
private static final int DEFAULT_FLAGS;
static {
int defaultFlags = 0;
MethodOptimizationInfo defaultOptInfo = DefaultMethodOptimizationInfo.DEFAULT_INSTANCE;
defaultFlags |= BooleanUtils.intValue(defaultOptInfo.cannotBeKept()) * CANNOT_BE_KEPT_FLAG;
defaultFlags |=
BooleanUtils.intValue(defaultOptInfo.classInitializerMayBePostponed())
* CLASS_INITIALIZER_MAY_BE_POSTPONED_FLAG;
defaultFlags |=
BooleanUtils.intValue(defaultOptInfo.hasBeenInlinedIntoSingleCallSite())
* HAS_BEEN_INLINED_INTO_SINGLE_CALL_SITE_FLAG;
defaultFlags |=
BooleanUtils.intValue(defaultOptInfo.mayHaveSideEffects()) * MAY_HAVE_SIDE_EFFECT_FLAG;
defaultFlags |=
BooleanUtils.intValue(defaultOptInfo.returnValueOnlyDependsOnArguments())
* RETURN_VALUE_ONLY_DEPENDS_ON_ARGUMENTS_FLAG;
defaultFlags |=
BooleanUtils.intValue(defaultOptInfo.neverReturnsNormally()) * NEVER_RETURNS_NORMALLY_FLAG;
defaultFlags |=
BooleanUtils.intValue(defaultOptInfo.isInitializerEnablingJavaVmAssertions())
* INITIALIZER_ENABLING_JAVA_ASSERTIONS_FLAG;
defaultFlags |=
BooleanUtils.intValue(defaultOptInfo.returnValueHasBeenPropagated())
* RETURN_VALUE_HAS_BEEN_PROPAGATED_FLAG;
DEFAULT_FLAGS = defaultFlags;
}
private int flags = DEFAULT_FLAGS;
MutableMethodOptimizationInfo() {
// Intentionally left empty, just use the default values.
}
// Copy constructor used to create a mutable copy. Do not forget to copy from template when a new
// field is added.
private MutableMethodOptimizationInfo(MutableMethodOptimizationInfo template) {
abstractFunction = template.abstractFunction;
argumentInfos = template.argumentInfos;
flags = template.flags;
initializedClassesOnNormalExit = template.initializedClassesOnNormalExit;
returnedArgument = template.returnedArgument;
abstractReturnValue = template.abstractReturnValue;
setDynamicType(template.dynamicType);
inlining = template.inlining;
nopInliningConstraint = template.nopInliningConstraint;
simpleInliningConstraint = template.simpleInliningConstraint;
bridgeInfo = template.bridgeInfo;
instanceInitializerInfoCollection = template.instanceInitializerInfoCollection;
nonNullParamOrThrow = template.nonNullParamOrThrow;
nonNullParamOnNormalExits = template.nonNullParamOnNormalExits;
classInlinerConstraint = template.classInlinerConstraint;
enumUnboxerMethodClassification = template.enumUnboxerMethodClassification;
maxRemovedAndroidLogLevel = template.maxRemovedAndroidLogLevel;
}
public MutableMethodOptimizationInfo applyIf(
boolean condition,
Consumer<MutableMethodOptimizationInfo> thenConsumer,
Consumer<MutableMethodOptimizationInfo> elseConsumer) {
if (condition) {
thenConsumer.accept(this);
} else {
elseConsumer.accept(this);
}
return this;
}
public MutableMethodOptimizationInfo fixup(
AppView<AppInfoWithLiveness> appView,
DexEncodedMethod method,
MethodOptimizationInfoFixer fixer) {
return fixupArgumentInfos(method, fixer)
.fixupBridgeInfo(fixer)
.fixupClassInlinerMethodConstraint(appView, fixer)
.fixupDynamicType(fixer)
.fixupAbstractReturnValue(appView, fixer)
.fixupEnumUnboxerMethodClassification(fixer)
.fixupInstanceInitializerInfo(appView, fixer)
.fixupNonNullParamOnNormalExits(fixer)
.fixupNonNullParamOrThrow(fixer)
.fixupReturnedArgumentIndex(fixer)
.fixupParametersWithBitwiseOperations(fixer)
.fixupNopInliningConstraint(appView, fixer)
.fixupSimpleInliningConstraint(appView, fixer)
.fixupUnusedArguments(fixer);
}
private MutableMethodOptimizationInfo fixupDynamicType(MethodOptimizationInfoFixer fixer) {
if (dynamicType.isUnknown()) {
return this;
}
return setDynamicType(fixer.fixupDynamicType(dynamicType));
}
public MutableMethodOptimizationInfo fixupClassTypeReferences(
AppView<AppInfoWithLiveness> appView, GraphLens lens) {
return fixupClassTypeReferences(appView, lens, emptySet());
}
public MutableMethodOptimizationInfo fixupClassTypeReferences(
AppView<AppInfoWithLiveness> appView, GraphLens lens, Set<DexType> prunedTypes) {
DynamicType rewrittenDynamicType = dynamicType.rewrittenWithLens(appView, lens, prunedTypes);
if (rewrittenDynamicType.hasDynamicUpperBoundType()) {
DynamicTypeWithUpperBound rewrittenDynamicTypeWithUpperBound =
rewrittenDynamicType.asDynamicTypeWithUpperBound();
if (rewrittenDynamicTypeWithUpperBound.getDynamicUpperBoundType().isPrimitiveType()) {
// Do not store primitive dynamic types.
assert verifyDynamicTypeIsUnboxedEnum(appView, dynamicType);
return unsetDynamicType();
}
}
return setDynamicType(rewrittenDynamicType);
}
private static boolean verifyDynamicTypeIsUnboxedEnum(
AppView<?> appView, DynamicType dynamicType) {
assert dynamicType.isDynamicTypeWithUpperBound();
DynamicTypeWithUpperBound dynamicTypeWithUpperBound = dynamicType.asDynamicTypeWithUpperBound();
TypeElement dynamicUpperBoundType = dynamicTypeWithUpperBound.getDynamicUpperBoundType();
assert dynamicUpperBoundType.isClassType();
ClassTypeElement dynamicUpperBoundClassType = dynamicUpperBoundType.asClassType();
assert appView.hasUnboxedEnums();
assert appView.unboxedEnums().isUnboxedEnum(dynamicUpperBoundClassType.getClassType());
return true;
}
public MutableMethodOptimizationInfo fixupAbstractReturnValue(
AppView<AppInfoWithLiveness> appView, MethodOptimizationInfoFixer fixer) {
if (abstractReturnValue.isUnknown()) {
return this;
}
abstractReturnValue = fixer.fixupAbstractReturnValue(appView, abstractReturnValue);
return this;
}
public MutableMethodOptimizationInfo fixupAbstractReturnValue(
AppView<AppInfoWithLiveness> appView,
DexEncodedMethod method,
GraphLens lens,
GraphLens codeLens) {
abstractReturnValue =
abstractReturnValue.rewrittenWithLens(appView, method.getReturnType(), lens, codeLens);
return this;
}
public MutableMethodOptimizationInfo fixupInstanceInitializerInfo(
AppView<AppInfoWithLiveness> appView,
GraphLens lens,
GraphLens codeLens,
PrunedItems prunedItems) {
instanceInitializerInfoCollection =
instanceInitializerInfoCollection.rewrittenWithLens(appView, lens, codeLens, prunedItems);
return this;
}
private void setFlag(int flag) {
flags |= flag;
}
private void clearFlag(int flag) {
flags &= ~flag;
}
private boolean isFlagSet(int flag) {
return (flags & flag) != 0;
}
@Override
public boolean cannotBeKept() {
return isFlagSet(CANNOT_BE_KEPT_FLAG);
}
// TODO(b/140214568): Should be package-private.
public void markCannotBeKept() {
setFlag(CANNOT_BE_KEPT_FLAG);
}
@Override
public boolean classInitializerMayBePostponed() {
return isFlagSet(CLASS_INITIALIZER_MAY_BE_POSTPONED_FLAG);
}
void markClassInitializerMayBePostponed() {
setFlag(CLASS_INITIALIZER_MAY_BE_POSTPONED_FLAG);
}
void unsetClassInitializerMayBePostponed() {
clearFlag(CLASS_INITIALIZER_MAY_BE_POSTPONED_FLAG);
}
@Override
public CallSiteOptimizationInfo getArgumentInfos() {
return argumentInfos;
}
public MutableMethodOptimizationInfo fixupArgumentInfos(
DexEncodedMethod method, MethodOptimizationInfoFixer fixer) {
if (argumentInfos.isConcreteCallSiteOptimizationInfo()) {
return setArgumentInfos(
method,
fixer.fixupCallSiteOptimizationInfo(argumentInfos.asConcreteCallSiteOptimizationInfo()));
}
return this;
}
MutableMethodOptimizationInfo setArgumentInfos(
// Method reference allows easily debugging when the optimization info for a given method
// changes.
@SuppressWarnings("unused") DexEncodedMethod method, CallSiteOptimizationInfo argumentInfos) {
this.argumentInfos = argumentInfos;
return this;
}
public void unsetArgumentInfos() {
argumentInfos = CallSiteOptimizationInfo.top();
}
@Override
public ClassInlinerMethodConstraint getClassInlinerMethodConstraint() {
return classInlinerConstraint;
}
public MutableMethodOptimizationInfo fixupClassInlinerMethodConstraint(
AppView<AppInfoWithLiveness> appView, MethodOptimizationInfoFixer fixer) {
classInlinerConstraint =
fixer.fixupClassInlinerMethodConstraint(appView, classInlinerConstraint);
return this;
}
void setClassInlinerMethodConstraint(ClassInlinerMethodConstraint classInlinerConstraint) {
this.classInlinerConstraint = classInlinerConstraint;
}
void unsetClassInlinerMethodConstraint() {
this.classInlinerConstraint = ClassInlinerMethodConstraint.alwaysFalse();
}
void setConvertCheckNotNull() {
this.convertCheckNotNull = true;
}
@Override
public EnumUnboxerMethodClassification getEnumUnboxerMethodClassification() {
return enumUnboxerMethodClassification;
}
public MutableMethodOptimizationInfo setEnumUnboxerMethodClassification(
EnumUnboxerMethodClassification enumUnboxerMethodClassification) {
// Check monotonicity.
assert !this.enumUnboxerMethodClassification.isCheckNotNullClassification()
|| enumUnboxerMethodClassification.isCheckNotNullClassification();
this.enumUnboxerMethodClassification = enumUnboxerMethodClassification;
return this;
}
public void unsetEnumUnboxerMethodClassification() {
this.enumUnboxerMethodClassification = EnumUnboxerMethodClassification.unknown();
}
public MutableMethodOptimizationInfo fixupEnumUnboxerMethodClassification(
MethodOptimizationInfoFixer fixer) {
enumUnboxerMethodClassification =
fixer.fixupEnumUnboxerMethodClassification(enumUnboxerMethodClassification);
return this;
}
@Override
public DynamicType getDynamicType() {
return dynamicType;
}
@Override
public Set<DexType> getInitializedClassesOnNormalExit() {
return initializedClassesOnNormalExit;
}
@Override
public InstanceInitializerInfo getContextInsensitiveInstanceInitializerInfo() {
return instanceInitializerInfoCollection.getContextInsensitive();
}
@Override
public InstanceInitializerInfo getInstanceInitializerInfo(InvokeDirect invoke) {
return instanceInitializerInfoCollection.get(invoke);
}
public MutableMethodOptimizationInfo fixupInstanceInitializerInfo(
AppView<AppInfoWithLiveness> appView, MethodOptimizationInfoFixer fixer) {
instanceInitializerInfoCollection =
fixer.fixupInstanceInitializerInfo(appView, instanceInitializerInfoCollection);
return this;
}
@Override
public int getMaxRemovedAndroidLogLevel() {
return maxRemovedAndroidLogLevel;
}
public void joinMaxRemovedAndroidLogLevel(int maxRemovedAndroidLogLevel) {
this.maxRemovedAndroidLogLevel =
MaximumRemovedAndroidLogLevelRule.joinMaxRemovedAndroidLogLevel(
this.maxRemovedAndroidLogLevel, maxRemovedAndroidLogLevel);
}
@Override
public BitSet getNonNullParamOrThrow() {
return nonNullParamOrThrow;
}
public MutableMethodOptimizationInfo fixupNonNullParamOrThrow(MethodOptimizationInfoFixer fixer) {
nonNullParamOrThrow = fixer.fixupNonNullParamOrThrow(nonNullParamOrThrow);
return this;
}
void setNonNullParamOrThrow(BitSet facts) {
this.nonNullParamOrThrow = facts;
}
void unsetNonNullParamOrThrow() {
this.nonNullParamOrThrow = null;
}
@Override
public BitSet getNonNullParamOnNormalExits() {
return nonNullParamOnNormalExits;
}
public MutableMethodOptimizationInfo fixupNonNullParamOnNormalExits(
MethodOptimizationInfoFixer fixer) {
nonNullParamOnNormalExits = fixer.fixupNonNullParamOnNormalExits(nonNullParamOnNormalExits);
return this;
}
void setNonNullParamOnNormalExits(BitSet facts) {
this.nonNullParamOnNormalExits = facts;
}
void unsetNonNullParamOnNormalExits() {
nonNullParamOnNormalExits = null;
}
@Override
public boolean hasBeenInlinedIntoSingleCallSite() {
return isFlagSet(HAS_BEEN_INLINED_INTO_SINGLE_CALL_SITE_FLAG);
}
void markInlinedIntoSingleCallSite() {
setFlag(HAS_BEEN_INLINED_INTO_SINGLE_CALL_SITE_FLAG);
}
void unsetInlinedIntoSingleCallSite() {
clearFlag(HAS_BEEN_INLINED_INTO_SINGLE_CALL_SITE_FLAG);
}
@Override
public boolean returnsArgument() {
return returnedArgument != -1;
}
@Override
public int getReturnedArgument() {
return returnedArgument;
}
@Override
public boolean neverReturnsNormally() {
return isFlagSet(NEVER_RETURNS_NORMALLY_FLAG);
}
@Override
public BridgeInfo getBridgeInfo() {
return bridgeInfo;
}
public MutableMethodOptimizationInfo fixupBridgeInfo(MethodOptimizationInfoFixer fixer) {
if (bridgeInfo != null) {
bridgeInfo = fixer.fixupBridgeInfo(bridgeInfo);
}
return this;
}
void setBridgeInfo(BridgeInfo bridgeInfo) {
this.bridgeInfo = bridgeInfo;
}
void unsetBridgeInfo() {
this.bridgeInfo = null;
}
@Override
public AbstractFunction getAbstractFunction() {
return abstractFunction;
}
@Override
public AbstractValue getAbstractReturnValue() {
return abstractReturnValue;
}
@Override
public SimpleInliningConstraint getNopInliningConstraint() {
return nopInliningConstraint;
}
@Override
public SimpleInliningConstraint getSimpleInliningConstraint() {
return simpleInliningConstraint;
}
@Override
public boolean hasParametersWithBitwiseOperations() {
return parametersWithBitwiseOperations != null;
}
@Override
public BitSet getParametersWithBitwiseOperations() {
return parametersWithBitwiseOperations;
}
public void setParametersWithBitwiseOperations(BitSet parametersWithBitwiseOperations) {
if (parametersWithBitwiseOperations != null && !parametersWithBitwiseOperations.isEmpty()) {
this.parametersWithBitwiseOperations = parametersWithBitwiseOperations;
} else {
this.parametersWithBitwiseOperations = null;
}
}
public MutableMethodOptimizationInfo fixupParametersWithBitwiseOperations(
MethodOptimizationInfoFixer fixer) {
return fixupParametersWithBitwiseOperations(fixer.fixupArguments(unusedArguments));
}
public MutableMethodOptimizationInfo fixupParametersWithBitwiseOperations(
BitSet parametersWithBitwiseOperations) {
setParametersWithBitwiseOperations(parametersWithBitwiseOperations);
return this;
}
@Override
public BitSet getUnusedArguments() {
return unusedArguments;
}
public MutableMethodOptimizationInfo fixupUnusedArguments(MethodOptimizationInfoFixer fixer) {
return fixupUnusedArguments(fixer.fixupArguments(unusedArguments));
}
public MutableMethodOptimizationInfo fixupUnusedArguments(BitSet unusedArguments) {
this.unusedArguments =
unusedArguments != null && !unusedArguments.isEmpty() ? unusedArguments : null;
return this;
}
void setUnusedArguments(BitSet unusedArguments) {
// Verify monotonicity (i.e., unused arguments should never become used).
assert !hasUnusedArguments() || unusedArguments != null;
assert !hasUnusedArguments()
|| BitSetUtils.verifyLessThanOrEqualTo(getUnusedArguments(), unusedArguments);
this.unusedArguments =
unusedArguments != null && !unusedArguments.isEmpty() ? unusedArguments : null;
}
void unsetUnusedArguments() {
unusedArguments = null;
}
@Override
public boolean isConvertCheckNotNull() {
return convertCheckNotNull;
}
@Override
public boolean isInitializerEnablingJavaVmAssertions() {
return isFlagSet(INITIALIZER_ENABLING_JAVA_ASSERTIONS_FLAG);
}
@Override
public boolean isMultiCallerMethod() {
return inlining == InlinePreference.MultiCallerInline;
}
@Override
public OptionalBool isReturnValueUsed() {
return isReturnValueUsed;
}
void setIsReturnValueUsed(OptionalBool isReturnValueUsed) {
this.isReturnValueUsed = isReturnValueUsed;
}
@Override
public boolean forceInline() {
return inlining == InlinePreference.ForceInline;
}
@Override
public boolean mayHaveSideEffects() {
return isFlagSet(MAY_HAVE_SIDE_EFFECT_FLAG);
}
@Override
public boolean mayHaveSideEffects(InvokeMethod invoke, InternalOptions options) {
if (!mayHaveSideEffects()) {
return false;
}
if (getNopInliningConstraint().isSatisfied(invoke)) {
return false;
}
return true;
}
@Override
public boolean returnValueOnlyDependsOnArguments() {
return isFlagSet(RETURN_VALUE_ONLY_DEPENDS_ON_ARGUMENTS_FLAG);
}
void setNopInliningConstraint(SimpleInliningConstraint constraint) {
this.nopInliningConstraint = constraint;
}
void unsetNopInliningConstraint() {
nopInliningConstraint = NeverSimpleInliningConstraint.getInstance();
}
public MutableMethodOptimizationInfo fixupNopInliningConstraint(
AppView<AppInfoWithLiveness> appView, MethodOptimizationInfoFixer fixer) {
nopInliningConstraint =
fixer.fixupNopInliningConstraint(
appView, nopInliningConstraint, appView.simpleInliningConstraintFactory());
return this;
}
void setSimpleInliningConstraint(SimpleInliningConstraint constraint) {
this.simpleInliningConstraint = constraint;
}
void unsetSimpleInliningConstraint() {
simpleInliningConstraint = NeverSimpleInliningConstraint.getInstance();
}
public MutableMethodOptimizationInfo fixupSimpleInliningConstraint(
AppView<AppInfoWithLiveness> appView, MethodOptimizationInfoFixer fixer) {
simpleInliningConstraint =
fixer.fixupSimpleInliningConstraint(
appView, simpleInliningConstraint, appView.simpleInliningConstraintFactory());
return this;
}
void setInstanceInitializerInfoCollection(
InstanceInitializerInfoCollection instanceInitializerInfoCollection) {
this.instanceInitializerInfoCollection = instanceInitializerInfoCollection;
}
void unsetInstanceInitializerInfoCollection() {
instanceInitializerInfoCollection = InstanceInitializerInfoCollection.empty();
}
void setInitializerEnablingJavaAssertions() {
setFlag(INITIALIZER_ENABLING_JAVA_ASSERTIONS_FLAG);
}
void unsetInitializerEnablingJavaVmAssertions() {
clearFlag(INITIALIZER_ENABLING_JAVA_ASSERTIONS_FLAG);
}
void markInitializesClassesOnNormalExit(Set<DexType> initializedClassesOnNormalExit) {
if (initializedClassesOnNormalExit.isEmpty()) {
unsetInitializedClassesOnNormalExit();
} else {
this.initializedClassesOnNormalExit = initializedClassesOnNormalExit;
}
}
void unsetInitializedClassesOnNormalExit() {
initializedClassesOnNormalExit =
DefaultMethodOptimizationInfo.getInstance().getInitializedClassesOnNormalExit();
}
void markReturnsArgument(int returnedArgumentIndex) {
assert returnedArgumentIndex >= 0;
assert returnedArgument == -1 || returnedArgument == returnedArgumentIndex;
returnedArgument = returnedArgumentIndex;
}
void unsetReturnedArgument() {
returnedArgument = -1;
}
public MutableMethodOptimizationInfo fixupReturnedArgumentIndex(
MethodOptimizationInfoFixer fixer) {
returnedArgument = fixer.fixupReturnedArgumentIndex(returnedArgument);
return this;
}
void markMayNotHaveSideEffects() {
clearFlag(MAY_HAVE_SIDE_EFFECT_FLAG);
}
void unsetMayNotHaveSideEffects() {
setFlag(MAY_HAVE_SIDE_EFFECT_FLAG);
}
void markReturnValueOnlyDependsOnArguments() {
setFlag(RETURN_VALUE_ONLY_DEPENDS_ON_ARGUMENTS_FLAG);
}
void unsetReturnValueOnlyDependsOnArguments() {
clearFlag(RETURN_VALUE_ONLY_DEPENDS_ON_ARGUMENTS_FLAG);
}
void markNeverReturnsNormally() {
setFlag(NEVER_RETURNS_NORMALLY_FLAG);
}
void unsetNeverReturnsNormally() {
clearFlag(NEVER_RETURNS_NORMALLY_FLAG);
}
void setAbstractReturnValue(AbstractValue value, DexEncodedMethod method) {
assert !value.isNull() || method.getReturnType().isReferenceType();
setAbstractReturnValue(value);
}
private void setAbstractReturnValue(AbstractValue value) {
assert !abstractReturnValue.isSingleValue()
|| abstractReturnValue.equals(value)
|| (abstractReturnValue.isSingleStatelessFieldValue()
&& value.isSingleStatefulFieldValue()
&& abstractReturnValue
.asSingleFieldValue()
.getField()
.isIdenticalTo(value.asSingleFieldValue().getField()))
: "return single value changed from " + abstractReturnValue + " to " + value;
abstractReturnValue = value;
}
void unsetAbstractReturnValue() {
abstractReturnValue = UnknownValue.getInstance();
}
void setAbstractFunction(AbstractFunction abstractFunction) {
this.abstractFunction = abstractFunction;
}
void setDynamicType(AppView<?> appView, DynamicType newDynamicType, DexEncodedMethod method) {
setDynamicType(appView, newDynamicType, method.getReturnType().toTypeElement(appView));
}
public void setDynamicType(
AppView<?> appView, DynamicType newDynamicType, TypeElement staticReturnType) {
assert newDynamicType != null;
// We may get more precise type information if the method is reprocessed (e.g., due to
// optimization info collected from all call sites), and hence the
// `returnsObjectWithUpperBoundType` is allowed to become more precise.
// TODO(b/142559221): non-materializable assume instructions?
// Nullability could be less precise, though. For example, suppose a value is known to be
// non-null after a safe invocation, hence recorded with the non-null variant. If that call is
// inlined and the method is reprocessed, such non-null assumption cannot be made again.
assert verifyDynamicType(appView, newDynamicType, staticReturnType);
setDynamicType(newDynamicType);
}
public MutableMethodOptimizationInfo setDynamicType(DynamicType dynamicType) {
assert !dynamicType.hasDynamicUpperBoundType()
|| !dynamicType.asDynamicTypeWithUpperBound().getDynamicUpperBoundType().isPrimitiveType();
this.dynamicType = dynamicType;
return this;
}
private boolean verifyDynamicType(
AppView<?> appView, DynamicType newDynamicType, TypeElement staticReturnType) {
if (appView.enableWholeProgramOptimizations()) {
TypeElement previousDynamicUpperBoundType =
dynamicType.getDynamicUpperBoundType(staticReturnType);
TypeElement newDynamicUpperBoundType =
newDynamicType.getDynamicUpperBoundType(staticReturnType);
assert newDynamicUpperBoundType.lessThanOrEqualUpToNullability(
previousDynamicUpperBoundType, appView)
: "upper bound type changed from "
+ previousDynamicUpperBoundType
+ " to "
+ newDynamicUpperBoundType;
}
return true;
}
public MutableMethodOptimizationInfo unsetDynamicType() {
return setDynamicType(DynamicType.unknown());
}
// TODO(b/140214568): Should be package-private.
public void markForceInline() {
// For concurrent scenarios we should allow the flag to be already set
assert inlining == InlinePreference.Default || inlining == InlinePreference.ForceInline;
inlining = InlinePreference.ForceInline;
}
void unsetForceInline() {
inlining = InlinePreference.Default;
}
void setMultiCallerMethod() {
if (inlining == InlinePreference.Default) {
inlining = InlinePreference.MultiCallerInline;
} else {
assert inlining == InlinePreference.ForceInline;
}
}
// TODO(b/140214568): Should be package-private.
public void markAsPropagated() {
setFlag(RETURN_VALUE_HAS_BEEN_PROPAGATED_FLAG);
}
@Override
public boolean returnValueHasBeenPropagated() {
return isFlagSet(RETURN_VALUE_HAS_BEEN_PROPAGATED_FLAG);
}
@SuppressWarnings("ReferenceEquality")
public boolean isEffectivelyDefault() {
DefaultMethodOptimizationInfo top = DefaultMethodOptimizationInfo.getInstance();
return argumentInfos == top.getArgumentInfos()
&& initializedClassesOnNormalExit == top.getInitializedClassesOnNormalExit()
&& returnedArgument == top.getReturnedArgument()
&& abstractFunction == top.getAbstractFunction()
&& abstractReturnValue == top.getAbstractReturnValue()
&& classInlinerConstraint == top.getClassInlinerMethodConstraint()
&& convertCheckNotNull == top.isConvertCheckNotNull()
&& enumUnboxerMethodClassification == top.getEnumUnboxerMethodClassification()
&& dynamicType == top.getDynamicType()
&& inlining == InlinePreference.Default
&& isReturnValueUsed == top.isReturnValueUsed()
&& bridgeInfo == top.getBridgeInfo()
&& instanceInitializerInfoCollection.isEmpty()
&& nonNullParamOrThrow == top.getNonNullParamOrThrow()
&& nonNullParamOnNormalExits == top.getNonNullParamOnNormalExits()
&& nopInliningConstraint == top.getNopInliningConstraint()
&& simpleInliningConstraint == top.getSimpleInliningConstraint()
&& maxRemovedAndroidLogLevel == top.getMaxRemovedAndroidLogLevel()
&& parametersWithBitwiseOperations == top.getParametersWithBitwiseOperations()
&& unusedArguments == top.getUnusedArguments()
&& flags == DEFAULT_FLAGS;
}
@Override
public boolean isMutableOptimizationInfo() {
return true;
}
@Override
public MutableMethodOptimizationInfo toMutableOptimizationInfo() {
return this;
}
@Override
public MutableMethodOptimizationInfo asMutableMethodOptimizationInfo() {
return this;
}
public MutableMethodOptimizationInfo mutableCopy() {
return new MutableMethodOptimizationInfo(this);
}
}