blob: a7277a874bf731c613649f7513b56c60a4cee54b [file] [log] [blame]
// Copyright (c) 2023, 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.conversion.passes;
import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.conversion.passes.result.CodeRewriterResult;
import com.android.tools.r8.ir.optimize.AffectedValues;
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
import com.android.tools.r8.utils.BooleanBox;
import com.google.common.collect.Sets;
import java.util.ListIterator;
import java.util.Set;
public class MoveResultRewriter extends CodeRewriterPass<AppInfo> {
public MoveResultRewriter(AppView<?> appView) {
super(appView);
}
@Override
protected String getRewriterId() {
return "MoveResultRewriter";
}
@Override
protected boolean shouldRewriteCode(IRCode code, MethodProcessor methodProcessor) {
return options.isGeneratingDex() && code.metadata().mayHaveInvokeMethod();
}
// Replace result uses for methods where something is known about what is returned.
@Override
protected CodeRewriterResult rewriteCode(IRCode code) {
boolean changed = false;
boolean mayHaveRemovedTrivialPhi = false;
Set<BasicBlock> blocksToBeRemoved = Sets.newIdentityHashSet();
ListIterator<BasicBlock> blockIterator = code.listIterator();
TypeAnalysis typeAnalysis =
new TypeAnalysis(appView, code).setKeepRedundantBlocksAfterAssumeRemoval(true);
while (blockIterator.hasNext()) {
BasicBlock block = blockIterator.next();
if (blocksToBeRemoved.contains(block)) {
continue;
}
InstructionListIterator iterator = block.listIterator(code);
while (iterator.hasNext()) {
InvokeMethod invoke = iterator.next().asInvokeMethod();
if (invoke == null || !invoke.hasOutValue() || invoke.outValue().hasLocalInfo()) {
continue;
}
// Check if the invoked method is known to return one of its arguments.
DexClassAndMethod target = invoke.lookupSingleTarget(appView, code.context());
if (target == null) {
continue;
}
MethodOptimizationInfo optimizationInfo = target.getDefinition().getOptimizationInfo();
if (!optimizationInfo.returnsArgument()) {
continue;
}
int argumentIndex = optimizationInfo.getReturnedArgument();
// Replace the out value of the invoke with the argument and ignore the out value.
if (argumentIndex < 0 || !checkArgumentType(invoke, argumentIndex)) {
continue;
}
Value argument = invoke.arguments().get(argumentIndex);
Value outValue = invoke.outValue();
assert outValue.verifyCompatible(argument.outType());
// Make sure that we are only narrowing information here. Note, in cases where we cannot
// find the definition of types, computing lessThanOrEqual will return false unless it is
// object.
if (!argument.getType().lessThanOrEqual(outValue.getType(), appView)) {
continue;
}
AffectedValues affectedValues =
argument.getType().equals(outValue.getType())
? AffectedValues.empty()
: outValue.affectedValues();
mayHaveRemovedTrivialPhi |= outValue.numberOfPhiUsers() > 0;
outValue.replaceUsers(argument);
invoke.clearOutValue();
changed = true;
if (!affectedValues.isEmpty()) {
BooleanBox removedAssumeInstructionInCurrentBlock = new BooleanBox();
typeAnalysis
.setKeepRedundantBlocksAfterAssumeRemoval(true)
.narrowingWithAssumeRemoval(
affectedValues,
assume -> removedAssumeInstructionInCurrentBlock.or(assume.getBlock() == block));
if (removedAssumeInstructionInCurrentBlock.isTrue()) {
// Workaround ConcurrentModificationException.
iterator = block.listIterator(code);
}
}
}
}
AffectedValues affectedValues = new AffectedValues();
if (!blocksToBeRemoved.isEmpty()) {
code.removeBlocks(blocksToBeRemoved);
code.removeAllDeadAndTrivialPhis(affectedValues);
assert code.getUnreachableBlocks().isEmpty();
} else if (mayHaveRemovedTrivialPhi) {
code.removeAllDeadAndTrivialPhis(affectedValues);
}
typeAnalysis.narrowingWithAssumeRemoval(affectedValues);
if (changed) {
code.removeRedundantBlocks();
}
return CodeRewriterResult.hasChanged(changed);
}
private boolean checkArgumentType(InvokeMethod invoke, int argumentIndex) {
// TODO(sgjesse): Insert cast if required.
TypeElement returnType =
TypeElement.fromDexType(invoke.getInvokedMethod().proto.returnType, maybeNull(), appView);
TypeElement argumentType =
TypeElement.fromDexType(getArgumentType(invoke, argumentIndex), maybeNull(), appView);
return appView.enableWholeProgramOptimizations()
? argumentType.lessThanOrEqual(returnType, appView)
: argumentType.equals(returnType);
}
private DexType getArgumentType(InvokeMethod invoke, int argumentIndex) {
if (invoke.isInvokeStatic()) {
return invoke.getInvokedMethod().proto.parameters.values[argumentIndex];
}
if (argumentIndex == 0) {
return invoke.getInvokedMethod().holder;
}
return invoke.getInvokedMethod().proto.parameters.values[argumentIndex - 1];
}
}