Classify methods as checkNotNull() methods for enum unboxing
This adds a new EnumUnboxerMethodClassification to the MethodOptimizationInfo.
A given method is classified as a checkNotNull() method by the enum unboxer if it is a static method with a parameter of type java.lang.Object, which has a single if-zero user.
Follow up work will use this piece of optimization info to allow unboxing of enums with checkNotNull() users.
Bug: 192037990
Change-Id: I9024de37c46da742be63ac8d412fde458206e488
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
index 9dfe2e3..13b7140 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
+import com.android.tools.r8.ir.optimize.enums.classification.EnumUnboxerMethodClassification;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -62,6 +63,9 @@
void setClassInlinerMethodConstraint(
ProgramMethod method, ClassInlinerMethodConstraint classInlinerConstraint);
+ void setEnumUnboxerMethodClassification(
+ ProgramMethod method, EnumUnboxerMethodClassification enumUnboxerMethodClassification);
+
void setInstanceInitializerInfoCollection(
DexEncodedMethod method, InstanceInitializerInfoCollection instanceInitializerInfoCollection);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/CheckNotNullEnumUnboxerMethodClassification.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/CheckNotNullEnumUnboxerMethodClassification.java
new file mode 100644
index 0000000..239a123
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/CheckNotNullEnumUnboxerMethodClassification.java
@@ -0,0 +1,29 @@
+// 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;
+
+public final class CheckNotNullEnumUnboxerMethodClassification
+ extends EnumUnboxerMethodClassification {
+
+ private int argumentIndex;
+
+ CheckNotNullEnumUnboxerMethodClassification(int argumentIndex) {
+ this.argumentIndex = argumentIndex;
+ }
+
+ public int getArgumentIndex() {
+ return argumentIndex;
+ }
+
+ @Override
+ public boolean isCheckNotNullClassification() {
+ return true;
+ }
+
+ @Override
+ public CheckNotNullEnumUnboxerMethodClassification asCheckNotNullClassification() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/EnumUnboxerMethodClassification.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/EnumUnboxerMethodClassification.java
new file mode 100644
index 0000000..2b34291
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/EnumUnboxerMethodClassification.java
@@ -0,0 +1,30 @@
+// 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;
+
+public abstract class EnumUnboxerMethodClassification {
+
+ public static UnknownEnumUnboxerMethodClassification unknown() {
+ return UnknownEnumUnboxerMethodClassification.getInstance();
+ }
+
+ public EnumUnboxerMethodClassification fixupAfterRemovingThisParameter() {
+ // Only static methods are currently classified by the enum unboxer.
+ assert isUnknownClassification();
+ return unknown();
+ }
+
+ public boolean isCheckNotNullClassification() {
+ return false;
+ }
+
+ public CheckNotNullEnumUnboxerMethodClassification asCheckNotNullClassification() {
+ return null;
+ }
+
+ public boolean isUnknownClassification() {
+ return false;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/EnumUnboxerMethodClassificationAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/EnumUnboxerMethodClassificationAnalysis.java
new file mode 100644
index 0000000..a608d51
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/EnumUnboxerMethodClassificationAnalysis.java
@@ -0,0 +1,74 @@
+// 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 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.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) {
+ 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++) {
+ DexType parameter = method.getParameter(index);
+ if (parameter != dexItemFactory.objectType) {
+ continue;
+ }
+
+ Argument argument = entryIterator.next().asArgument();
+ if (hasSingleIfZeroUser(argument)) {
+ return new CheckNotNullEnumUnboxerMethodClassification(index);
+ }
+ }
+
+ return EnumUnboxerMethodClassification.unknown();
+ }
+
+ private static boolean hasSingleIfZeroUser(Argument argument) {
+ Value value = argument.outValue();
+ if (value.hasDebugUsers() || value.hasPhiUsers()) {
+ return false;
+ }
+ Set<Instruction> users = value.uniqueUsers();
+ if (users.size() != 1) {
+ return false;
+ }
+ Instruction user = users.iterator().next();
+ if (!user.isIf()) {
+ return false;
+ }
+ If ifUser = user.asIf();
+ return ifUser.isZeroTest() && (ifUser.getType() == Type.EQ || ifUser.getType() == Type.NE);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/UnknownEnumUnboxerMethodClassification.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/UnknownEnumUnboxerMethodClassification.java
new file mode 100644
index 0000000..9f51d0a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/UnknownEnumUnboxerMethodClassification.java
@@ -0,0 +1,22 @@
+// 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;
+
+public final class UnknownEnumUnboxerMethodClassification extends EnumUnboxerMethodClassification {
+
+ private static final UnknownEnumUnboxerMethodClassification INSTANCE =
+ new UnknownEnumUnboxerMethodClassification();
+
+ private UnknownEnumUnboxerMethodClassification() {}
+
+ static UnknownEnumUnboxerMethodClassification getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public boolean isUnknownClassification() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
index a98bc38..2d8ff77 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
+import com.android.tools.r8.ir.optimize.enums.classification.EnumUnboxerMethodClassification;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
import com.android.tools.r8.ir.optimize.info.initializer.DefaultInstanceInitializerInfo;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
@@ -65,6 +66,11 @@
}
@Override
+ public EnumUnboxerMethodClassification getEnumUnboxerMethodClassification() {
+ return EnumUnboxerMethodClassification.unknown();
+ }
+
+ @Override
public TypeElement getDynamicUpperBoundType() {
return UNKNOWN_TYPE;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
index a25eabb..4de895c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
+import com.android.tools.r8.ir.optimize.enums.classification.EnumUnboxerMethodClassification;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
import com.android.tools.r8.utils.InternalOptions;
@@ -36,6 +37,8 @@
public abstract ClassInlinerMethodConstraint getClassInlinerMethodConstraint();
+ public abstract EnumUnboxerMethodClassification getEnumUnboxerMethodClassification();
+
public abstract TypeElement getDynamicUpperBoundType();
public final TypeElement getDynamicUpperBoundTypeOrElse(TypeElement orElse) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index 15afa8c..8755235 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -86,6 +86,8 @@
import com.android.tools.r8.ir.optimize.DynamicTypeOptimization;
import com.android.tools.r8.ir.optimize.classinliner.analysis.ClassInlinerMethodConstraintAnalysis;
import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
+import com.android.tools.r8.ir.optimize.enums.classification.EnumUnboxerMethodClassification;
+import com.android.tools.r8.ir.optimize.enums.classification.EnumUnboxerMethodClassificationAnalysis;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeAnalyzer;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
@@ -137,6 +139,7 @@
identifyInvokeSemanticsForInlining(definition, code, feedback, timing);
}
computeClassInlinerMethodConstraint(method, code, feedback, timing);
+ computeEnumUnboxerMethodClassification(method, code, feedback, timing);
computeSimpleInliningConstraint(method, code, feedback, timing);
computeDynamicReturnType(dynamicTypeOptimization, feedback, definition, code, timing);
computeInitializedClassesOnNormalExit(feedback, definition, code, timing);
@@ -783,6 +786,20 @@
feedback.setClassInlinerMethodConstraint(method, classInlinerMethodConstraint);
}
+ private void computeEnumUnboxerMethodClassification(
+ ProgramMethod method, IRCode code, OptimizationFeedback feedback, Timing timing) {
+ timing.begin("Compute enum unboxer method classification");
+ computeEnumUnboxerMethodClassification(method, code, feedback);
+ timing.end();
+ }
+
+ private void computeEnumUnboxerMethodClassification(
+ ProgramMethod method, IRCode code, OptimizationFeedback feedback) {
+ EnumUnboxerMethodClassification enumUnboxerMethodClassification =
+ EnumUnboxerMethodClassificationAnalysis.analyze(appView, method, code);
+ feedback.setEnumUnboxerMethodClassification(method, enumUnboxerMethodClassification);
+ }
+
private void computeSimpleInliningConstraint(
ProgramMethod method, IRCode code, OptimizationFeedback feedback, Timing timing) {
if (appView.options().enableSimpleInliningConstraints) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
index 7a0dae1..f50c71a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
@@ -20,6 +20,7 @@
import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
+import com.android.tools.r8.ir.optimize.enums.classification.EnumUnboxerMethodClassification;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
@@ -41,6 +42,8 @@
DefaultMethodOptimizationInfo.UNKNOWN_ABSTRACT_RETURN_VALUE;
private ClassInlinerMethodConstraint classInlinerConstraint =
ClassInlinerMethodConstraint.alwaysFalse();
+ private EnumUnboxerMethodClassification enumUnboxerMethodClassification =
+ EnumUnboxerMethodClassification.unknown();
private TypeElement returnsObjectWithUpperBoundType = DefaultMethodOptimizationInfo.UNKNOWN_TYPE;
private ClassTypeElement returnsObjectWithLowerBoundType =
DefaultMethodOptimizationInfo.UNKNOWN_CLASS_TYPE;
@@ -154,6 +157,7 @@
nonNullParamOrThrow = template.nonNullParamOrThrow;
nonNullParamOnNormalExits = template.nonNullParamOnNormalExits;
classInlinerConstraint = template.classInlinerConstraint;
+ enumUnboxerMethodClassification = template.enumUnboxerMethodClassification;
apiReferenceLevel = template.apiReferenceLevel;
}
@@ -246,6 +250,16 @@
}
@Override
+ public EnumUnboxerMethodClassification getEnumUnboxerMethodClassification() {
+ return enumUnboxerMethodClassification;
+ }
+
+ void setEnumUnboxerMethodClassification(
+ EnumUnboxerMethodClassification enumUnboxerMethodClassification) {
+ this.enumUnboxerMethodClassification = enumUnboxerMethodClassification;
+ }
+
+ @Override
public TypeElement getDynamicUpperBoundType() {
return returnsObjectWithUpperBoundType;
}
@@ -555,6 +569,8 @@
public void adjustOptimizationInfoAfterRemovingThisParameter(
AppView<AppInfoWithLiveness> appView) {
classInlinerConstraint = classInlinerConstraint.fixupAfterRemovingThisParameter();
+ enumUnboxerMethodClassification =
+ enumUnboxerMethodClassification.fixupAfterRemovingThisParameter();
simpleInliningConstraint =
simpleInliningConstraint.fixupAfterRemovingThisParameter(
appView.simpleInliningConstraintFactory());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
index dd781e8..c9390d7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
+import com.android.tools.r8.ir.optimize.enums.classification.EnumUnboxerMethodClassification;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -264,6 +265,13 @@
}
@Override
+ public synchronized void setEnumUnboxerMethodClassification(
+ ProgramMethod method, EnumUnboxerMethodClassification enumUnboxerMethodClassification) {
+ getMethodOptimizationInfoForUpdating(method)
+ .setEnumUnboxerMethodClassification(enumUnboxerMethodClassification);
+ }
+
+ @Override
public synchronized void setInstanceInitializerInfoCollection(
DexEncodedMethod method,
InstanceInitializerInfoCollection instanceInitializerInfoCollection) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
index e862824..623d8d2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
+import com.android.tools.r8.ir.optimize.enums.classification.EnumUnboxerMethodClassification;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -117,6 +118,10 @@
ProgramMethod method, ClassInlinerMethodConstraint classInlinerConstraint) {}
@Override
+ public void setEnumUnboxerMethodClassification(
+ ProgramMethod method, EnumUnboxerMethodClassification enumUnboxerMethodClassification) {}
+
+ @Override
public void setInstanceInitializerInfoCollection(
DexEncodedMethod method,
InstanceInitializerInfoCollection instanceInitializerInfoCollection) {}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
index 5c39d40..b248a6d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
+import com.android.tools.r8.ir.optimize.enums.classification.EnumUnboxerMethodClassification;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -169,6 +170,12 @@
}
@Override
+ public void setEnumUnboxerMethodClassification(
+ ProgramMethod method, EnumUnboxerMethodClassification enumUnboxerMethodClassification) {
+ // Ignored.
+ }
+
+ @Override
public void setInstanceInitializerInfoCollection(
DexEncodedMethod method,
InstanceInitializerInfoCollection instanceInitializerInfoCollection) {