blob: c76e842a0b4fd8804a599649a20080cdfbab5706 [file] [log] [blame]
// 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.optimize.argumentpropagation;
import com.android.tools.r8.graph.AppView;
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.TypeAnalysis;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.SingleValue;
import com.android.tools.r8.ir.code.Argument;
import com.android.tools.r8.ir.code.Assume;
import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.Sets;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
public class ArgumentPropagatorIROptimizer {
/**
* Applies the (non-trivial) argument information to the given piece of code.
*
* <p>This involves replacing usages of {@link Argument} instructions by constants, and injecting
* {@link Assume} instructions when non-trivial information is known about non-constant arguments
* such as their nullability, dynamic type, interval, etc.
*/
// TODO(b/270398965): Replace LinkedList.
@SuppressWarnings("JdkObsolete")
public static void optimize(
AppView<AppInfoWithLiveness> appView,
IRCode code,
ConcreteCallSiteOptimizationInfo optimizationInfo) {
Set<Value> affectedValues = Sets.newIdentityHashSet();
List<Assume> assumeInstructions = new LinkedList<>();
List<Instruction> instructionsToAdd = new LinkedList<>();
InstructionListIterator iterator = code.entryBlock().listIterator(code);
while (iterator.hasNext()) {
Argument argument = iterator.next().asArgument();
if (argument == null) {
break;
}
Value argumentValue = argument.asArgument().outValue();
if (argumentValue.hasLocalInfo()) {
continue;
}
// If the argument is constant, then materialize the constant and replace all uses of the
// argument by the newly materialized constant.
// TODO(b/190154391): Constant arguments should instead be removed from the enclosing method
// signature, and this should assert that the argument does not have a single materializable
// value.
AbstractValue abstractValue = optimizationInfo.getAbstractArgumentValue(argument.getIndex());
if (abstractValue.isSingleValue()) {
SingleValue singleValue = abstractValue.asSingleValue();
if (singleValue.isMaterializableInContext(appView, code.context())) {
Instruction replacement =
singleValue.createMaterializingInstruction(appView, code, argument);
replacement.setPosition(argument.getPosition());
affectedValues.addAll(argumentValue.affectedValues());
argumentValue.replaceUsers(replacement.outValue());
instructionsToAdd.add(replacement);
continue;
}
}
// If a dynamic type is known for the argument, then inject an Assume instruction with the
// dynamic type information.
// TODO(b/190154391): This should also materialize dynamic lower bound information.
// TODO(b/190154391) This should also materialize the nullability of array arguments.
if (argumentValue.getType().isReferenceType()) {
DynamicType dynamicType = optimizationInfo.getDynamicType(argument.getIndex());
if (dynamicType.isUnknown()) {
continue;
}
if (dynamicType.isBottom()) {
assert false;
continue;
}
if (dynamicType.getNullability().isDefinitelyNull()) {
ConstNumber nullInstruction = code.createConstNull();
nullInstruction.setPosition(argument.getPosition());
affectedValues.addAll(argumentValue.affectedValues());
argumentValue.replaceUsers(nullInstruction.outValue());
instructionsToAdd.add(nullInstruction);
continue;
}
if (dynamicType.isNotNullType()) {
if (!argumentValue.getType().isDefinitelyNotNull()) {
Value nonNullValue =
code.createValue(argumentValue.getType().asReferenceType().asMeetWithNotNull());
argumentValue.replaceUsers(nonNullValue, affectedValues);
Assume assumeNotNull =
Assume.createAssumeNonNullInstruction(
nonNullValue, argumentValue, argument, appView);
assumeNotNull.setPosition(argument.getPosition());
assumeInstructions.add(assumeNotNull);
}
continue;
}
DynamicTypeWithUpperBound dynamicTypeWithUpperBound =
dynamicType.asDynamicTypeWithUpperBound();
Value specializedArg;
if (dynamicTypeWithUpperBound.strictlyLessThan(argumentValue.getType(), appView)) {
specializedArg = code.createValue(argumentValue.getType());
affectedValues.addAll(argumentValue.affectedValues());
argumentValue.replaceUsers(specializedArg);
Assume assumeType =
Assume.createAssumeDynamicTypeInstruction(
dynamicTypeWithUpperBound, specializedArg, argumentValue, argument, appView);
assumeType.setPosition(argument.getPosition());
assumeInstructions.add(assumeType);
} else {
specializedArg = argumentValue;
}
assert specializedArg != null && specializedArg.getType().isReferenceType();
if (dynamicType.getNullability().isDefinitelyNotNull()) {
// If we already knew `arg` is never null, e.g., receiver, skip adding non-null.
if (!specializedArg.getType().isDefinitelyNotNull()) {
Value nonNullArg =
code.createValue(specializedArg.getType().asReferenceType().asMeetWithNotNull());
affectedValues.addAll(specializedArg.affectedValues());
specializedArg.replaceUsers(nonNullArg);
Assume assumeNotNull =
Assume.createAssumeNonNullInstruction(
nonNullArg, specializedArg, argument, appView);
assumeNotNull.setPosition(argument.getPosition());
assumeInstructions.add(assumeNotNull);
}
}
}
}
// Insert the newly created instructions after the last Argument instruction.
assert !iterator.peekPrevious().isArgument();
iterator.previous();
assert iterator.peekPrevious().isArgument();
assumeInstructions.forEach(iterator::add);
// TODO(b/190154391): Can update method signature and save more on call sites.
instructionsToAdd.forEach(iterator::add);
if (!affectedValues.isEmpty()) {
new TypeAnalysis(appView).narrowing(affectedValues);
}
}
}