blob: 0495bba75848d86d8545d69a40c8d429c15929dd [file] [log] [blame]
// Copyright (c) 2019, 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.info;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.If.Type;
import com.android.tools.r8.ir.code.InstanceGet;
import com.android.tools.r8.ir.code.InstancePut;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
import com.android.tools.r8.ir.code.Return;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.utils.Pair;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public final class ParameterUsagesInfo {
private ImmutableList<ParameterUsage> parametersUsages;
public ParameterUsagesInfo(List<ParameterUsage> usages) {
assert !usages.isEmpty();
parametersUsages = ImmutableList.copyOf(usages);
assert parametersUsages.size() ==
parametersUsages.stream().map(usage -> usage.index).collect(Collectors.toSet()).size();
}
ParameterUsage getParameterUsage(int parameter) {
for (ParameterUsage usage : parametersUsages) {
if (usage.index == parameter) {
return usage;
}
}
return null;
}
public final static class ParameterUsage {
public final int index;
public final Set<Type> ifZeroTest;
public final List<Pair<Invoke.Type, DexMethod>> callsReceiver;
// If a field of this argument is assigned: arg.f = x.
public final boolean hasFieldAssignment;
// If a field of this argument is read: x = arg.f.
public final boolean hasFieldRead;
// If this argument is assigned to a field: x.f = arg.
public final boolean isAssignedToField;
// If this argument is returned: return arg.
public final boolean isReturned;
ParameterUsage(
int index,
Set<Type> ifZeroTest,
List<Pair<Invoke.Type, DexMethod>> callsReceiver,
boolean hasFieldAssignment,
boolean hasFieldRead,
boolean isAssignedToField,
boolean isReturned) {
this.index = index;
this.ifZeroTest =
ifZeroTest.isEmpty() ? Collections.emptySet() : ImmutableSet.copyOf(ifZeroTest);
this.callsReceiver =
callsReceiver.isEmpty() ? Collections.emptyList() : ImmutableList.copyOf(callsReceiver);
this.hasFieldAssignment = hasFieldAssignment;
this.hasFieldRead = hasFieldRead;
this.isAssignedToField = isAssignedToField;
this.isReturned = isReturned;
}
public boolean notUsed() {
return ifZeroTest == null
&& callsReceiver == null
&& !hasFieldAssignment
&& !hasFieldRead
&& !isAssignedToField
&& !isReturned;
}
}
public static class ParameterUsageBuilder {
private final int index;
private final Value arg;
private final Set<Type> ifZeroTestTypes = new HashSet<>();
private final List<Pair<Invoke.Type, DexMethod>> callsOnReceiver = new ArrayList<>();
private boolean hasFieldAssignment = false;
private boolean hasFieldRead = false;
private boolean isAssignedToField = false;
private boolean isReturned = false;
public ParameterUsageBuilder(Value arg, int index) {
this.arg = arg;
this.index = index;
}
// Returns false if the instruction is not supported.
public boolean note(Instruction instruction) {
if (instruction.isIf()) {
return note(instruction.asIf());
}
if (instruction.isInstanceGet()) {
return note(instruction.asInstanceGet());
}
if (instruction.isInstancePut()) {
return note(instruction.asInstancePut());
}
if (instruction.isInvokeMethodWithReceiver()) {
return note(instruction.asInvokeMethodWithReceiver());
}
if (instruction.isReturn()) {
return note(instruction.asReturn());
}
return false;
}
public ParameterUsage build() {
return new ParameterUsage(
index,
ifZeroTestTypes,
callsOnReceiver,
hasFieldAssignment,
hasFieldRead,
isAssignedToField,
isReturned);
}
private boolean note(If ifInstruction) {
if (ifInstruction.asIf().isZeroTest()) {
assert ifInstruction.inValues().size() == 1 && ifInstruction.inValues().get(0) == arg;
ifZeroTestTypes.add(ifInstruction.asIf().getType());
return true;
}
return false;
}
private boolean note(InstanceGet instanceGetInstruction) {
assert arg != instanceGetInstruction.outValue();
if (instanceGetInstruction.object() == arg) {
hasFieldRead = true;
return true;
}
return false;
}
private boolean note(InstancePut instancePutInstruction) {
assert arg != instancePutInstruction.outValue();
if (instancePutInstruction.object() == arg) {
hasFieldAssignment = true;
isAssignedToField |= instancePutInstruction.value() == arg;
return true;
}
if (instancePutInstruction.value() == arg) {
isAssignedToField = true;
return true;
}
return false;
}
private boolean note(InvokeMethodWithReceiver invokeInstruction) {
if (invokeInstruction.inValues().lastIndexOf(arg) == 0) {
callsOnReceiver.add(
new Pair<>(
invokeInstruction.asInvokeMethodWithReceiver().getType(),
invokeInstruction.asInvokeMethodWithReceiver().getInvokedMethod()));
return true;
}
return false;
}
private boolean note(Return returnInstruction) {
assert returnInstruction.inValues().size() == 1 && returnInstruction.inValues().get(0) == arg;
isReturned = true;
return true;
}
}
}