blob: 9dff1d90acb88bcd8b17439db3ae6924876a8fdd [file] [log] [blame]
// Copyright (c) 2025, 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;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.proto.ArgumentInfo;
import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
import com.android.tools.r8.graph.proto.RemovedArgumentInfo;
import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
import com.android.tools.r8.graph.proto.RewrittenTypeInfo;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.SingleValue;
import com.android.tools.r8.ir.code.Argument;
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.InstructionListIterator;
import com.android.tools.r8.ir.code.MaterializingInstructionsInfo;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Position.SourcePosition;
import com.android.tools.r8.ir.code.UnusedArgument;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.AffectedValues;
import com.android.tools.r8.utils.ArrayUtils;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
public class LensCodeArgumentRewriter {
private final AppView<?> appView;
public LensCodeArgumentRewriter(AppView<?> appView) {
this.appView = appView;
}
// Applies the prototype changes of the current method to the argument instructions:
// - Replaces constant arguments by their constant value and then removes the (now unused)
// argument instruction
// - Removes unused arguments
// - Updates the type of arguments whose type has been strengthened
// TODO(b/270398965): Replace LinkedList.
@SuppressWarnings("JdkObsolete")
public void rewriteArguments(
IRCode code,
DexMethod originalMethodReference,
RewrittenPrototypeDescription prototypeChanges,
Set<Phi> affectedPhis,
Set<UnusedArgument> unusedArguments) {
AffectedValues affectedValues = new AffectedValues();
ArgumentInfoCollection argumentInfoCollection = prototypeChanges.getArgumentInfoCollection();
List<Instruction> argumentPostlude = new LinkedList<>();
int oldArgumentIndex = 0;
int nextArgumentIndex = 0;
int numberOfRemovedArguments = 0;
BasicBlock basicBlock = code.entryBlock();
InstructionListIterator instructionIterator = basicBlock.listIterator();
while (instructionIterator.hasNext()) {
Instruction instruction = instructionIterator.next();
if (!instruction.isArgument()) {
break;
}
Argument argument = instruction.asArgument();
ArgumentInfo argumentInfo = argumentInfoCollection.getArgumentInfo(oldArgumentIndex);
if (argumentInfo.isRemovedArgumentInfo()) {
rewriteRemovedArgument(
code,
instructionIterator,
originalMethodReference,
argument,
argumentInfo.asRemovedArgumentInfo(),
affectedPhis,
affectedValues,
argumentPostlude,
unusedArguments);
numberOfRemovedArguments++;
} else {
int newArgumentIndex =
argumentInfoCollection.getNewArgumentIndex(oldArgumentIndex, numberOfRemovedArguments);
Argument replacement;
if (argumentInfo.isRewrittenTypeInfo()) {
replacement =
rewriteArgumentType(
code,
argument,
argumentInfo.asRewrittenTypeInfo(),
affectedPhis,
newArgumentIndex);
argument.outValue().replaceUsers(replacement.outValue());
} else if (newArgumentIndex != oldArgumentIndex) {
replacement =
Argument.builder()
.setIndex(newArgumentIndex)
.setFreshOutValue(code, argument.getOutType(), argument.getLocalInfo())
.setPosition(argument.getPosition())
.build();
argument.outValue().replaceUsers(replacement.outValue());
} else {
replacement = argument;
}
if (newArgumentIndex == nextArgumentIndex) {
// This is the right position for the argument. Insert it into the code at this position.
if (replacement != argument) {
instructionIterator.replaceCurrentInstruction(replacement);
}
nextArgumentIndex++;
} else {
// Due the a permutation of the argument order, this argument needs to be inserted at a
// later point. Enqueue the argument into the argument postlude.
instructionIterator.removeInstructionIgnoreOutValue();
ListIterator<Instruction> argumentPostludeIterator = argumentPostlude.listIterator();
while (argumentPostludeIterator.hasNext()) {
Instruction current = argumentPostludeIterator.next();
if (!current.isArgument()
|| replacement.getIndexRaw() < current.asArgument().getIndexRaw()) {
argumentPostludeIterator.previous();
break;
}
}
argumentPostludeIterator.add(replacement);
}
}
oldArgumentIndex++;
}
instructionIterator.previous();
if (!argumentPostlude.isEmpty()) {
for (Instruction instruction : argumentPostlude) {
instructionIterator.add(instruction);
}
}
affectedValues.narrowingWithAssumeRemoval(appView, code);
}
private Argument rewriteArgumentType(
IRCode code,
Argument argument,
RewrittenTypeInfo rewrittenTypeInfo,
Set<Phi> affectedPhis,
int newArgumentIndex) {
TypeElement rewrittenType = rewrittenTypeInfo.getNewType().toTypeElement(appView);
Argument replacement =
Argument.builder()
.setIndex(newArgumentIndex)
.setFreshOutValue(code, rewrittenType, argument.getLocalInfo())
.setPosition(argument.getPosition())
.build();
affectedPhis.addAll(argument.outValue().uniquePhiUsers());
return replacement;
}
private void rewriteRemovedArgument(
IRCode code,
InstructionListIterator instructionIterator,
DexMethod originalMethodReference,
Argument argument,
RemovedArgumentInfo removedArgumentInfo,
Set<Phi> affectedPhis,
AffectedValues affectedValues,
List<Instruction> argumentPostlude,
Set<UnusedArgument> unusedArguments) {
Instruction[] replacement;
if (removedArgumentInfo.hasSingleValue()) {
SingleValue singleValue = removedArgumentInfo.getSingleValue();
TypeElement type =
removedArgumentInfo.getType().isReferenceType() && singleValue.isNull()
? TypeElement.getNull()
: removedArgumentInfo.getType().toTypeElement(appView);
Position position =
SourcePosition.builder().setLine(0).setMethod(originalMethodReference).build();
replacement =
singleValue.createMaterializingInstructions(
appView,
code,
MaterializingInstructionsInfo.create(type, argument.getLocalInfo(), position));
} else {
TypeElement unusedArgumentType = removedArgumentInfo.getType().toTypeElement(appView);
UnusedArgument unusedArgument =
UnusedArgument.builder()
.setFreshOutValue(code, unusedArgumentType)
.setPosition(Position.none())
.build();
unusedArguments.add(unusedArgument);
replacement = new Instruction[] {unusedArgument};
}
Value replacementValue = ArrayUtils.last(replacement).outValue();
argument.outValue().replaceUsers(replacementValue, affectedValues);
affectedPhis.addAll(replacementValue.uniquePhiUsers());
Collections.addAll(argumentPostlude, replacement);
instructionIterator.removeOrReplaceByDebugLocalRead();
}
}