JVM_IR: Optimize null checks.
Introduce lowering to remove null checks for primitive type expressions and replace them with true/false. Side-effects are preserved. Generate ifnull/ifnonnull instructions for null checks instead of materializing a null literal for an equality check.
This commit is contained in:
committed by
Mikhael Bogdanov
parent
f5312f42c5
commit
690b8e0ac9
@@ -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<BackendContext, IrFile> {
|
||||
@@ -273,7 +273,7 @@ val jvmPhases = listOf(
|
||||
|
||||
TailrecPhase,
|
||||
ToArrayPhase,
|
||||
NegatedExpressionLowering,
|
||||
JvmBuiltinOptimizationLowering,
|
||||
|
||||
makePatchParentsPhase(3),
|
||||
|
||||
|
||||
+38
-15
@@ -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<IrBranch>): 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
|
||||
|
||||
+98
@@ -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
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -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
|
||||
|
||||
-50
@@ -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
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
+2
-17
@@ -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) ||
|
||||
|
||||
@@ -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<Pair<ParameterDescriptor, I
|
||||
|
||||
fun IrExpression.isNullConst() = this is IrConst<*> && 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 }
|
||||
|
||||
|
||||
+23
@@ -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
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
fun box(): String {
|
||||
230?.hashCode()
|
||||
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
fun box(): String {
|
||||
var result = 0
|
||||
if (1 == 1) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
fun returningBoxed() : Int? = 1
|
||||
fun acceptingBoxed(x : Int?) : Int ? = x
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
fun main(p: String?) {
|
||||
if (!(p == null)) {
|
||||
"then"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
fun main(p: String?) {
|
||||
if (p == null) {
|
||||
"then"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
class A
|
||||
|
||||
fun foo(x: Any?) {}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
fun box() {
|
||||
val x: Any? = "abc"
|
||||
val y: Any? = if (1 == 1) x else "cde"
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
class A
|
||||
fun box() {
|
||||
val x: A? = A()
|
||||
|
||||
Vendored
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
class A
|
||||
fun box() {
|
||||
val x: A? = A()
|
||||
|
||||
@@ -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!!
|
||||
|
||||
@@ -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!!
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
val a : Int? = 10
|
||||
|
||||
fun foo() = a!!.toString()
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
fun test(x: String?) {
|
||||
if (x == null) return
|
||||
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
fun test1() {
|
||||
val a = null
|
||||
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
fun test1() {
|
||||
val a = Unit
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
fun test() {
|
||||
val value = System.getProperty("key")
|
||||
if (value != null) {
|
||||
|
||||
Vendored
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
fun almostAlwaysTrue() = true
|
||||
|
||||
fun runNoInline(f: () -> Unit) = f()
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
fun test() {
|
||||
lateinit var z: String
|
||||
run {
|
||||
|
||||
+18
@@ -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
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
fun findUserId(username: String): Long? = null
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user