Enum unboxing: Support System.identityHashCode
Bug: 166325341
Change-Id: I4ebe8ecf3c511e6722e620cf5205d277b9865845
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 2cf3328..91a70a9 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -170,6 +170,7 @@
public final DexString endsWithMethodName = createString("endsWith");
public final DexString equalsMethodName = createString("equals");
public final DexString hashCodeMethodName = createString("hashCode");
+ public final DexString identityHashCodeName = createString("identityHashCode");
public final DexString equalsIgnoreCaseMethodName = createString("equalsIgnoreCase");
public final DexString contentEqualsMethodName = createString("contentEquals");
public final DexString indexOfMethodName = createString("indexOf");
@@ -225,6 +226,7 @@
public final DexString fieldDescriptor = createString("Ljava/lang/reflect/Field;");
public final DexString methodDescriptor = createString("Ljava/lang/reflect/Method;");
public final DexString enumDescriptor = createString("Ljava/lang/Enum;");
+ public final DexString javaLangSystemDescriptor = createString("Ljava/lang/System;");
public final DexString annotationDescriptor = createString("Ljava/lang/annotation/Annotation;");
public final DexString objectsDescriptor = createString("Ljava/util/Objects;");
public final DexString collectionsDescriptor = createString("Ljava/util/Collections;");
@@ -353,7 +355,7 @@
public final DexType stringBuilderType = createStaticallyKnownType(stringBuilderDescriptor);
public final DexType stringBufferType = createStaticallyKnownType(stringBufferDescriptor);
- public final DexType javaLangSystemType = createStaticallyKnownType("Ljava/lang/System;");
+ public final DexType javaLangSystemType = createStaticallyKnownType(javaLangSystemDescriptor);
public final DexType javaIoPrintStreamType = createStaticallyKnownType("Ljava/io/PrintStream;");
public final DexType varHandleType = createStaticallyKnownType(varHandleDescriptor);
@@ -464,6 +466,7 @@
public final ClassMethods classMethods = new ClassMethods();
public final ConstructorMethods constructorMethods = new ConstructorMethods();
public final EnumMembers enumMembers = new EnumMembers();
+ public final JavaLangSystemMethods javaLangSystemMethods = new JavaLangSystemMethods();
public final NullPointerExceptionMethods npeMethods = new NullPointerExceptionMethods();
public final IllegalArgumentExceptionMethods illegalArgumentExceptionMethods =
new IllegalArgumentExceptionMethods();
@@ -1269,6 +1272,19 @@
}
}
+ public class JavaLangSystemMethods {
+ public final DexMethod identityHashCode;
+
+ private JavaLangSystemMethods() {
+ identityHashCode =
+ createMethod(
+ javaLangSystemDescriptor,
+ identityHashCodeName,
+ intDescriptor,
+ new DexString[] {objectDescriptor});
+ }
+ }
+
public class EnumMembers {
public final DexField nameField = createField(enumType, stringType, "name");
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index be6229e..73773ba 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -798,6 +798,10 @@
}
assert dexClass.isLibraryClass();
if (dexClass.type != factory.enumType) {
+ // System.identityHashCode(Object) is supported for proto enums.
+ if (singleTarget == factory.javaLangSystemMethods.identityHashCode) {
+ return Reason.ELIGIBLE;
+ }
return Reason.UNSUPPORTED_LIBRARY_CALL;
}
// TODO(b/147860220): EnumSet and EnumMap may be interesting to model.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index 087e808..9ffc360 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -33,6 +33,7 @@
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.MemberType;
@@ -176,6 +177,14 @@
convertedEnums.put(invoke, enumType);
continue;
}
+ } else if (invokedMethod == factory.javaLangSystemMethods.identityHashCode) {
+ assert invokeStatic.inValues().size() == 1;
+ Value argument = invokeStatic.getArgument(0);
+ DexType enumType = getEnumTypeOrNull(argument, convertedEnums);
+ if (enumType != null) {
+ invokeStatic.outValue().replaceUsers(argument);
+ iterator.removeOrReplaceByDebugLocalRead();
+ }
}
}
// Rewrites direct access to enum values into the corresponding int, $VALUES is not
@@ -185,7 +194,7 @@
DexType holder = staticGet.getField().holder;
if (enumsToUnbox.containsEnum(holder)) {
if (staticGet.outValue() == null) {
- iterator.removeInstructionIgnoreOutValue();
+ iterator.removeOrReplaceByDebugLocalRead();
continue;
}
EnumValueInfoMap enumValueInfoMap = enumsToUnbox.getEnumValueInfoMap(holder);
@@ -236,7 +245,7 @@
private void replaceEnumInvoke(
InstructionListIterator iterator,
- InvokeMethodWithReceiver invokeMethod,
+ InvokeMethod invokeMethod,
DexMethod method,
Function<DexMethod, DexEncodedMethod> synthesizor) {
utilityMethods.computeIfAbsent(method, synthesizor);
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/OrdinalHashCodeEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/OrdinalHashCodeEnumUnboxingTest.java
index 79f9176..1946116 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/OrdinalHashCodeEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/OrdinalHashCodeEnumUnboxingTest.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.enumunboxing;
import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import java.util.List;
@@ -43,6 +44,7 @@
.addKeepMainRule(classToTest)
.addKeepRules(enumKeepRules.getKeepRules())
.enableNeverClassInliningAnnotations()
+ .enableInliningAnnotations()
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
.allowDiagnosticInfoMessages()
.setMinApi(parameters.getApiLevel())
@@ -66,8 +68,29 @@
public static void main(String[] args) {
System.out.println(MyEnum.A.ordinal());
System.out.println(0);
- System.out.println(MyEnum.A.hashCode());
+ System.out.println(ordinal(MyEnum.A));
System.out.println(0);
+ System.out.println(ordinal(MyEnum.B));
+ System.out.println(1);
+ System.out.println(MyEnum.A.hashCode());
+ System.out.println(MyEnum.A.hashCode());
+ System.out.println(hash(MyEnum.A));
+ System.out.println(System.identityHashCode(MyEnum.A));
+ System.out.println(hash(null));
+ System.out.println(0);
+ Object o = new Object();
+ System.out.println(System.identityHashCode(o));
+ System.out.println(o.hashCode());
+ }
+
+ @NeverInline
+ private static int hash(MyEnum e) {
+ return System.identityHashCode(e);
+ }
+
+ @NeverInline
+ private static int ordinal(MyEnum e) {
+ return e.ordinal();
}
}
}