diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt index c4a3d96234b..bad80a5b4bc 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt @@ -7,9 +7,7 @@ package org.jetbrains.kotlin.backend.jvm.codegen import org.jetbrains.kotlin.backend.common.descriptors.propertyIfAccessor import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin -import org.jetbrains.kotlin.backend.jvm.intrinsics.ComparisonIntrinsic import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicFunction -import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods import org.jetbrains.kotlin.backend.jvm.lower.CrIrType import org.jetbrains.kotlin.backend.jvm.lower.JvmBuiltinOptimizationLowering.Companion.isNegation import org.jetbrains.kotlin.builtins.KotlinBuiltIns @@ -739,53 +737,6 @@ class ExpressionCodegen( jumpIfFalse = !jumpIfFalse } - // Do not materialize null constants to check for null. Instead use the JVM bytecode - // ifnull and ifnonnull instructions. - if (condition is IrCall - && condition.symbol == classCodegen.context.irBuiltIns.eqeqSymbol - && (condition.getValueArgument(0)!!.isNullConst() || condition.getValueArgument(1)!!.isNullConst()) - ) { - val left = condition.getValueArgument(0)!! - val right = condition.getValueArgument(1)!! - (if (left.isNullConst()) right else left).accept(this, data).materialize() - if (jumpIfFalse) { - mv.ifnonnull(jumpToLabel) - } else { - mv.ifnull(jumpToLabel) - } - return - } - - // For comparison intrinsics, branch directly based on the comparison instead of - // materializing a boolean and performing and extra jump. - if (condition is IrCall) { - val intrinsic = classCodegen.context.irIntrinsics.getIntrinsic(condition.descriptor.original as CallableMemberDescriptor) - if (intrinsic is ComparisonIntrinsic) { - val callable = resolveToCallable(condition, false) - (callable as IrIntrinsicFunction).loadArguments(this, data) - val stackValue = intrinsic.genStackValue(condition, classCodegen.context) - BranchedValue.condJump(stackValue, jumpToLabel, jumpIfFalse, mv) - return - } - } - - // For instance of type operators, branch directly on the instanceof result instead - // of materializing a boolean and performing an extra jump. - // TODO after redundant materialization is removed, this will be unnecessary - if (condition is IrTypeOperatorCall && - (condition.operator == IrTypeOperator.NOT_INSTANCEOF || condition.operator == IrTypeOperator.INSTANCEOF) - ) { - val asmType = condition.typeOperand.toKotlinType().asmType - condition.argument.accept(this, data).coerce(AsmTypes.OBJECT_TYPE).materialize() - val type = boxType(asmType) - generateIsCheck(mv, condition.typeOperand.toKotlinType(), type, state.languageVersionSettings.isReleaseCoroutines()) - if (condition.operator == IrTypeOperator.NOT_INSTANCEOF) { - jumpIfFalse = !jumpIfFalse - } - BranchedValue.condJump(onStack(Type.BOOLEAN_TYPE), jumpToLabel, jumpIfFalse, mv) - return - } - BranchedValue.condJump(condition.accept(this, data), jumpToLabel, jumpIfFalse, mv) } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/CompareTo.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/CompareTo.kt index 18f1876e1d5..3b44122eddf 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/CompareTo.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/CompareTo.kt @@ -17,6 +17,8 @@ package org.jetbrains.kotlin.backend.jvm.intrinsics import org.jetbrains.kotlin.backend.jvm.JvmBackendContext +import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo +import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen import org.jetbrains.kotlin.codegen.AsmUtil.comparisonOperandType import org.jetbrains.kotlin.codegen.StackValue import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression @@ -63,18 +65,7 @@ class CompareTo : IntrinsicMethod() { class PrimitiveComparison( private val primitiveNumberType: KotlinType, private val operatorToken: KtSingleValueToken -) : IntrinsicMethod(), ComparisonIntrinsic { - - override fun genStackValue(expression: IrMemberAccessExpression, context: JvmBackendContext): StackValue { - val parameterType = context.state.typeMapper.mapType(primitiveNumberType) - - return StackValue.cmp( - operatorToken, - parameterType, - StackValue.onStack(parameterType, primitiveNumberType), - StackValue.onStack(parameterType, primitiveNumberType) - ) - } +) : IntrinsicMethod() { override fun toCallable( expression: IrMemberAccessExpression, @@ -84,10 +75,15 @@ class PrimitiveComparison( val parameterType = context.state.typeMapper.mapType(primitiveNumberType) return object : IrIntrinsicFunction(expression, signature, context, listOf(parameterType, parameterType)) { - override fun genInvokeInstruction(v: InstructionAdapter) { - genStackValue(expression, context).put(Type.BOOLEAN_TYPE, v) + override fun invoke(v: InstructionAdapter, codegen: ExpressionCodegen, data: BlockInfo): StackValue { + loadArguments(codegen, data) + return StackValue.cmp( + operatorToken, + parameterType, + StackValue.onStack(parameterType, primitiveNumberType), + StackValue.onStack(parameterType, primitiveNumberType) + ) } } } - } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/Equals.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/Equals.kt index 669896164b2..092dc7fd899 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/Equals.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/Equals.kt @@ -7,6 +7,8 @@ package org.jetbrains.kotlin.backend.jvm.intrinsics import com.intellij.psi.tree.IElementType import org.jetbrains.kotlin.backend.jvm.JvmBackendContext +import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo +import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.AsmUtil.* import org.jetbrains.kotlin.codegen.StackValue @@ -14,71 +16,50 @@ import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin import org.jetbrains.kotlin.ir.types.toKotlinType +import org.jetbrains.kotlin.ir.util.isNullConst import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberOrNullableType import org.jetbrains.kotlin.types.typeUtil.upperBoundedByPrimitiveNumberOrNullableType +import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter -class Equals(val operator: IElementType) : IntrinsicMethod(), ComparisonIntrinsic { - - private fun argumentTypes( - expression: IrMemberAccessExpression, - context: JvmBackendContext - ): Pair { - val receiverAndArgs = expression.receiverAndArgs().apply { - assert(size == 2) { "Equals expects 2 arguments, but ${joinToString()}" } - } - - var leftType = context.state.typeMapper.mapType(receiverAndArgs.first().type.toKotlinType()) - var rightType = context.state.typeMapper.mapType(receiverAndArgs.last().type.toKotlinType()) - - if (isPrimitive(leftType) != isPrimitive(rightType)) { - leftType = boxType(leftType) - rightType = boxType(rightType) - } - return Pair(leftType, rightType) - } - - override fun genStackValue( - expression: IrMemberAccessExpression, - context: JvmBackendContext - ): StackValue { - val (leftType, rightType) = argumentTypes(expression, context) - val opToken = expression.origin - return when { - leftType.isFloatingPoint && rightType.isFloatingPoint -> - genEqualsBoxedOnStack(operator) - - opToken === IrStatementOrigin.EQEQEQ || opToken === IrStatementOrigin.EXCLEQEQ -> { - // TODO: always casting to the type of the left operand in case of primitives looks wrong - val operandType = if (isPrimitive(leftType)) leftType else OBJECT_TYPE - StackValue.cmp(operator, operandType, StackValue.onStack(leftType), StackValue.onStack(rightType)) - } - - else -> - genEqualsForExpressionsOnStack(operator, StackValue.onStack(leftType), StackValue.onStack(rightType)) - } - } +class Equals(val operator: IElementType) : IntrinsicMethod() { override fun toCallable( expression: IrMemberAccessExpression, signature: JvmMethodSignature, context: JvmBackendContext ): IrIntrinsicFunction { - val (leftType, rightType) = argumentTypes(expression, context).let { - if (it.first.isFloatingPoint && it.second.isFloatingPoint) - Pair(OBJECT_TYPE, OBJECT_TYPE) - else - it + val receiverAndArgs = expression.receiverAndArgs().apply { + assert(size == 2) { "Equals expects 2 arguments, but ${joinToString()}" } + } + + val (leftArg, rightArg) = receiverAndArgs + var leftType = context.state.typeMapper.mapType(leftArg.type.toKotlinType()) + var rightType = context.state.typeMapper.mapType(rightArg.type.toKotlinType()) + if (isPrimitive(leftType) != isPrimitive(rightType)) { + leftType = boxType(leftType) + rightType = boxType(rightType) } return object : IrIntrinsicFunction(expression, signature, context, listOf(leftType, rightType)) { - override fun genInvokeInstruction(v: InstructionAdapter) { - val value = genStackValue(expression, context) - value.put(Type.BOOLEAN_TYPE, v) + override fun invoke(v: InstructionAdapter, codegen: ExpressionCodegen, data: BlockInfo): StackValue { + val opToken = expression.origin + return if (leftArg.isNullConst() || rightArg.isNullConst()) { + val other = if (leftArg.isNullConst()) rightArg else leftArg + StackValue.compareWithNull(other.accept(codegen, data), Opcodes.IFNONNULL) + } else if (leftType.isFloatingPoint && rightType.isFloatingPoint) { + genEqualsBoxedOnStack(operator) + } else if (opToken === IrStatementOrigin.EQEQEQ || opToken === IrStatementOrigin.EXCLEQEQ) { + // TODO: always casting to the type of the left operand in case of primitives looks wrong + val operandType = if (isPrimitive(leftType)) leftType else OBJECT_TYPE + StackValue.cmp(operator, operandType, codegen.gen(leftArg, operandType, data), codegen.gen(rightArg, operandType, data)) + } else { + genEqualsForExpressionsOnStack(operator, codegen.gen(leftArg, leftType, data), codegen.gen(rightArg, rightType, data)) + } } } } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IntrinsicMethod.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IntrinsicMethod.kt index 4936810dfe4..4ac2a247bec 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IntrinsicMethod.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IntrinsicMethod.kt @@ -8,7 +8,6 @@ package org.jetbrains.kotlin.backend.jvm.intrinsics import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.codegen.Callable import org.jetbrains.kotlin.codegen.CallableMethod -import org.jetbrains.kotlin.codegen.StackValue import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression import org.jetbrains.kotlin.ir.types.toKotlinType @@ -16,10 +15,6 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.Method -interface ComparisonIntrinsic { - fun genStackValue(expression: IrMemberAccessExpression, context: JvmBackendContext): StackValue -} - abstract class IntrinsicMethod { open fun toCallable( diff --git a/compiler/testData/codegen/bytecodeText/intrinsicsCompare/charSmartCast.kt b/compiler/testData/codegen/bytecodeText/intrinsicsCompare/charSmartCast.kt index b6efd697ed9..53b7a1b412b 100644 --- a/compiler/testData/codegen/bytecodeText/intrinsicsCompare/charSmartCast.kt +++ b/compiler/testData/codegen/bytecodeText/intrinsicsCompare/charSmartCast.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR fun equals3(a: Char?, b: Char?) = a != null && b != null && a == b fun equals4(a: Char?, b: Char?) = if (a is Char && b is Char) a == b else null!! diff --git a/compiler/testData/codegen/bytecodeText/intrinsicsCompare/longSmartCast.kt b/compiler/testData/codegen/bytecodeText/intrinsicsCompare/longSmartCast.kt index 617b78b96de..02fd0b3ea8f 100644 --- a/compiler/testData/codegen/bytecodeText/intrinsicsCompare/longSmartCast.kt +++ b/compiler/testData/codegen/bytecodeText/intrinsicsCompare/longSmartCast.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR fun equals3(a: Long?, b: Long?) = a != null && b != null && a == b fun equals4(a: Long?, b: Long?) = if (a is Long && b is Long) a == b else null!!