Generate optimized hashCode for primitive type with jvmTarget 1.8+

#KT-7571 Fixed
This commit is contained in:
Mikhael Bogdanov
2017-06-27 14:09:26 +02:00
parent d7e4350d42
commit 69457ef3f1
15 changed files with 293 additions and 29 deletions
@@ -27,10 +27,12 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.builtins.PrimitiveType;
import org.jetbrains.kotlin.codegen.binding.CalculatedClosure;
import org.jetbrains.kotlin.codegen.context.CodegenContext;
import org.jetbrains.kotlin.codegen.intrinsics.HashCode;
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods;
import org.jetbrains.kotlin.codegen.serialization.JvmStringTable;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
import org.jetbrains.kotlin.config.JvmTarget;
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.lexer.KtTokens;
@@ -512,7 +514,7 @@ public class AsmUtil {
});
}
static void genHashCode(MethodVisitor mv, InstructionAdapter iv, Type type) {
static void genHashCode(MethodVisitor mv, InstructionAdapter iv, Type type, JvmTarget jvmTarget) {
if (type.getSort() == Type.ARRAY) {
Type elementType = correctElementType(type);
if (elementType.getSort() == Type.OBJECT || elementType.getSort() == Type.ARRAY) {
@@ -525,16 +527,6 @@ public class AsmUtil {
else if (type.getSort() == Type.OBJECT) {
iv.invokevirtual("java/lang/Object", "hashCode", "()I", false);
}
else if (type.getSort() == Type.LONG) {
genLongHashCode(mv, iv);
}
else if (type.getSort() == Type.DOUBLE) {
iv.invokestatic("java/lang/Double", "doubleToLongBits", "(D)J", false);
genLongHashCode(mv, iv);
}
else if (type.getSort() == Type.FLOAT) {
iv.invokestatic("java/lang/Float", "floatToIntBits", "(F)I", false);
}
else if (type.getSort() == Type.BOOLEAN) {
Label end = new Label();
iv.dup();
@@ -543,8 +535,24 @@ public class AsmUtil {
iv.iconst(1);
iv.mark(end);
}
else { // byte short char int
// do nothing
else {
if (JvmTarget.JVM_1_6 == jvmTarget) {
if (type.getSort() == Type.LONG) {
genLongHashCode(mv, iv);
}
else if (type.getSort() == Type.DOUBLE) {
iv.invokestatic("java/lang/Double", "doubleToLongBits", "(D)J", false);
genLongHashCode(mv, iv);
}
else if (type.getSort() == Type.FLOAT) {
iv.invokestatic("java/lang/Float", "floatToIntBits", "(F)I", false);
}
else { // byte short char int
// do nothing
}
} else {
HashCode.Companion.invokeHashCode(iv, type);
}
}
}
@@ -573,7 +573,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
iv.ifnull(ifNull);
}
genHashCode(mv, iv, asmType);
genHashCode(mv, iv, asmType, state.getTarget());
if (ifNull != null) {
Label end = new Label();
@@ -16,22 +16,40 @@
package org.jetbrains.kotlin.codegen.intrinsics
import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.Callable
import org.jetbrains.kotlin.codegen.CallableMethod
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
class HashCode : IntrinsicMethod() {
override fun toCallable(method: CallableMethod): Callable =
object : IntrinsicCallable(
Type.INT_TYPE,
emptyList(),
nullOrObject(method.dispatchReceiverType),
nullOrObject(method.extensionReceiverType)
) {
override fun invokeIntrinsic(v: InstructionAdapter) {
v.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "hashCode", "()I", false)
}
class HashCode(private val jvmTarget: JvmTarget) : IntrinsicMethod() {
override fun toCallable(method: CallableMethod): Callable {
val receiverType = method.dispatchReceiverType ?: method.extensionReceiverType ?: error("No receiver for callable: $method")
val useObjectHashCode = JvmTarget.JVM_1_6 == jvmTarget || !AsmUtil.isPrimitive(receiverType)
return object : IntrinsicCallable(
Type.INT_TYPE,
emptyList(),
if (useObjectHashCode) nullOrObject(method.dispatchReceiverType) else method.dispatchReceiverType,
if (useObjectHashCode) nullOrObject(method.extensionReceiverType) else method.extensionReceiverType
) {
override fun invokeIntrinsic(v: InstructionAdapter) {
v.invokeHashCode(if (useObjectHashCode) AsmTypes.OBJECT_TYPE else receiverType)
}
}
}
companion object {
fun InstructionAdapter.invokeHashCode(type: Type) {
if (AsmUtil.isPrimitive(type)) {
val boxedType = AsmUtil.boxType(type)
visitMethodInsn(Opcodes.INVOKESTATIC, boxedType.internalName, "hashCode", Type.getMethodDescriptor(Type.INT_TYPE, type), false)
}
else {
visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "hashCode", "()I", false)
}
}
}
}
@@ -22,6 +22,8 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.builtins.PrimitiveType;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.config.JvmTarget;
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.FqNameUnsafe;
@@ -45,7 +47,7 @@ public class IntrinsicMethods {
private static final IntrinsicMethod RANGE_TO = new RangeTo();
private static final IntrinsicMethod INC = new Increment(1);
private static final IntrinsicMethod DEC = new Increment(-1);
private static final IntrinsicMethod HASH_CODE = new HashCode();
private final IntrinsicMethod HASH_CODE;
private static final IntrinsicMethod ARRAY_SIZE = new ArraySize();
private static final Equals EQUALS = new Equals();
@@ -59,7 +61,8 @@ public class IntrinsicMethods {
private static final IntrinsicMethod ARRAY_ITERATOR = new ArrayIterator();
private final IntrinsicsMap intrinsicsMap = new IntrinsicsMap();
public IntrinsicMethods() {
public IntrinsicMethods(JvmTarget jvmTarget) {
HASH_CODE = new HashCode(jvmTarget);
intrinsicsMap.registerIntrinsic(KOTLIN_JVM, RECEIVER_PARAMETER_FQ_NAME, "javaClass", -1, JavaClassProperty.INSTANCE);
intrinsicsMap.registerIntrinsic(KOTLIN_JVM, KotlinBuiltIns.FQ_NAMES.kClass, "java", -1, new KClassJavaProperty());
intrinsicsMap.registerIntrinsic(KotlinBuiltIns.FQ_NAMES.kCallable.toSafe(), null, "name", -1, new KCallableNameProperty());
@@ -138,7 +138,7 @@ class GenerationState @JvmOverloads constructor(
this.bindingContext, classBuilderMode, fileClassesProvider, IncompatibleClassTrackerImpl(extraJvmDiagnosticsTrace),
this.moduleName, isJvm8Target, isJvm8TargetWithDefaults
)
val intrinsics: IntrinsicMethods = IntrinsicMethods()
val intrinsics: IntrinsicMethods = IntrinsicMethods(target)
val samWrapperClasses: SamWrapperClasses = SamWrapperClasses(this)
val inlineCycleReporter: InlineCycleReporter = InlineCycleReporter(diagnostics)
val mappingsClassesForWhenByEnum: MappingsClassesForWhenByEnum = MappingsClassesForWhenByEnum(this)