| // 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.DexItemFactory; |
| 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.InvokeStatic; |
| import com.android.tools.r8.ir.code.Monitor; |
| import com.android.tools.r8.ir.code.Return; |
| import com.android.tools.r8.ir.code.Value; |
| import com.android.tools.r8.utils.ListUtils; |
| 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 index) { |
| for (ParameterUsage usage : parametersUsages) { |
| if (usage.index == index) { |
| return usage; |
| } |
| } |
| return null; |
| } |
| |
| ParameterUsagesInfo remove(int index) { |
| assert parametersUsages.size() > 0; |
| assert 0 <= index && index <= ListUtils.last(parametersUsages).index; |
| ImmutableList.Builder<ParameterUsage> builder = ImmutableList.builder(); |
| for (ParameterUsage usage : parametersUsages) { |
| // Once we remove or pass the designated index, copy-n-shift the remaining usages. |
| if (index < usage.index) { |
| builder.add(ParameterUsage.copyAndShift(usage, 1)); |
| } else if (index == usage.index) { |
| // Do not add the parameter usage with the matched index. |
| } else { |
| // Until we meet the `parameter` of interest, keep copying. |
| assert usage.index < index; |
| builder.add(usage); |
| } |
| } |
| ImmutableList<ParameterUsage> adjustedUsages = builder.build(); |
| if (adjustedUsages.isEmpty()) { |
| return DefaultMethodOptimizationInfo.UNKNOWN_PARAMETER_USAGE_INFO; |
| } |
| return new ParameterUsagesInfo(adjustedUsages); |
| } |
| |
| 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; |
| |
| // If this argument is used in a monitor instruction. |
| public final boolean isUsedInMonitor; |
| |
| ParameterUsage( |
| int index, |
| Set<Type> ifZeroTest, |
| List<Pair<Invoke.Type, DexMethod>> callsReceiver, |
| boolean hasFieldAssignment, |
| boolean hasFieldRead, |
| boolean isAssignedToField, |
| boolean isReturned, |
| boolean isUsedInMonitor) { |
| 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; |
| this.isUsedInMonitor = isUsedInMonitor; |
| } |
| |
| static ParameterUsage copyAndShift(ParameterUsage original, int shift) { |
| assert original.index >= shift; |
| return new ParameterUsage( |
| original.index - shift, |
| original.ifZeroTest, |
| original.callsReceiver, |
| original.hasFieldAssignment, |
| original.hasFieldRead, |
| original.isAssignedToField, |
| original.isReturned, |
| original.isUsedInMonitor); |
| } |
| |
| public boolean notUsed() { |
| return (ifZeroTest == null || ifZeroTest.isEmpty()) |
| && (callsReceiver == null || callsReceiver.isEmpty()) |
| && !hasFieldAssignment |
| && !hasFieldRead |
| && !isAssignedToField |
| && !isReturned |
| && !isUsedInMonitor; |
| } |
| } |
| |
| public static class ParameterUsageBuilder { |
| |
| private final int index; |
| private final Value arg; |
| private final DexItemFactory dexItemFactory; |
| |
| 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; |
| private boolean isUsedInMonitor = false; |
| |
| ParameterUsageBuilder(Value arg, int index, DexItemFactory dexItemFactory) { |
| this.arg = arg; |
| this.index = index; |
| this.dexItemFactory = dexItemFactory; |
| } |
| |
| // Returns false if the instruction is not supported. |
| public boolean note(Instruction instruction) { |
| if (instruction.isAssume()) { |
| // Keep examining other users if there are no phi users, but the param usage builder should |
| // consider aliased users. |
| return !instruction.outValue().hasPhiUsers(); |
| } |
| 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.isInvokeStatic()) { |
| return note(instruction.asInvokeStatic()); |
| } |
| if (instruction.isReturn()) { |
| return note(instruction.asReturn()); |
| } |
| if (instruction.isMonitor()) { |
| return note(instruction.asMonitor()); |
| } |
| return false; |
| } |
| |
| public ParameterUsage build() { |
| return new ParameterUsage( |
| index, |
| ifZeroTestTypes, |
| callsOnReceiver, |
| hasFieldAssignment, |
| hasFieldRead, |
| isAssignedToField, |
| isReturned, |
| isUsedInMonitor); |
| } |
| |
| private boolean note(If ifInstruction) { |
| if (ifInstruction.asIf().isZeroTest()) { |
| assert ifInstruction.inValues().size() == 1 |
| && ifInstruction.inValues().get(0).getAliasedValue() == arg; |
| ifZeroTestTypes.add(ifInstruction.asIf().getType()); |
| return true; |
| } |
| return false; |
| } |
| |
| private boolean note(InstanceGet instanceGetInstruction) { |
| assert arg != instanceGetInstruction.outValue(); |
| if (instanceGetInstruction.object().getAliasedValue() == arg) { |
| hasFieldRead = true; |
| return true; |
| } |
| return false; |
| } |
| |
| private boolean note(InstancePut instancePutInstruction) { |
| assert arg != instancePutInstruction.outValue(); |
| if (instancePutInstruction.object().getAliasedValue() == arg) { |
| hasFieldAssignment = true; |
| isAssignedToField |= instancePutInstruction.value().getAliasedValue() == arg; |
| return true; |
| } |
| if (instancePutInstruction.value().getAliasedValue() == arg) { |
| isAssignedToField = true; |
| return true; |
| } |
| return false; |
| } |
| |
| private boolean note(InvokeMethodWithReceiver invokeInstruction) { |
| if (ListUtils.lastIndexMatching( |
| invokeInstruction.inValues(), v -> v.getAliasedValue() == arg) == 0) { |
| callsOnReceiver.add( |
| new Pair<>( |
| invokeInstruction.asInvokeMethodWithReceiver().getType(), |
| invokeInstruction.asInvokeMethodWithReceiver().getInvokedMethod())); |
| return true; |
| } |
| return false; |
| } |
| |
| private boolean note(InvokeStatic invokeInstruction) { |
| if (invokeInstruction.getInvokedMethod() == dexItemFactory.objectsMethods.requireNonNull) { |
| if (!invokeInstruction.hasOutValue() || !invokeInstruction.outValue().hasAnyUsers()) { |
| ifZeroTestTypes.add(Type.EQ); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private boolean note(Return returnInstruction) { |
| assert returnInstruction.inValues().size() == 1 |
| && returnInstruction.inValues().get(0).getAliasedValue() == arg; |
| isReturned = true; |
| return true; |
| } |
| |
| private boolean note(Monitor monitorInstruction) { |
| assert monitorInstruction.inValues().size() == 1; |
| assert monitorInstruction.inValues().get(0).getAliasedValue() == arg; |
| isUsedInMonitor = true; |
| return true; |
| } |
| } |
| } |