diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLoweringPhases.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLoweringPhases.kt index 6b082563d48..a040fe08d51 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLoweringPhases.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLoweringPhases.kt @@ -217,10 +217,10 @@ private val ToArrayPhase = makeJvmPhase( description = "Handle toArray functions" ) -private val NegatedExpressionLowering = makeJvmPhase( - { context, file -> NegatedExpressionLowering(context).lower(file) }, - name = "NegatedExpression", - description = "Handle negated expressions" +private val JvmBuiltinOptimizationLowering = makeJvmPhase( + { context, file -> JvmBuiltinOptimizationLowering(context).lower(file) }, + name = "JvmBuiltinOptimizationLowering", + description = "Optimize builtin calls for JVM code generation" ) object IrFileEndPhase : CompilerPhase { @@ -273,7 +273,7 @@ val jvmPhases = listOf( TailrecPhase, ToArrayPhase, - NegatedExpressionLowering, + JvmBuiltinOptimizationLowering, makePatchParentsPhase(3), 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 829bc6764ed..2ec7a4bff1c 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 @@ -9,8 +9,8 @@ import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin 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.NegatedExpressionLowering.Companion.isNegation -import org.jetbrains.kotlin.backend.jvm.lower.NegatedExpressionLowering.Companion.negationArgument +import org.jetbrains.kotlin.backend.jvm.lower.JvmBuiltinOptimizationLowering.Companion.isNegation +import org.jetbrains.kotlin.backend.jvm.lower.JvmBuiltinOptimizationLowering.Companion.negationArgument import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.codegen.* import org.jetbrains.kotlin.codegen.AsmUtil.* @@ -35,6 +35,7 @@ import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.types.toKotlinType import org.jetbrains.kotlin.ir.util.dump +import org.jetbrains.kotlin.ir.util.isNullConst import org.jetbrains.kotlin.ir.util.render import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.resolve.DescriptorUtils @@ -631,30 +632,18 @@ class ExpressionCodegen( return expression.onStack } - override fun visitWhen(expression: IrWhen, data: BlockInfo): StackValue { expression.markLineNumber(startOffset = true) return genIfWithBranches(expression.branches[0], data, expression.type.toKotlinType(), expression.branches.drop(1)) } - private fun genIfWithBranches(branch: IrBranch, data: BlockInfo, type: KotlinType, otherBranches: List): StackValue { val elseLabel = Label() - var condition = branch.condition val thenBranch = branch.result //TODO don't generate condition for else branch - java verifier fails with empty stack val elseBranch = branch is IrElseBranch if (!elseBranch) { - var jumpIfFalse = true - if (isNegation(condition, classCodegen.context)) { - // Instead of materializing a negated value when used for control flow, flip the branch - // targets instead. This significantly cuts down the amount of branches and loads of - // const_0 and const_1 in the generated java bytecode. - condition = negationArgument(condition as IrCall) - jumpIfFalse = false - } - gen(condition, data).put(condition.asmType, mv) - BranchedValue.condJump(StackValue.onStack(condition.asmType), elseLabel, jumpIfFalse, mv) + genConditionWithOptimizationsIfPossible(branch, data, elseLabel) } val end = Label() @@ -676,6 +665,40 @@ class ExpressionCodegen( return result } + private fun genConditionWithOptimizationsIfPossible(branch: IrBranch, data: BlockInfo, elseLabel: Label) { + var condition = branch.condition + var jumpIfFalse = true + if (isNegation(condition, classCodegen.context)) { + // Instead of materializing a negated value when used for control flow, flip the branch + // targets instead. This significantly cuts down the amount of branches and loads of + // const_0 and const_1 in the generated java bytecode. + condition = negationArgument(condition as IrCall) + jumpIfFalse = false + } + if (isNullCheck(condition)) { + // Do not materialize null constants to check for null. Instead use the JVM bytecode + // ifnull and ifnonnull instructions. + val call = condition as IrCall + val left = call.getValueArgument(0)!! + val right = call.getValueArgument(1)!! + val other = if (left.isNullConst()) right else left + gen(other, data).put(other.asmType, mv) + if (jumpIfFalse) { + mv.ifnonnull(elseLabel) + } else { + mv.ifnull(elseLabel) + } + } else { + gen(condition, data).put(condition.asmType, mv) + BranchedValue.condJump(onStack(condition.asmType), elseLabel, jumpIfFalse, mv) + } + } + + private fun isNullCheck(expression: IrExpression): Boolean { + return expression is IrCall + && expression.symbol == classCodegen.context.irBuiltIns.eqeqSymbol + && (expression.getValueArgument(0)!!.isNullConst() || expression.getValueArgument(1)!!.isNullConst()) + } override fun visitTypeOperator(expression: IrTypeOperatorCall, data: BlockInfo): StackValue { val asmType = expression.typeOperand.toKotlinType().asmType diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmBuiltinOptimizationLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmBuiltinOptimizationLowering.kt new file mode 100644 index 00000000000..564c4741cea --- /dev/null +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmBuiltinOptimizationLowering.kt @@ -0,0 +1,98 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.jvm.lower + +import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.jvm.JvmBackendContext +import org.jetbrains.kotlin.codegen.intrinsics.Not +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrConst +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrGetValue +import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl +import org.jetbrains.kotlin.ir.types.isPrimitiveType +import org.jetbrains.kotlin.ir.types.toKotlinType +import org.jetbrains.kotlin.ir.util.coerceToUnitIfNeeded +import org.jetbrains.kotlin.ir.util.isNullConst +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid + +class JvmBuiltinOptimizationLowering(val context: JvmBackendContext) : FileLoweringPass { + + companion object { + fun isNegation(expression: IrExpression, context: JvmBackendContext): Boolean { + // TODO: there should be only one representation of the 'not' operator. + return expression is IrCall && + (expression.symbol == context.irBuiltIns.booleanNotSymbol || + context.state.intrinsics.getIntrinsic(expression.symbol.descriptor) is Not) + } + + fun negationArgument(call: IrCall): IrExpression { + // TODO: there should be only one representation of the 'not' operator. + // Once there is only the IR definition the negation argument will + // always be a value argument and this method can be removed. + return call.dispatchReceiver ?: call.getValueArgument(0)!! + } + } + + private fun hasNoSideEffectsForNullCompare(expression: IrExpression): Boolean { + return expression.type.isPrimitiveType() && (expression is IrConst<*> || expression is IrGetValue) + } + + private fun isNullCheckOfPrimitiveTypeValue(call: IrCall, context: JvmBackendContext): Boolean { + if (call.symbol == context.irBuiltIns.eqeqSymbol) { + val left = call.getValueArgument(0)!! + val right = call.getValueArgument(1)!! + // When used for null checks, it is safe to eliminate constants and local variable loads. + // Even if a local variable of simple type is updated via the debugger it still cannot + // be null. + return (right.isNullConst() && left.type.isPrimitiveType()) + || (left.isNullConst() && right.type.isPrimitiveType()) + } + return false + } + + private fun isNullCheckOfNullConstant(call: IrCall, context: JvmBackendContext): Boolean { + if (call.symbol == context.irBuiltIns.eqeqSymbol) { + val left = call.getValueArgument(0)!! + val right = call.getValueArgument(1)!! + return right.isNullConst() && left.isNullConst() + } + return false + } + + override fun lower(irFile: IrFile) { + irFile.transformChildrenVoid(object : IrElementTransformerVoid() { + override fun visitCall(expression: IrCall): IrExpression { + expression.transformChildrenVoid(this) + return if (isNegation(expression, context) && isNegation(negationArgument(expression), context)) { + // TODO: This lowering is currently JvmBackend specific because there are multiple + // definitions of the boolean 'not' operator. Once there is only the irBuiltins + // definition this lowering could be shared with other backends. + negationArgument(negationArgument(expression) as IrCall) + } else if (isNullCheckOfPrimitiveTypeValue(expression, context)) { + val left = expression.getValueArgument(0)!! + val nonNullArgument = if (left.isNullConst()) expression.getValueArgument(1)!! else left + val constFalse = IrConstImpl.constFalse(expression.startOffset, expression.endOffset, context.irBuiltIns.booleanType) + if (hasNoSideEffectsForNullCompare(nonNullArgument)) { + constFalse + } else { + IrBlockImpl(expression.startOffset, expression.endOffset, expression.type, expression.origin).apply { + statements.add(nonNullArgument.coerceToUnitIfNeeded(nonNullArgument.type.toKotlinType(), context.irBuiltIns)) + statements.add(constFalse) + } + } + } else if (isNullCheckOfNullConstant(expression, context)) { + IrConstImpl.constTrue(expression.startOffset, expression.endOffset, context.irBuiltIns.booleanType) + } else { + expression + } + } + }) + } +} \ No newline at end of file diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmCoercionToUnitPatcher.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmCoercionToUnitPatcher.kt index beeaea4d6c1..23d354130ba 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmCoercionToUnitPatcher.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmCoercionToUnitPatcher.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.types.toKotlinType import org.jetbrains.kotlin.ir.util.TypeTranslator +import org.jetbrains.kotlin.ir.util.coerceToUnitIfNeeded import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.psi2ir.transformations.InsertImplicitCasts @@ -30,7 +31,7 @@ class JvmCoercionToUnitPatcher(val context: JvmBackendContext) : override fun IrExpression.coerceToUnit(): IrExpression { if (type.isSubtypeOf(context.irBuiltIns.unitType) && this is IrCall) { - return coerceToUnitIfNeeded(symbol.owner.returnType.toKotlinType()) + return coerceToUnitIfNeeded(symbol.owner.returnType.toKotlinType(), context.irBuiltIns) } return this diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/NegatedExpressionLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/NegatedExpressionLowering.kt deleted file mode 100644 index bd28b1b55ee..00000000000 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/NegatedExpressionLowering.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license - * that can be found in the license/LICENSE.txt file. - */ - -package org.jetbrains.kotlin.backend.jvm.lower - -import org.jetbrains.kotlin.backend.common.FileLoweringPass -import org.jetbrains.kotlin.backend.jvm.JvmBackendContext -import org.jetbrains.kotlin.codegen.intrinsics.Not -import org.jetbrains.kotlin.ir.declarations.IrFile -import org.jetbrains.kotlin.ir.expressions.IrCall -import org.jetbrains.kotlin.ir.expressions.IrExpression -import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid -import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid - -// TODO: This lowering is currently JvmBackend specific because there are multiple -// definitions of the boolean 'not' operator. Once there is only the irBuiltins -// definition this lowering could be shared with other backends. -class NegatedExpressionLowering(val context: JvmBackendContext) : FileLoweringPass { - - companion object { - fun isNegation(expression: IrExpression, context: JvmBackendContext) : Boolean { - // TODO: there should be only one representation of the 'not' operator. - return expression is IrCall && - (expression.symbol == context.irBuiltIns.booleanNotSymbol || - context.state.intrinsics.getIntrinsic(expression.symbol.descriptor) is Not) - } - - fun negationArgument(call: IrCall) : IrExpression { - // TODO: there should be only one representation of the 'not' operator. - // Once there is only the IR definition the negation argument will - // always be a value argument and this method can be removed. - return call.dispatchReceiver ?: call.getValueArgument(0)!! - } - } - - override fun lower(irFile: IrFile) { - irFile.transformChildrenVoid(object : IrElementTransformerVoid() { - override fun visitCall(expression: IrCall): IrExpression { - expression.transformChildrenVoid(this) - return if (isNegation(expression, context) && isNegation(negationArgument(expression), context)) - negationArgument(negationArgument(expression) as IrCall) - else - expression - } - }) - } - -} \ No newline at end of file diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt index e64db342266..36e6b02086b 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt @@ -30,6 +30,7 @@ import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.classifierOrFail import org.jetbrains.kotlin.ir.types.impl.originalKotlinType import org.jetbrains.kotlin.ir.util.TypeTranslator +import org.jetbrains.kotlin.ir.util.coerceToUnitIfNeeded import org.jetbrains.kotlin.ir.util.render import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor @@ -259,28 +260,12 @@ open class InsertImplicitCasts( protected open fun IrExpression.coerceToUnit(): IrExpression { val valueType = getKotlinType(this) - return coerceToUnitIfNeeded(valueType) + return coerceToUnitIfNeeded(valueType, irBuiltIns) } protected fun getKotlinType(irExpression: IrExpression) = irExpression.type.originalKotlinType!! - protected fun IrExpression.coerceToUnitIfNeeded(valueType: KotlinType): IrExpression { - return if (isUnitSubtype(valueType)) - this - else - IrTypeOperatorCallImpl( - startOffset, endOffset, - irBuiltIns.unitType, - IrTypeOperator.IMPLICIT_COERCION_TO_UNIT, - irBuiltIns.unitType, irBuiltIns.unitType.classifierOrFail, - this - ) - } - - protected fun isUnitSubtype(valueType: KotlinType) = - KotlinTypeChecker.DEFAULT.isSubtypeOf(valueType, builtIns.unitType) - private fun KotlinType.isBuiltInIntegerType(): Boolean = KotlinBuiltIns.isByte(this) || KotlinBuiltIns.isShort(this) || diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IrUtils.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IrUtils.kt index 34f86f11232..f8e2e80b677 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IrUtils.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IrUtils.kt @@ -14,8 +14,10 @@ import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl import org.jetbrains.kotlin.ir.declarations.impl.IrTypeParameterImpl import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl +import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.name.FqName @@ -24,6 +26,8 @@ import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.source.PsiSourceElement +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.checker.KotlinTypeChecker /** * Binds the arguments explicitly represented in the IR to the parameters of the accessed function. @@ -140,6 +144,20 @@ fun IrMemberAccessExpression.addArguments(args: List && this.kind == IrConstKind.Null + +fun IrExpression.coerceToUnitIfNeeded(valueType: KotlinType, irBuiltIns: IrBuiltIns): IrExpression { + return if (KotlinTypeChecker.DEFAULT.isSubtypeOf(valueType, irBuiltIns.unitType.toKotlinType())) + this + else + IrTypeOperatorCallImpl( + startOffset, endOffset, + irBuiltIns.unitType, + IrTypeOperator.IMPLICIT_COERCION_TO_UNIT, + irBuiltIns.unitType, irBuiltIns.unitType.classifierOrFail, + this + ) +} + fun IrMemberAccessExpression.usesDefaultArguments(): Boolean = this.descriptor.valueParameters.any { this.getValueArgument(it) == null } diff --git a/compiler/testData/codegen/box/nullCheckOptimization/primitiveCheckWithSideEffect.kt b/compiler/testData/codegen/box/nullCheckOptimization/primitiveCheckWithSideEffect.kt new file mode 100644 index 00000000000..1e7b4e5b74e --- /dev/null +++ b/compiler/testData/codegen/box/nullCheckOptimization/primitiveCheckWithSideEffect.kt @@ -0,0 +1,23 @@ +var result = "fail 1" + +fun withSideEffect() : Int { + result = "OK" + return 42 +} + +fun box(): String { + if (withSideEffect() == null) { + return "fail 2" + } + + if (result != "OK") { + return "fail 3" + } + + result = "fail 4" + if (withSideEffect() != null) { + return result + } + + return result +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/boxingOptimization/kt7224.kt b/compiler/testData/codegen/bytecodeText/boxingOptimization/kt7224.kt index 4e5bcc6b6a3..ec6fa03d080 100644 --- a/compiler/testData/codegen/bytecodeText/boxingOptimization/kt7224.kt +++ b/compiler/testData/codegen/bytecodeText/boxingOptimization/kt7224.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR fun box(): String { 230?.hashCode() diff --git a/compiler/testData/codegen/bytecodeText/boxingOptimization/simpleUninitializedMerge.kt b/compiler/testData/codegen/bytecodeText/boxingOptimization/simpleUninitializedMerge.kt index e3eaa27193a..9e839ce1920 100644 --- a/compiler/testData/codegen/bytecodeText/boxingOptimization/simpleUninitializedMerge.kt +++ b/compiler/testData/codegen/bytecodeText/boxingOptimization/simpleUninitializedMerge.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR fun box(): String { var result = 0 if (1 == 1) { diff --git a/compiler/testData/codegen/bytecodeText/boxingOptimization/unsafeRemoving.kt b/compiler/testData/codegen/bytecodeText/boxingOptimization/unsafeRemoving.kt index 29a5a0c394d..0c211227755 100644 --- a/compiler/testData/codegen/bytecodeText/boxingOptimization/unsafeRemoving.kt +++ b/compiler/testData/codegen/bytecodeText/boxingOptimization/unsafeRemoving.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR fun returningBoxed() : Int? = 1 fun acceptingBoxed(x : Int?) : Int ? = x diff --git a/compiler/testData/codegen/bytecodeText/conditions/negatedNullCompare.kt b/compiler/testData/codegen/bytecodeText/conditions/negatedNullCompare.kt index 845d2c01714..82770b9acc6 100644 --- a/compiler/testData/codegen/bytecodeText/conditions/negatedNullCompare.kt +++ b/compiler/testData/codegen/bytecodeText/conditions/negatedNullCompare.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR fun main(p: String?) { if (!(p == null)) { "then" diff --git a/compiler/testData/codegen/bytecodeText/conditions/nullCompare.kt b/compiler/testData/codegen/bytecodeText/conditions/nullCompare.kt index c6156870d3e..28110eba44d 100644 --- a/compiler/testData/codegen/bytecodeText/conditions/nullCompare.kt +++ b/compiler/testData/codegen/bytecodeText/conditions/nullCompare.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR fun main(p: String?) { if (p == null) { "then" diff --git a/compiler/testData/codegen/bytecodeText/deadCodeElimination/boxing.kt b/compiler/testData/codegen/bytecodeText/deadCodeElimination/boxing.kt index b1b5240cad7..ec4628b2635 100644 --- a/compiler/testData/codegen/bytecodeText/deadCodeElimination/boxing.kt +++ b/compiler/testData/codegen/bytecodeText/deadCodeElimination/boxing.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR class A fun foo(x: Any?) {} diff --git a/compiler/testData/codegen/bytecodeText/deadCodeElimination/literal.kt b/compiler/testData/codegen/bytecodeText/deadCodeElimination/literal.kt index 8af1b1d0f99..a3e1485370b 100644 --- a/compiler/testData/codegen/bytecodeText/deadCodeElimination/literal.kt +++ b/compiler/testData/codegen/bytecodeText/deadCodeElimination/literal.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR fun box() { val x: Any? = "abc" val y: Any? = if (1 == 1) x else "cde" diff --git a/compiler/testData/codegen/bytecodeText/deadCodeElimination/simpleConstructor.kt b/compiler/testData/codegen/bytecodeText/deadCodeElimination/simpleConstructor.kt index 396ce6d1090..dfec1f31eba 100644 --- a/compiler/testData/codegen/bytecodeText/deadCodeElimination/simpleConstructor.kt +++ b/compiler/testData/codegen/bytecodeText/deadCodeElimination/simpleConstructor.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR class A fun box() { val x: A? = A() diff --git a/compiler/testData/codegen/bytecodeText/deadCodeElimination/simpleConstructorNotRedundant.kt b/compiler/testData/codegen/bytecodeText/deadCodeElimination/simpleConstructorNotRedundant.kt index f160e4c4969..b175e4e2f06 100644 --- a/compiler/testData/codegen/bytecodeText/deadCodeElimination/simpleConstructorNotRedundant.kt +++ b/compiler/testData/codegen/bytecodeText/deadCodeElimination/simpleConstructorNotRedundant.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR class A fun box() { val x: A? = A() diff --git a/compiler/testData/codegen/bytecodeText/ieee754/smartCastsForDouble.kt b/compiler/testData/codegen/bytecodeText/ieee754/smartCastsForDouble.kt index d7fc68450f2..e279293dd8e 100644 --- a/compiler/testData/codegen/bytecodeText/ieee754/smartCastsForDouble.kt +++ b/compiler/testData/codegen/bytecodeText/ieee754/smartCastsForDouble.kt @@ -1,5 +1,4 @@ // !LANGUAGE: +ProperIeee754Comparisons -// IGNORE_BACKEND: JVM_IR fun equals5(a: Any?, b: Any?) = if (a is Double && b is Double?) a == b else null!! fun equals6(a: Any?, b: Any?) = if (a is Double? && b is Double) a == b else null!! diff --git a/compiler/testData/codegen/bytecodeText/ieee754/smartCastsForFloat.kt b/compiler/testData/codegen/bytecodeText/ieee754/smartCastsForFloat.kt index b0e9d79b42f..526f626e3f3 100644 --- a/compiler/testData/codegen/bytecodeText/ieee754/smartCastsForFloat.kt +++ b/compiler/testData/codegen/bytecodeText/ieee754/smartCastsForFloat.kt @@ -1,5 +1,4 @@ // !LANGUAGE: +ProperIeee754Comparisons -// IGNORE_BACKEND: JVM_IR fun equals5(a: Any?, b: Any?) = if (a is Float && b is Float?) a == b else null!! fun equals6(a: Any?, b: Any?) = if (a is Float? && b is Float) a == b else null!! diff --git a/compiler/testData/codegen/bytecodeText/intConstantNullable.kt b/compiler/testData/codegen/bytecodeText/intConstantNullable.kt index 92c54c31e9e..648b9154190 100644 --- a/compiler/testData/codegen/bytecodeText/intConstantNullable.kt +++ b/compiler/testData/codegen/bytecodeText/intConstantNullable.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR val a : Int? = 10 fun foo() = a!!.toString() diff --git a/compiler/testData/codegen/bytecodeText/nullCheckOptimization/alreadyCheckedForNull.kt b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/alreadyCheckedForNull.kt index 94e23ed9abe..9611bc4d514 100644 --- a/compiler/testData/codegen/bytecodeText/nullCheckOptimization/alreadyCheckedForNull.kt +++ b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/alreadyCheckedForNull.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR fun test(x: String?) { if (x == null) return diff --git a/compiler/testData/codegen/bytecodeText/nullCheckOptimization/ifNullEqualsNull.kt b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/ifNullEqualsNull.kt index 858a14b546f..fd188e7f7a1 100644 --- a/compiler/testData/codegen/bytecodeText/nullCheckOptimization/ifNullEqualsNull.kt +++ b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/ifNullEqualsNull.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR fun test1() { val a = null diff --git a/compiler/testData/codegen/bytecodeText/nullCheckOptimization/ifUnitEqualsNull.kt b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/ifUnitEqualsNull.kt index 6a3bec9436d..44408837217 100644 --- a/compiler/testData/codegen/bytecodeText/nullCheckOptimization/ifUnitEqualsNull.kt +++ b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/ifUnitEqualsNull.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR fun test1() { val a = Unit diff --git a/compiler/testData/codegen/bytecodeText/nullCheckOptimization/kt12839.kt b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/kt12839.kt index 927dd90a578..391fdb2ec4e 100644 --- a/compiler/testData/codegen/bytecodeText/nullCheckOptimization/kt12839.kt +++ b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/kt12839.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR fun test() { val value = System.getProperty("key") if (value != null) { diff --git a/compiler/testData/codegen/bytecodeText/nullCheckOptimization/localLateinit/checkedAlways.kt b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/localLateinit/checkedAlways.kt index 482fdc3f2df..275536e616b 100644 --- a/compiler/testData/codegen/bytecodeText/nullCheckOptimization/localLateinit/checkedAlways.kt +++ b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/localLateinit/checkedAlways.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR fun almostAlwaysTrue() = true fun runNoInline(f: () -> Unit) = f() diff --git a/compiler/testData/codegen/bytecodeText/nullCheckOptimization/localLateinit/initialized.kt b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/localLateinit/initialized.kt index b73f0d43670..4281c646167 100644 --- a/compiler/testData/codegen/bytecodeText/nullCheckOptimization/localLateinit/initialized.kt +++ b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/localLateinit/initialized.kt @@ -1,3 +1,4 @@ +// IGNORE_BACKEND: JVM_IR fun test() { lateinit var z: String run { diff --git a/compiler/testData/codegen/bytecodeText/nullCheckOptimization/primitiveCheck.kt b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/primitiveCheck.kt new file mode 100644 index 00000000000..686378454e3 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/primitiveCheck.kt @@ -0,0 +1,18 @@ +fun f() : Int { + return 42 +} + +fun box(): String { + if (f() == null) { + return "fail 1" + } + + if (f() != null) { + return "OK" + } + + return "fail 2" +} + +// 0 IF +// 0 ACONST_NULL diff --git a/compiler/testData/codegen/bytecodeText/when/kt18818.kt b/compiler/testData/codegen/bytecodeText/when/kt18818.kt index 43c17a31247..225a7f05cf2 100644 --- a/compiler/testData/codegen/bytecodeText/when/kt18818.kt +++ b/compiler/testData/codegen/bytecodeText/when/kt18818.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR fun findUserId(username: String): Long? = null fun main(args: Array) { diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index f8c0356bd82..b7bd27b38e7 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -15977,6 +15977,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/nullCheckOptimization/kt7774.kt"); } + @TestMetadata("primitiveCheckWithSideEffect.kt") + public void testPrimitiveCheckWithSideEffect() throws Exception { + runTest("compiler/testData/codegen/box/nullCheckOptimization/primitiveCheckWithSideEffect.kt"); + } + @TestMetadata("trivialInstanceOf.kt") public void testTrivialInstanceOf() throws Exception { runTest("compiler/testData/codegen/box/nullCheckOptimization/trivialInstanceOf.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 69134bad9dc..40dadad025d 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -2798,6 +2798,11 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/nullCheckOptimization/notNullAsNotNullable.kt"); } + @TestMetadata("primitiveCheck.kt") + public void testPrimitiveCheck() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/nullCheckOptimization/primitiveCheck.kt"); + } + @TestMetadata("redundantSafeCall.kt") public void testRedundantSafeCall() throws Exception { runTest("compiler/testData/codegen/bytecodeText/nullCheckOptimization/redundantSafeCall.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/IrBytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/IrBytecodeTextTestGenerated.java index f0678413a95..53b9b73bd35 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/IrBytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/IrBytecodeTextTestGenerated.java @@ -2798,6 +2798,11 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/nullCheckOptimization/notNullAsNotNullable.kt"); } + @TestMetadata("primitiveCheck.kt") + public void testPrimitiveCheck() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/nullCheckOptimization/primitiveCheck.kt"); + } + @TestMetadata("redundantSafeCall.kt") public void testRedundantSafeCall() throws Exception { runTest("compiler/testData/codegen/bytecodeText/nullCheckOptimization/redundantSafeCall.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 75694a5618d..8796d31665d 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -15977,6 +15977,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/nullCheckOptimization/kt7774.kt"); } + @TestMetadata("primitiveCheckWithSideEffect.kt") + public void testPrimitiveCheckWithSideEffect() throws Exception { + runTest("compiler/testData/codegen/box/nullCheckOptimization/primitiveCheckWithSideEffect.kt"); + } + @TestMetadata("trivialInstanceOf.kt") public void testTrivialInstanceOf() throws Exception { runTest("compiler/testData/codegen/box/nullCheckOptimization/trivialInstanceOf.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 4d74d2d7485..72a898c02d1 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -15982,6 +15982,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/nullCheckOptimization/kt7774.kt"); } + @TestMetadata("primitiveCheckWithSideEffect.kt") + public void testPrimitiveCheckWithSideEffect() throws Exception { + runTest("compiler/testData/codegen/box/nullCheckOptimization/primitiveCheckWithSideEffect.kt"); + } + @TestMetadata("trivialInstanceOf.kt") public void testTrivialInstanceOf() throws Exception { runTest("compiler/testData/codegen/box/nullCheckOptimization/trivialInstanceOf.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java index d631996b56c..716d7b28fac 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java @@ -12372,6 +12372,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTest("compiler/testData/codegen/box/nullCheckOptimization/kt7774.kt"); } + @TestMetadata("primitiveCheckWithSideEffect.kt") + public void testPrimitiveCheckWithSideEffect() throws Exception { + runTest("compiler/testData/codegen/box/nullCheckOptimization/primitiveCheckWithSideEffect.kt"); + } + @TestMetadata("trivialInstanceOf.kt") public void testTrivialInstanceOf() throws Exception { runTest("compiler/testData/codegen/box/nullCheckOptimization/trivialInstanceOf.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 03e18c770d6..e897b842e4f 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -13427,6 +13427,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTest("compiler/testData/codegen/box/nullCheckOptimization/kt7774.kt"); } + @TestMetadata("primitiveCheckWithSideEffect.kt") + public void testPrimitiveCheckWithSideEffect() throws Exception { + runTest("compiler/testData/codegen/box/nullCheckOptimization/primitiveCheckWithSideEffect.kt"); + } + @TestMetadata("trivialInstanceOf.kt") public void testTrivialInstanceOf() throws Exception { runTest("compiler/testData/codegen/box/nullCheckOptimization/trivialInstanceOf.kt");