blob: 83b1c967186ec905f98a14e7a5079a2c8f93f642 [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.optimize.numberunboxer;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.lens.NonIdentityGraphLens;
import com.android.tools.r8.graph.proto.ArgumentInfo;
import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
import com.android.tools.r8.graph.proto.RewrittenTypeInfo;
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.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Return;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.optimize.CustomLensCodeRewriter;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class NumberUnboxerRewriter implements CustomLensCodeRewriter {
private final AppView<AppInfoWithLiveness> appView;
public NumberUnboxerRewriter(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
}
@Override
public Set<Phi> rewriteCode(
IRCode code,
MethodProcessor methodProcessor,
RewrittenPrototypeDescription prototypeChanges,
NonIdentityGraphLens graphLens) {
assert graphLens.isNumberUnboxerLens();
Set<Phi> affectedPhis = Sets.newIdentityHashSet();
rewriteArgs(code, prototypeChanges, affectedPhis);
InstructionListIterator iterator = code.instructionListIterator();
while (iterator.hasNext()) {
Instruction next = iterator.next();
if (next.isInvokeMethod()) {
InvokeMethod invokeMethod = next.asInvokeMethod();
// TODO(b/314117865): This is assuming that there are no non-rebound method references.
DexMethod rewrittenMethod =
graphLens
.lookupMethod(
invokeMethod.getInvokedMethod(),
code.context().getReference(),
invokeMethod.getType(),
graphLens.getPrevious())
.getReference();
assert rewrittenMethod != null;
RewrittenPrototypeDescription rewrittenPrototypeDescription =
graphLens.lookupPrototypeChangesForMethodDefinition(
rewrittenMethod, graphLens.getPrevious());
if (!rewrittenPrototypeDescription.isEmpty()) {
unboxInvokeValues(
code, iterator, invokeMethod, rewrittenPrototypeDescription, affectedPhis);
}
} else if (next.isReturn() && next.asReturn().hasReturnValue()) {
unboxReturnIfNeeded(code, iterator, next.asReturn(), prototypeChanges);
}
}
return affectedPhis;
}
private void unboxInvokeValues(
IRCode code,
InstructionListIterator iterator,
InvokeMethod invokeMethod,
RewrittenPrototypeDescription prototypeChanges,
Set<Phi> affectedPhis) {
assert prototypeChanges.getArgumentInfoCollection().numberOfRemovedArguments() == 0;
for (int inValueIndex = 0; inValueIndex < invokeMethod.inValues().size(); inValueIndex++) {
ArgumentInfo argumentInfo =
prototypeChanges.getArgumentInfoCollection().getArgumentInfo(inValueIndex);
if (argumentInfo.isRewrittenTypeInfo()) {
Value invokeArg = invokeMethod.getArgument(inValueIndex);
InvokeVirtual unboxOperation =
computeUnboxInvokeIfNeeded(
code, invokeArg, argumentInfo.asRewrittenTypeInfo(), invokeMethod.getPosition());
if (unboxOperation != null) {
iterator.addBeforeAndPositionAfterNewInstruction(unboxOperation);
invokeMethod.replaceValue(inValueIndex, unboxOperation.outValue());
}
}
}
if (invokeMethod.hasOutValue()) {
InvokeStatic boxOperation =
computeBoxInvokeIfNeeded(
code,
invokeMethod.outValue(),
prototypeChanges.getRewrittenReturnInfo(),
invokeMethod.getPosition());
if (boxOperation != null) {
iterator.add(boxOperation);
affectedPhis.addAll(boxOperation.outValue().uniquePhiUsers());
}
}
}
private void unboxReturnIfNeeded(
IRCode code,
InstructionListIterator iterator,
Return ret,
RewrittenPrototypeDescription prototypeChanges) {
InvokeVirtual unbox =
computeUnboxInvokeIfNeeded(
code, ret.returnValue(), prototypeChanges.getRewrittenReturnInfo(), ret.getPosition());
if (unbox != null) {
iterator.addBeforeAndPositionAfterNewInstruction(unbox);
ret.replaceValue(ret.returnValue(), unbox.outValue());
}
}
private InvokeVirtual computeUnboxInvokeIfNeeded(
IRCode code, Value input, RewrittenTypeInfo rewrittenTypeInfo, Position pos) {
if (rewrittenTypeInfo == null) {
return null;
}
assert rewrittenTypeInfo.getOldType().isReferenceType();
assert rewrittenTypeInfo.getNewType().isPrimitiveType();
assert appView.dexItemFactory().primitiveToBoxed.containsValue(rewrittenTypeInfo.getOldType());
return InvokeVirtual.builder()
.setMethod(appView.dexItemFactory().getUnboxPrimitiveMethod(rewrittenTypeInfo.getNewType()))
.setFreshOutValue(
code.valueNumberGenerator, rewrittenTypeInfo.getNewType().toTypeElement(appView))
.setArguments(ImmutableList.of(input))
.setPosition(pos)
.build();
}
private InvokeStatic computeBoxInvokeIfNeeded(
IRCode code, Value output, RewrittenTypeInfo rewrittenTypeInfo, Position pos) {
if (rewrittenTypeInfo == null) {
return null;
}
assert rewrittenTypeInfo.getOldType().isReferenceType();
assert rewrittenTypeInfo.getNewType().isPrimitiveType();
assert appView.dexItemFactory().primitiveToBoxed.containsValue(rewrittenTypeInfo.getOldType());
Value outValue = code.createValue(rewrittenTypeInfo.getOldType().toTypeElement(appView));
output.replaceUsers(outValue);
return InvokeStatic.builder()
.setOutValue(outValue)
.setMethod(appView.dexItemFactory().getBoxPrimitiveMethod(rewrittenTypeInfo.getNewType()))
.setSingleArgument(output)
.setPosition(pos)
.build();
}
private void rewriteArgs(
IRCode code, RewrittenPrototypeDescription prototypeChanges, Set<Phi> affectedPhis) {
List<InvokeStatic> boxingOperations = new ArrayList<>();
InstructionListIterator iterator = code.entryBlock().listIterator(code);
Position pos = iterator.peekNext().getPosition();
assert prototypeChanges.getArgumentInfoCollection().numberOfRemovedArguments() == 0;
int originalNumberOfArguments = code.getNumberOfArguments();
for (int argumentIndex = 0; argumentIndex < originalNumberOfArguments; argumentIndex++) {
ArgumentInfo argumentInfo =
prototypeChanges.getArgumentInfoCollection().getArgumentInfo(argumentIndex);
Instruction next = iterator.next();
assert next.isArgument();
if (argumentInfo.isRewrittenTypeInfo()) {
InvokeStatic boxOperation =
computeBoxInvokeIfNeeded(
code, next.outValue(), argumentInfo.asRewrittenTypeInfo(), pos);
if (boxOperation != null) {
boxingOperations.add(boxOperation);
}
}
}
assert !iterator.peekNext().isArgument();
for (InvokeStatic boxingOp : boxingOperations) {
iterator.add(boxingOp);
affectedPhis.addAll(boxingOp.outValue().uniquePhiUsers());
}
}
}