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"
|
description = "Handle toArray functions"
|
||||||
)
|
)
|
||||||
|
|
||||||
private val NegatedExpressionLowering = makeJvmPhase(
|
private val JvmBuiltinOptimizationLowering = makeJvmPhase(
|
||||||
{ context, file -> NegatedExpressionLowering(context).lower(file) },
|
{ context, file -> JvmBuiltinOptimizationLowering(context).lower(file) },
|
||||||
name = "NegatedExpression",
|
name = "JvmBuiltinOptimizationLowering",
|
||||||
description = "Handle negated expressions"
|
description = "Optimize builtin calls for JVM code generation"
|
||||||
)
|
)
|
||||||
|
|
||||||
object IrFileEndPhase : CompilerPhase<BackendContext, IrFile> {
|
object IrFileEndPhase : CompilerPhase<BackendContext, IrFile> {
|
||||||
@@ -273,7 +273,7 @@ val jvmPhases = listOf(
|
|||||||
|
|
||||||
TailrecPhase,
|
TailrecPhase,
|
||||||
ToArrayPhase,
|
ToArrayPhase,
|
||||||
NegatedExpressionLowering,
|
JvmBuiltinOptimizationLowering,
|
||||||
|
|
||||||
makePatchParentsPhase(3),
|
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.IrIntrinsicFunction
|
||||||
import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods
|
import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods
|
||||||
import org.jetbrains.kotlin.backend.jvm.lower.CrIrType
|
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.JvmBuiltinOptimizationLowering.Companion.isNegation
|
||||||
import org.jetbrains.kotlin.backend.jvm.lower.NegatedExpressionLowering.Companion.negationArgument
|
import org.jetbrains.kotlin.backend.jvm.lower.JvmBuiltinOptimizationLowering.Companion.negationArgument
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.codegen.*
|
import org.jetbrains.kotlin.codegen.*
|
||||||
import org.jetbrains.kotlin.codegen.AsmUtil.*
|
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.symbols.IrSymbol
|
||||||
import org.jetbrains.kotlin.ir.types.toKotlinType
|
import org.jetbrains.kotlin.ir.types.toKotlinType
|
||||||
import org.jetbrains.kotlin.ir.util.dump
|
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.util.render
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
@@ -631,30 +632,18 @@ class ExpressionCodegen(
|
|||||||
return expression.onStack
|
return expression.onStack
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun visitWhen(expression: IrWhen, data: BlockInfo): StackValue {
|
override fun visitWhen(expression: IrWhen, data: BlockInfo): StackValue {
|
||||||
expression.markLineNumber(startOffset = true)
|
expression.markLineNumber(startOffset = true)
|
||||||
return genIfWithBranches(expression.branches[0], data, expression.type.toKotlinType(), expression.branches.drop(1))
|
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 {
|
private fun genIfWithBranches(branch: IrBranch, data: BlockInfo, type: KotlinType, otherBranches: List<IrBranch>): StackValue {
|
||||||
val elseLabel = Label()
|
val elseLabel = Label()
|
||||||
var condition = branch.condition
|
|
||||||
val thenBranch = branch.result
|
val thenBranch = branch.result
|
||||||
//TODO don't generate condition for else branch - java verifier fails with empty stack
|
//TODO don't generate condition for else branch - java verifier fails with empty stack
|
||||||
val elseBranch = branch is IrElseBranch
|
val elseBranch = branch is IrElseBranch
|
||||||
if (!elseBranch) {
|
if (!elseBranch) {
|
||||||
var jumpIfFalse = true
|
genConditionWithOptimizationsIfPossible(branch, data, elseLabel)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val end = Label()
|
val end = Label()
|
||||||
@@ -676,6 +665,40 @@ class ExpressionCodegen(
|
|||||||
return result
|
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 {
|
override fun visitTypeOperator(expression: IrTypeOperatorCall, data: BlockInfo): StackValue {
|
||||||
val asmType = expression.typeOperand.toKotlinType().asmType
|
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.expressions.IrExpression
|
||||||
import org.jetbrains.kotlin.ir.types.toKotlinType
|
import org.jetbrains.kotlin.ir.types.toKotlinType
|
||||||
import org.jetbrains.kotlin.ir.util.TypeTranslator
|
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.ir.visitors.transformChildrenVoid
|
||||||
import org.jetbrains.kotlin.psi2ir.transformations.InsertImplicitCasts
|
import org.jetbrains.kotlin.psi2ir.transformations.InsertImplicitCasts
|
||||||
|
|
||||||
@@ -30,7 +31,7 @@ class JvmCoercionToUnitPatcher(val context: JvmBackendContext) :
|
|||||||
|
|
||||||
override fun IrExpression.coerceToUnit(): IrExpression {
|
override fun IrExpression.coerceToUnit(): IrExpression {
|
||||||
if (type.isSubtypeOf(context.irBuiltIns.unitType) && this is IrCall) {
|
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
|
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.classifierOrFail
|
||||||
import org.jetbrains.kotlin.ir.types.impl.originalKotlinType
|
import org.jetbrains.kotlin.ir.types.impl.originalKotlinType
|
||||||
import org.jetbrains.kotlin.ir.util.TypeTranslator
|
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.util.render
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||||
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
|
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
|
||||||
@@ -259,28 +260,12 @@ open class InsertImplicitCasts(
|
|||||||
|
|
||||||
protected open fun IrExpression.coerceToUnit(): IrExpression {
|
protected open fun IrExpression.coerceToUnit(): IrExpression {
|
||||||
val valueType = getKotlinType(this)
|
val valueType = getKotlinType(this)
|
||||||
return coerceToUnitIfNeeded(valueType)
|
return coerceToUnitIfNeeded(valueType, irBuiltIns)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun getKotlinType(irExpression: IrExpression) =
|
protected fun getKotlinType(irExpression: IrExpression) =
|
||||||
irExpression.type.originalKotlinType!!
|
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 =
|
private fun KotlinType.isBuiltInIntegerType(): Boolean =
|
||||||
KotlinBuiltIns.isByte(this) ||
|
KotlinBuiltIns.isByte(this) ||
|
||||||
KotlinBuiltIns.isShort(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.IrFieldImpl
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrTypeParameterImpl
|
import org.jetbrains.kotlin.ir.declarations.impl.IrTypeParameterImpl
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
|
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.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
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.symbols.*
|
||||||
import org.jetbrains.kotlin.ir.types.*
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
import org.jetbrains.kotlin.name.FqName
|
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.psi.psiUtil.startOffset
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||||
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
|
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.
|
* 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.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 =
|
fun IrMemberAccessExpression.usesDefaultArguments(): Boolean =
|
||||||
this.descriptor.valueParameters.any { this.getValueArgument(it) == null }
|
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 {
|
fun box(): String {
|
||||||
230?.hashCode()
|
230?.hashCode()
|
||||||
|
|
||||||
|
|||||||
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
var result = 0
|
var result = 0
|
||||||
if (1 == 1) {
|
if (1 == 1) {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
fun returningBoxed() : Int? = 1
|
fun returningBoxed() : Int? = 1
|
||||||
fun acceptingBoxed(x : Int?) : Int ? = x
|
fun acceptingBoxed(x : Int?) : Int ? = x
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
fun main(p: String?) {
|
fun main(p: String?) {
|
||||||
if (!(p == null)) {
|
if (!(p == null)) {
|
||||||
"then"
|
"then"
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
fun main(p: String?) {
|
fun main(p: String?) {
|
||||||
if (p == null) {
|
if (p == null) {
|
||||||
"then"
|
"then"
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
class A
|
class A
|
||||||
|
|
||||||
fun foo(x: Any?) {}
|
fun foo(x: Any?) {}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
fun box() {
|
fun box() {
|
||||||
val x: Any? = "abc"
|
val x: Any? = "abc"
|
||||||
val y: Any? = if (1 == 1) x else "cde"
|
val y: Any? = if (1 == 1) x else "cde"
|
||||||
|
|||||||
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
class A
|
class A
|
||||||
fun box() {
|
fun box() {
|
||||||
val x: A? = A()
|
val x: A? = A()
|
||||||
|
|||||||
Vendored
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
class A
|
class A
|
||||||
fun box() {
|
fun box() {
|
||||||
val x: A? = A()
|
val x: A? = A()
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// !LANGUAGE: +ProperIeee754Comparisons
|
// !LANGUAGE: +ProperIeee754Comparisons
|
||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
fun equals5(a: Any?, b: Any?) = if (a is Double && b is Double?) a == b else null!!
|
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!!
|
fun equals6(a: Any?, b: Any?) = if (a is Double? && b is Double) a == b else null!!
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// !LANGUAGE: +ProperIeee754Comparisons
|
// !LANGUAGE: +ProperIeee754Comparisons
|
||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
fun equals5(a: Any?, b: Any?) = if (a is Float && b is Float?) a == b else null!!
|
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!!
|
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
|
val a : Int? = 10
|
||||||
|
|
||||||
fun foo() = a!!.toString()
|
fun foo() = a!!.toString()
|
||||||
|
|||||||
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
fun test(x: String?) {
|
fun test(x: String?) {
|
||||||
if (x == null) return
|
if (x == null) return
|
||||||
|
|
||||||
|
|||||||
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
fun test1() {
|
fun test1() {
|
||||||
val a = null
|
val a = null
|
||||||
|
|
||||||
|
|||||||
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
fun test1() {
|
fun test1() {
|
||||||
val a = Unit
|
val a = Unit
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
fun test() {
|
fun test() {
|
||||||
val value = System.getProperty("key")
|
val value = System.getProperty("key")
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
|
|||||||
Vendored
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
fun almostAlwaysTrue() = true
|
fun almostAlwaysTrue() = true
|
||||||
|
|
||||||
fun runNoInline(f: () -> Unit) = f()
|
fun runNoInline(f: () -> Unit) = f()
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// IGNORE_BACKEND: JVM_IR
|
||||||
fun test() {
|
fun test() {
|
||||||
lateinit var z: String
|
lateinit var z: String
|
||||||
run {
|
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 findUserId(username: String): Long? = null
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
|
|||||||
+5
@@ -15977,6 +15977,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
runTest("compiler/testData/codegen/box/nullCheckOptimization/kt7774.kt");
|
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")
|
@TestMetadata("trivialInstanceOf.kt")
|
||||||
public void testTrivialInstanceOf() throws Exception {
|
public void testTrivialInstanceOf() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/nullCheckOptimization/trivialInstanceOf.kt");
|
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");
|
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")
|
@TestMetadata("redundantSafeCall.kt")
|
||||||
public void testRedundantSafeCall() throws Exception {
|
public void testRedundantSafeCall() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/nullCheckOptimization/redundantSafeCall.kt");
|
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");
|
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")
|
@TestMetadata("redundantSafeCall.kt")
|
||||||
public void testRedundantSafeCall() throws Exception {
|
public void testRedundantSafeCall() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/nullCheckOptimization/redundantSafeCall.kt");
|
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");
|
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")
|
@TestMetadata("trivialInstanceOf.kt")
|
||||||
public void testTrivialInstanceOf() throws Exception {
|
public void testTrivialInstanceOf() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/nullCheckOptimization/trivialInstanceOf.kt");
|
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");
|
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")
|
@TestMetadata("trivialInstanceOf.kt")
|
||||||
public void testTrivialInstanceOf() throws Exception {
|
public void testTrivialInstanceOf() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/nullCheckOptimization/trivialInstanceOf.kt");
|
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");
|
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")
|
@TestMetadata("trivialInstanceOf.kt")
|
||||||
public void testTrivialInstanceOf() throws Exception {
|
public void testTrivialInstanceOf() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/nullCheckOptimization/trivialInstanceOf.kt");
|
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");
|
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")
|
@TestMetadata("trivialInstanceOf.kt")
|
||||||
public void testTrivialInstanceOf() throws Exception {
|
public void testTrivialInstanceOf() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/nullCheckOptimization/trivialInstanceOf.kt");
|
runTest("compiler/testData/codegen/box/nullCheckOptimization/trivialInstanceOf.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user