blob: b284aa52df15962a4acc1eb08e3e62badb690dd1 [file] [log] [blame]
// Copyright (c) 2016, 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.code;
import static com.google.common.base.Predicates.alwaysTrue;
import com.android.tools.r8.cf.LoadStoreHelper;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.dex.code.DexInstruction;
import com.android.tools.r8.dex.code.DexMoveResult;
import com.android.tools.r8.dex.code.DexMoveResultObject;
import com.android.tools.r8.dex.code.DexMoveResultWide;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItem;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
import com.android.tools.r8.utils.BooleanUtils;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import java.util.List;
import java.util.Set;
public abstract class Invoke extends Instruction {
static final int NO_SUCH_DEX_INSTRUCTION = -1;
@SuppressWarnings("InlineMeSuggester")
protected Invoke(Value result, List<Value> arguments) {
super(result, arguments);
}
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static Invoke create(
InvokeType type, DexItem target, DexProto proto, Value result, List<Value> arguments) {
return create(type, target, proto, result, arguments, false);
}
public static Invoke create(
InvokeType type,
DexItem target,
DexProto proto,
Value result,
List<Value> arguments,
boolean itf) {
switch (type) {
case DIRECT:
return new InvokeDirect((DexMethod) target, result, arguments, itf);
case INTERFACE:
return new InvokeInterface((DexMethod) target, result, arguments);
case STATIC:
return new InvokeStatic((DexMethod) target, result, arguments, itf);
case SUPER:
return new InvokeSuper((DexMethod) target, result, arguments, itf);
case VIRTUAL:
return new InvokeVirtual((DexMethod) target, result, arguments);
case NEW_ARRAY:
return new NewArrayFilled((DexType) target, result, arguments);
case MULTI_NEW_ARRAY:
return new InvokeMultiNewArray((DexType) target, result, arguments);
case CUSTOM:
throw new Unreachable("Use InvokeCustom constructor instead");
case POLYMORPHIC:
return new InvokePolymorphic((DexMethod) target, proto, result, arguments);
}
throw new Unreachable("Unknown invoke type: " + type);
}
public abstract InvokeType getType();
abstract public DexType getReturnType();
public boolean hasArguments() {
return !arguments().isEmpty();
}
@SuppressWarnings("ReferenceEquality")
public boolean hasReturnTypeVoid(DexItemFactory factory) {
return getReturnType() == factory.voidType;
}
public List<Value> arguments() {
return inValues;
}
public Value getArgument(int index) {
assert index < arguments().size();
return arguments().get(index);
}
public Value getArgumentForParameter(int index) {
int offset = BooleanUtils.intValue(!isInvokeStatic());
return getArgument(index + offset);
}
public Value getFirstArgument() {
return getArgument(0);
}
public Value getSecondArgument() {
return getArgument(1);
}
public Value getThirdArgument() {
return getArgument(2);
}
public Value getLastArgument() {
return getArgument(arguments().size() - 1);
}
public int requiredArgumentRegisters() {
int registers = 0;
for (Value inValue : inValues) {
registers += inValue.requiredRegisters();
}
return registers;
}
protected int fillArgumentRegisters(DexBuilder builder, int[] registers) {
assert !needsRangedInvoke(builder);
int i = 0;
for (Value value : arguments()) {
// If one of the arguments to the invoke instruction is an argument of the enclosing method
// that has been spilled at this location, then we need to take the argument from its
// original input register (because the register allocator never inserts moves from an
// argument register to a spill register). Note that this is only a problem if an argument
// has been spilled to a register that is not the argument's original register.
//
// For simplicity, we just use the original input register for all arguments if the register
// fits in 4 bits.
int register = builder.argumentOrAllocateRegister(value, getNumber());
if (register + value.requiredRegisters() - 1 > Constants.U4BIT_MAX) {
register = builder.allocatedRegister(value, getNumber());
}
assert register + value.requiredRegisters() - 1 <= Constants.U4BIT_MAX;
for (int j = 0; j < value.requiredRegisters(); j++) {
assert i < 5;
registers[i++] = register++;
}
}
return i;
}
protected boolean verifyInvokeRangeArgumentsAreConsecutive(DexBuilder builder) {
Value value = getFirstArgument();
int next = getRegisterForInvokeRange(builder, value) + value.requiredRegisters();
for (int i = 1; i < arguments().size(); i++) {
value = getArgument(i);
assert next == getRegisterForInvokeRange(builder, value);
next += value.requiredRegisters();
}
return true;
}
protected int getRegisterForInvokeRange(DexBuilder builder, Value argument) {
if (argumentsAreConsecutivePinnedInputArguments(builder.getRegisterAllocator())) {
return builder.getArgumentRegister(argument);
}
return builder.allocatedRegister(argument, getNumber());
}
protected void addInvokeAndMoveResult(DexInstruction instruction, DexBuilder builder) {
if (outValue != null && outValue.needsRegister()) {
TypeElement moveType = outValue.getType();
int register = builder.allocatedRegister(outValue, getNumber());
DexInstruction moveResult;
if (moveType.isSinglePrimitive()) {
moveResult = new DexMoveResult(register);
} else if (moveType.isWidePrimitive()) {
moveResult = new DexMoveResultWide(register);
} else if (moveType.isReferenceType()) {
moveResult = new DexMoveResultObject(register);
} else {
throw new Unreachable("Unexpected result type " + outType());
}
builder.add(this, instruction, moveResult);
} else {
builder.add(this, instruction);
}
}
@Override
public boolean couldIntroduceAnAlias(AppView<?> appView, Value root) {
assert root != null && root.getType().isReferenceType();
if (outValue == null) {
return false;
}
TypeElement outType = outValue.getType();
if (outType.isPrimitiveType()) {
return false;
}
if (appView.appInfo().hasLiveness()) {
if (outType.isClassType()
&& root.getType().isClassType()
&& appView
.appInfo()
.withLiveness()
.inDifferentHierarchy(
outType.asClassType().getClassType(),
root.getType().asClassType().getClassType())) {
return false;
}
}
return outType.isReferenceType();
}
@Override
public boolean instructionTypeCanThrow() {
return true;
}
@Override
public int maxInValueRegister() {
if (arguments().size() == 1
|| requiredArgumentRegisters() > 5
|| argumentsAreConsecutiveInputArguments()) {
return Constants.U16BIT_MAX;
}
return Constants.U4BIT_MAX;
}
private boolean argumentsAreConsecutiveInputArguments() {
return argumentsAreConsecutiveInputArgumentsThatMatches(alwaysTrue());
}
private boolean argumentsAreConsecutivePinnedInputArguments(
LinearScanRegisterAllocator registerAllocator) {
return argumentsAreConsecutiveInputArgumentsThatMatches(registerAllocator::isPinnedArgument);
}
private boolean argumentsAreConsecutiveInputArgumentsThatMatches(Predicate<Value> predicate) {
if (arguments().isEmpty()) {
return false;
}
Argument current = getFirstArgument().getDefinitionOrNull(Instruction::isArgument);
if (current == null) {
return false;
}
for (int i = 1; i < arguments().size(); i++) {
Argument next = getArgument(i).getDefinitionOrNull(Instruction::isArgument);
if (current.getNext() != next) {
return false;
}
current = next;
}
return Iterables.all(arguments(), predicate);
}
// Used to decide if this invoke should be emitted as invoke/range.
protected boolean needsRangedInvoke(DexBuilder builder) {
if (arguments().size() == 1) {
// Prefer non-range invoke since ART's LSE does not always consider invoke/range
// (b/378823200).
int registerEnd =
builder.getRegisterAllocator().getRegisterForValue(getFirstArgument(), getNumber())
+ getFirstArgument().requiredRegisters()
- 1;
return registerEnd > Constants.U4BIT_MAX;
}
if (requiredArgumentRegisters() > 5) {
// No way around using an invoke-range instruction.
return true;
}
if (argumentsAreConsecutivePinnedInputArguments(builder.getRegisterAllocator())) {
// Use the arguments from their input registers.
return true;
}
return false;
}
@Override
public int maxOutValueRegister() {
return Constants.U8BIT_MAX;
}
abstract protected String getTypeString();
@Override
public String getInstructionName() {
return "Invoke-" + getTypeString();
}
@Override
public boolean isInvoke() {
return true;
}
@Override
public Invoke asInvoke() {
return this;
}
@Override
public TypeElement evaluate(AppView<?> appView) {
DexType returnType = getReturnType();
if (returnType.isVoidType()) {
throw new Unreachable("void methods have no type.");
}
return TypeElement.fromDexType(returnType, Nullability.maybeNull(), appView);
}
@Override
public boolean outTypeKnownToBeBoolean(Set<Phi> seen) {
return getReturnType().isBooleanType();
}
/**
* Subclasses must implement load and store handling and make sure to deal with a null out-value
*/
@Override
public abstract void insertLoadAndStores(LoadStoreHelper helper);
}