diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantBoxingMethodTransformer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantBoxingMethodTransformer.kt index b4d721f4429..46646e36112 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantBoxingMethodTransformer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantBoxingMethodTransformer.kt @@ -39,7 +39,7 @@ class RedundantBoxingMethodTransformer(private val generationState: GenerationSt override fun transform(internalClassName: String, node: MethodNode) { val interpreter = RedundantBoxingInterpreter(node.instructions, generationState) - val frames = MethodTransformer.analyze(internalClassName, node, interpreter) + val frames = analyze(internalClassName, node, interpreter) interpretPopInstructionsForBoxedValues(interpreter, node, frames) @@ -168,7 +168,8 @@ class RedundantBoxingMethodTransformer(private val generationState: GenerationSt val frame = frames[i] ?: continue val insn = insnList[i] if ((insn.opcode == Opcodes.ASTORE || insn.opcode == Opcodes.ALOAD) && - (insn as VarInsnNode).`var` == localVariableNode.index) { + (insn as VarInsnNode).`var` == localVariableNode.index + ) { if (insn.getOpcode() == Opcodes.ASTORE) { values.add(frame.top()!!) } else { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/HashCode.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/HashCode.kt index 82c76066e1b..08f5e79549e 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/HashCode.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/HashCode.kt @@ -27,31 +27,38 @@ import org.jetbrains.kotlin.ir.util.render import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Type -// TODO Implement hashCode on primitive types as a lowering. object HashCode : IntrinsicMethod() { override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo) = with(codegen) { val receiver = expression.dispatchReceiver ?: error("No receiver for hashCode: ${expression.render()}") - val result = receiver.accept(this, data).materialized() + val receiverIrType = receiver.type + val receiverJvmType = typeMapper.mapType(receiverIrType) + val receiverValue = receiver.accept(this, data).materialized() + val receiverType = receiverValue.type val target = context.state.target when { irFunction.origin == JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD || - irFunction.origin == IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER -> - DescriptorAsmUtil.genHashCode(mv, mv, result.type, target) - target == JvmTarget.JVM_1_6 || !AsmUtil.isPrimitive(result.type) -> { - result.materializeAtBoxed(receiver.type) - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "hashCode", "()I", false) + irFunction.origin == IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER -> { + // TODO generate or lower IR for data class / inline class 'hashCode'? + DescriptorAsmUtil.genHashCode(mv, mv, receiverType, target) } - else -> { - val boxedType = AsmUtil.boxType(result.type) + target >= JvmTarget.JVM_1_8 && AsmUtil.isPrimitive(receiverJvmType) -> { + val boxedType = AsmUtil.boxPrimitiveType(receiverJvmType) + ?: throw AssertionError("Primitive type expected: $receiverJvmType") + receiverValue.materializeAt(receiverJvmType, receiverIrType) mv.visitMethodInsn( Opcodes.INVOKESTATIC, boxedType.internalName, "hashCode", - Type.getMethodDescriptor(Type.INT_TYPE, result.type), + Type.getMethodDescriptor(Type.INT_TYPE, receiverJvmType), false ) } + else -> { + receiverValue.materializeAtBoxed(receiverIrType) + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "hashCode", "()I", false) + } } MaterialValue(codegen, Type.INT_TYPE, codegen.context.irBuiltIns.intType) } + } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrCheckNotNull.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrCheckNotNull.kt index e79c83b380d..3ca3f25b515 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrCheckNotNull.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrCheckNotNull.kt @@ -13,18 +13,6 @@ import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.Type -private fun ExpressionCodegen.checkTopValueForNull() { - mv.dup() - if (state.unifiedNullChecks) { - mv.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, "checkNotNull", "(Ljava/lang/Object;)V", false) - } else { - val ifNonNullLabel = Label() - mv.ifnonnull(ifNonNullLabel) - mv.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, "throwNpe", "()V", false) - mv.mark(ifNonNullLabel) - } -} - object IrCheckNotNull : IntrinsicMethod() { override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? { val arg0 = expression.getValueArgument(0)!!.accept(codegen, data) @@ -37,4 +25,16 @@ object IrCheckNotNull : IntrinsicMethod() { arg0.materialized().also { codegen.checkTopValueForNull() }.discard() } } + + private fun ExpressionCodegen.checkTopValueForNull() { + mv.dup() + if (state.unifiedNullChecks) { + mv.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, "checkNotNull", "(Ljava/lang/Object;)V", false) + } else { + val ifNonNullLabel = Label() + mv.ifnonnull(ifNonNullLabel) + mv.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, "throwNpe", "()V", false) + mv.mark(ifNonNullLabel) + } + } } diff --git a/compiler/testData/codegen/bytecodeText/boxingOptimization/hashCodeOnNonNull.kt b/compiler/testData/codegen/bytecodeText/boxingOptimization/hashCodeOnNonNull.kt index 4553487d949..97fade9aa0a 100644 --- a/compiler/testData/codegen/bytecodeText/boxingOptimization/hashCodeOnNonNull.kt +++ b/compiler/testData/codegen/bytecodeText/boxingOptimization/hashCodeOnNonNull.kt @@ -1,11 +1,58 @@ -// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND_FIR: JVM_IR -fun foo() { - val x: Int? = 6 - val hc = x!!.hashCode() +fun testBoolean(): Int { + val b: Boolean? = true + return b!!.hashCode() } +fun testByte(): Int { + val b: Byte? = 1.toByte() + return b!!.hashCode() +} + +fun testChar(): Int { + val c: Char? = 'x' + return c!!.hashCode() +} + +fun testShort(): Int { + val s: Short? = 1.toShort() + return s!!.hashCode() +} + +fun testInt(): Int { + val i: Int? = 42 + return i!!.hashCode() +} + +fun testLong(): Int { + val l: Long? = 42L + return l!!.hashCode() +} + +fun testFloat(): Int { + val f: Float? = 0.0f + return f!!.hashCode() +} + +fun testDouble(): Int { + val d: Double? = 0.0 + return d!!.hashCode() +} + +// 1 java/lang/Boolean.hashCode \(Z\)I +// 1 java/lang/Character.hashCode \(C\)I +// 1 java/lang/Byte.hashCode \(B\)I +// 1 java/lang/Short.hashCode \(S\)I // 1 java/lang/Integer.hashCode \(I\)I -// 0 java/lang/Integer.valueOf +// 1 java/lang/Long.hashCode \(J\)I +// 1 java/lang/Float.hashCode \(F\)I +// 1 java/lang/Double.hashCode \(D\)I +// 0 valueOf +// 0 byteValue +// 0 shortValue // 0 intValue +// 0 longValue +// 0 floatValue +// 0 doubleValue +// 0 charValue