blob: 99f92eb18611f3f232d64fedae32399529283214 [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.code;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.utils.DequeUtils;
import com.google.common.collect.Sets;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
public class IRCodeUtils {
public static InvokeDirect getUniqueConstructorInvoke(
Value value, DexItemFactory dexItemFactory) {
InvokeDirect result = null;
for (Instruction user : value.uniqueUsers()) {
if (user.isInvokeDirect()) {
InvokeDirect invoke = user.asInvokeDirect();
if (!dexItemFactory.isConstructor(invoke.getInvokedMethod())) {
continue;
}
if (invoke.getReceiver() != value) {
continue;
}
if (result != null) {
// Does not have a unique constructor invoke.
return null;
}
result = invoke;
}
}
return result;
}
/**
* Finds the single assignment to the fields in {@param fields} in {@param code}. Note that this
* does not guarantee that the assignments found dominate all the normal exits.
*/
public static Map<DexEncodedField, StaticPut> findUniqueStaticPuts(
AppView<? extends AppInfoWithClassHierarchy> appView,
IRCode code,
Set<DexEncodedField> fields) {
Set<DexEncodedField> writtenMoreThanOnce = Sets.newIdentityHashSet();
Map<DexEncodedField, StaticPut> uniqueStaticPuts = new IdentityHashMap<>();
for (StaticPut staticPut : code.<StaticPut>instructions(Instruction::isStaticPut)) {
DexEncodedField field =
appView.appInfo().resolveField(staticPut.getField()).getResolvedField();
if (field == null || !fields.contains(field) || writtenMoreThanOnce.contains(field)) {
continue;
}
if (uniqueStaticPuts.put(field, staticPut) != null) {
writtenMoreThanOnce.add(field);
uniqueStaticPuts.remove(field);
}
}
return uniqueStaticPuts;
}
/**
* Removes {@param instruction} if it is a {@link NewArrayEmpty} instruction, which only has
* array-put users. Also removes all instructions that contribute to the computation of the
* indices and the elements, if they end up being unused, even if the instructions may have side
* effects (!).
*
* <p>Use with caution!
*/
public static void removeArrayAndTransitiveInputsIfNotUsed(IRCode code, Instruction definition) {
if (definition.isConstNumber()) {
// No need to explicitly remove `null`, it will be removed by ordinary dead code elimination
// anyway.
assert definition.asConstNumber().isZero();
return;
}
Value arrayValue = definition.outValue();
if (arrayValue.hasPhiUsers() || arrayValue.hasDebugUsers()) {
return;
}
if (!definition.isNewArrayEmptyOrInvokeNewArray()) {
assert false;
return;
}
Deque<InstructionOrPhi> worklist = new ArrayDeque<>();
InvokeNewArray invokeNewArray = definition.asInvokeNewArray();
if (invokeNewArray != null) {
worklist.add(definition);
} else if (definition.isNewArrayEmpty()) {
for (Instruction user : arrayValue.uniqueUsers()) {
// If we encounter an Assume instruction here, we also need to consider indirect users.
assert !user.isAssume();
if (!user.isArrayPut()) {
return;
}
worklist.add(user);
}
} else {
assert false;
}
internalRemoveInstructionAndTransitiveInputsIfNotUsed(code, worklist);
}
/**
* Removes the given instruction and all the instructions that are used to define the in-values of
* the given instruction, even if the instructions may have side effects (!).
*
* <p>Use with caution!
*/
public static void removeInstructionAndTransitiveInputsIfNotUsed(
IRCode code, Instruction instruction) {
internalRemoveInstructionAndTransitiveInputsIfNotUsed(
code, DequeUtils.newArrayDeque(instruction));
}
private static void internalRemoveInstructionAndTransitiveInputsIfNotUsed(
IRCode code, Deque<InstructionOrPhi> worklist) {
Set<InstructionOrPhi> removed = Sets.newIdentityHashSet();
while (!worklist.isEmpty()) {
InstructionOrPhi instructionOrPhi = worklist.removeFirst();
if (removed.contains(instructionOrPhi)) {
// Already removed.
continue;
}
if (instructionOrPhi.isPhi()) {
Phi current = instructionOrPhi.asPhi();
if (!current.hasUsers() && !current.hasDebugUsers()) {
boolean hasOtherPhiUserThanSelf = false;
for (Phi phiUser : current.uniquePhiUsers()) {
if (phiUser != current) {
hasOtherPhiUserThanSelf = true;
break;
}
}
if (!hasOtherPhiUserThanSelf) {
current.removeDeadPhi();
for (Value operand : current.getOperands()) {
worklist.add(operand.isPhi() ? operand.asPhi() : operand.definition);
}
removed.add(current);
}
}
} else {
Instruction current = instructionOrPhi.asInstruction();
if (!current.hasOutValue() || !current.outValue().hasAnyUsers()) {
current.getBlock().listIterator(code, current).removeOrReplaceByDebugLocalRead();
for (Value inValue : current.inValues()) {
worklist.add(inValue.isPhi() ? inValue.asPhi() : inValue.definition);
}
removed.add(current);
}
}
}
}
}