blob: f01abfcbbae5779e2dcc5e23cb55a543d5b16b4e [file] [log] [blame]
// Copyright (c) 2021, 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.enums.classification;
import static com.android.tools.r8.ir.code.Opcodes.ASSUME;
import static com.android.tools.r8.ir.code.Opcodes.IF;
import static com.android.tools.r8.ir.code.Opcodes.RETURN;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.Argument;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.If.Type;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionIterator;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.Set;
public class EnumUnboxerMethodClassificationAnalysis {
/**
* Simple analysis that classifies the given method using {@link
* CheckNotNullEnumUnboxerMethodClassification} if the method is static and has a parameter of
* type Object, which has a single if-zero user.
*/
public static EnumUnboxerMethodClassification analyze(
AppView<AppInfoWithLiveness> appView,
ProgramMethod method,
IRCode code,
MethodProcessor methodProcessor) {
if (!appView.options().enableEnumUnboxing) {
// The classification is unused when enum unboxing is disabled.
return EnumUnboxerMethodClassification.unknown();
}
if (!method.getAccessFlags().isStatic() || method.getParameters().isEmpty()) {
// Not classified for enum unboxing.
return EnumUnboxerMethodClassification.unknown();
}
// Look for an argument with a single if-zero user.
DexItemFactory dexItemFactory = appView.dexItemFactory();
InstructionIterator entryIterator = code.entryBlock().iterator();
for (int index = 0; index < method.getParameters().size(); index++) {
Argument argument = entryIterator.next().asArgument();
DexType parameter = method.getParameter(index);
if (parameter != dexItemFactory.objectType) {
continue;
}
if (onlyHasCheckNotNullUsers(argument, methodProcessor)) {
return new CheckNotNullEnumUnboxerMethodClassification(index);
}
}
return EnumUnboxerMethodClassification.unknown();
}
private static boolean onlyHasCheckNotNullUsers(
Argument argument, MethodProcessor methodProcessor) {
// Check that the argument has an if-zero user and a return user (optional).
Value value = argument.outValue();
if (value.hasDebugUsers() || value.hasPhiUsers()) {
return false;
}
Set<Instruction> users = value.aliasedUsers();
boolean seenIf = false;
for (Instruction user : users) {
switch (user.opcode()) {
case ASSUME:
if (user.outValue().hasDebugUsers() || user.outValue().hasPhiUsers()) {
return false;
}
break;
case IF:
{
If ifUser = user.asIf();
if (!ifUser.isZeroTest()
|| (ifUser.getType() != Type.EQ && ifUser.getType() != Type.NE)) {
return false;
}
seenIf = true;
break;
}
case RETURN:
break;
default:
return false;
}
}
// During the primary optimization pass, we require seeing an if instruction (to limit the
// amount of method duplication). For monotonicity, we do not require seeing an if instruction
// in the subsequent optimization passes, since the if instruction that lead us to return a
// CheckNotNullEnumUnboxerMethodClassification may be eliminated by (for example) the call site
// optimization.
return !methodProcessor.isPrimaryMethodProcessor() || seenIf;
}
}