diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt index e9949bd9cdd..ce2617733fd 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt @@ -380,6 +380,7 @@ internal val bitcodePhase = NamedCompilerPhase( lower = contextLLVMSetupPhase then buildDFGPhase then devirtualizationPhase then + redundantCoercionsCleaningPhase then dcePhase then createLLVMDeclarationsPhase then ghaPhase then diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/NewIrUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/NewIrUtils.kt index 7db45b92ef2..ca097b3ac47 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/NewIrUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/NewIrUtils.kt @@ -6,14 +6,17 @@ package org.jetbrains.kotlin.backend.konan.ir import org.jetbrains.kotlin.backend.common.atMostOne +import org.jetbrains.kotlin.backend.konan.DECLARATION_ORIGIN_INLINE_CLASS_SPECIAL_FUNCTION import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns +import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrConst import org.jetbrains.kotlin.ir.expressions.IrConstructorCall +import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl import org.jetbrains.kotlin.ir.symbols.IrClassSymbol @@ -90,4 +93,7 @@ fun buildSimpleAnnotation(irBuiltIns: IrBuiltIns, startOffset: Int, endOffset: I putValueArgument(index, IrConstImpl.string(startOffset, endOffset, irBuiltIns.stringType, arg)) } } -} \ No newline at end of file +} + +internal fun IrExpression.isBoxOrUnboxCall() = + (this is IrCall && symbol.owner.origin == DECLARATION_ORIGIN_INLINE_CLASS_SPECIAL_FUNCTION) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt index 2f7c0dfb4e4..b040bbc5c94 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt @@ -12,13 +12,13 @@ import org.jetbrains.kotlin.backend.common.phaser.PhaserState import org.jetbrains.kotlin.backend.common.phaser.namedUnitPhase import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.descriptors.GlobalHierarchyAnalysis +import org.jetbrains.kotlin.backend.konan.lower.RedundantCoercionsCleaner import org.jetbrains.kotlin.backend.konan.optimizations.* import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.* -import org.jetbrains.kotlin.konan.file.File import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.utils.addToStdlib.cast @@ -109,6 +109,12 @@ internal val devirtualizationPhase = makeKonanModuleOpPhase( } ) +internal val redundantCoercionsCleaningPhase = makeKonanModuleOpPhase( + name = "RedundantCoercionsCleaning", + description = "Redundant coercions cleaning", + op = { context, irModule -> irModule.files.forEach { RedundantCoercionsCleaner(context).lower(it) } } +) + internal val ghaPhase = makeKonanModuleOpPhase( name = "GHAPhase", description = "Global hierarchy analysis", diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/RedundantCoercionsCleaner.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/RedundantCoercionsCleaner.kt new file mode 100644 index 00000000000..23f4ff1e9c8 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/RedundantCoercionsCleaner.kt @@ -0,0 +1,259 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +package org.jetbrains.kotlin.backend.konan.lower + +import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.DECLARATION_ORIGIN_INLINE_CLASS_SPECIAL_FUNCTION +import org.jetbrains.kotlin.backend.konan.getInlinedClassNative +import org.jetbrains.kotlin.backend.konan.ir.isBoxOrUnboxCall +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.impl.* +import org.jetbrains.kotlin.ir.symbols.impl.IrReturnableBlockSymbolImpl +import org.jetbrains.kotlin.ir.types.classifierOrFail +import org.jetbrains.kotlin.ir.util.dump +import org.jetbrains.kotlin.ir.util.getArguments +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid +import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid +import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid + +internal class RedundantCoercionsCleaner(val context: Context) : FileLoweringPass, IrElementTransformerVoid() { + + private class PossiblyFoldedExpression(val expression: IrExpression, val folded: Boolean) { + fun getFullExpression(coercion: IrCall, cast: IrTypeOperatorCall?): IrExpression { + if (folded) return expression + require (coercion.dispatchReceiver == null && coercion.extensionReceiver == null) { + "Expected either or function without any receivers" + } + val castedExpression = + if (cast == null) + expression + else with (cast) { + IrTypeOperatorCallImpl(startOffset, endOffset, type, operator, + typeOperand, expression) + } + with (coercion) { + return IrCallImpl(startOffset, endOffset, type, symbol, typeArgumentsCount, origin).apply { + putValueArgument(0, castedExpression) + } + } + } + } + + private val returnableBlockValues = mutableMapOf>() + + private fun computeReturnableBlockValues(irFile: IrFile) { + irFile.acceptChildrenVoid(object: IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitContainerExpression(expression: IrContainerExpression) { + if (expression is IrReturnableBlock) + returnableBlockValues[expression] = mutableListOf() + + super.visitContainerExpression(expression) + } + + override fun visitReturn(expression: IrReturn) { + val returnableBlock = expression.returnTargetSymbol.owner as? IrReturnableBlock + if (returnableBlock != null) + returnableBlockValues[returnableBlock]!!.add(expression.value) + + super.visitReturn(expression) + } + }) + } + + override fun lower(irFile: IrFile) { + computeReturnableBlockValues(irFile) + irFile.transformChildrenVoid(this) + } + + override fun visitCall(expression: IrCall): IrExpression { + if (!expression.isBoxOrUnboxCall()) + return super.visitCall(expression) + + val argument = expression.getArguments().single().second + val foldedArgument = fold( + expression = argument, + coercion = expression, + cast = null, + transformRecursively = true) + return foldedArgument.getFullExpression(expression, null) + } + + private fun IrFunction.getCoercedClass(): IrClass { + if (name.asString().endsWith("-box>")) + return valueParameters[0].type.classifierOrFail.owner as IrClass + if (name.asString().endsWith("-unbox>")) + return returnType.classifierOrFail.owner as IrClass + error("Unexpected coercion: ${this.dump()}") + } + + private fun IrExpression.unwrapImplicitCasts(): IrExpression { + var expression = this + while (expression is IrTypeOperatorCall && expression.operator == IrTypeOperator.IMPLICIT_CAST) + expression = expression.argument + return expression + } + + /** + * TODO: JVM inliner crashed on attempt inline this function from transform.kt with: + * j.l.IllegalStateException: Couldn't obtain compiled function body for + * public inline fun kotlin.collections.MutableList.transform... + */ + private inline fun MutableList.transform(transformation: (T) -> IrElement) { + forEachIndexed { i, item -> + set(i, transformation(item) as T) + } + } + + private fun fold(expression: IrExpression, coercion: IrCall, + cast: IrTypeOperatorCall?, transformRecursively: Boolean): PossiblyFoldedExpression { + + val transformer = this + + fun IrExpression.transformIfAsked() = + if (transformRecursively) this.transform(transformer, data = null) else this + + fun IrElement.transformIfAsked() = + if (transformRecursively) this.transform(transformer, data = null) else this + + val coercionDeclaringClass = coercion.symbol.owner.getCoercedClass() + expression.unwrapImplicitCasts().let { + if (it.isBoxOrUnboxCall()) { + val result = + if (coercionDeclaringClass == (it as IrCall).symbol.owner.getCoercedClass()) + it.getArguments().single().second + else expression + + return PossiblyFoldedExpression(result.transformIfAsked(), result != expression) + } + } + return when (expression) { + is IrReturnableBlock -> { + val foldedReturnableBlockValues = returnableBlockValues[expression]!!.associate { + it to fold(it, coercion, cast, false) + } + val someoneFolded = foldedReturnableBlockValues.any { it.value.folded } + val transformedReturnableBlock = + if (!someoneFolded) + expression + else { + val oldSymbol = expression.symbol + val newSymbol = IrReturnableBlockSymbolImpl(expression.descriptor) + val transformedReturnableBlock = with(expression) { + IrReturnableBlockImpl( + startOffset = startOffset, + endOffset = endOffset, + type = coercion.type, + symbol = newSymbol, + origin = origin, + statements = statements, + inlineFunctionSymbol = inlineFunctionSymbol) + } + transformedReturnableBlock.transformChildrenVoid(object: IrElementTransformerVoid() { + override fun visitExpression(expression: IrExpression): IrExpression { + foldedReturnableBlockValues[expression]?.let { + return it.getFullExpression(coercion, cast) + } + return super.visitExpression(expression) + } + + override fun visitReturn(expression: IrReturn): IrExpression { + expression.transformChildrenVoid(this) + return if (expression.returnTargetSymbol != oldSymbol) + expression + else with(expression) { + IrReturnImpl( + startOffset = startOffset, + endOffset = endOffset, + type = context.irBuiltIns.nothingType, + returnTargetSymbol = newSymbol, + value = value) + } + } + }) + transformedReturnableBlock + } + if (transformRecursively) + transformedReturnableBlock.transformChildrenVoid(this) + PossiblyFoldedExpression(transformedReturnableBlock, someoneFolded) + } + + is IrBlock -> { + val statements = expression.statements + if (statements.isEmpty()) + PossiblyFoldedExpression(expression, false) + else { + val lastStatement = statements.last() as IrExpression + val foldedLastStatement = fold(lastStatement, coercion, cast, transformRecursively) + statements.transform { + if (it == lastStatement) + foldedLastStatement.expression + else + it.transformIfAsked() + } + val transformedBlock = + if (!foldedLastStatement.folded) + expression + else with(expression) { + IrBlockImpl( + startOffset = startOffset, + endOffset = endOffset, + type = coercion.type, + origin = origin, + statements = statements) + } + PossiblyFoldedExpression(transformedBlock, foldedLastStatement.folded) + } + } + + is IrWhen -> { + val foldedBranches = expression.branches.map { fold(it.result, coercion, cast, transformRecursively) } + val someoneFolded = foldedBranches.any { it.folded } + val transformedWhen = with(expression) { + IrWhenImpl(startOffset, endOffset, if (someoneFolded) coercion.type else type, origin, + branches.asSequence().withIndex().map { (index, branch) -> + IrBranchImpl( + startOffset = branch.startOffset, + endOffset = branch.endOffset, + condition = branch.condition.transformIfAsked(), + result = if (someoneFolded) + foldedBranches[index].getFullExpression(coercion, cast) + else foldedBranches[index].expression) + }.toList()) + } + return PossiblyFoldedExpression(transformedWhen, someoneFolded) + } + + is IrTypeOperatorCall -> + if (expression.operator != IrTypeOperator.CAST + && expression.operator != IrTypeOperator.IMPLICIT_CAST + && expression.operator != IrTypeOperator.SAFE_CAST) + PossiblyFoldedExpression(expression.transformIfAsked(), false) + else { + if (expression.typeOperand.getInlinedClassNative() != coercionDeclaringClass) + PossiblyFoldedExpression(expression.transformIfAsked(), false) + else { + val foldedArgument = fold(expression.argument, coercion, expression, transformRecursively) + if (foldedArgument.folded) + foldedArgument + else + PossiblyFoldedExpression(expression.apply { argument = foldedArgument.expression }, false) + } + } + + else -> PossiblyFoldedExpression(expression.transformIfAsked(), false) + } + } +} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/Devirtualization.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/Devirtualization.kt index a6b24fb2456..53cf6c28e37 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/Devirtualization.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/Devirtualization.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.backend.common.push import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.irBlock import org.jetbrains.kotlin.backend.konan.* +import org.jetbrains.kotlin.backend.konan.ir.isBoxOrUnboxCall import org.jetbrains.kotlin.backend.konan.util.IntArrayList import org.jetbrains.kotlin.backend.konan.util.LongArrayList import org.jetbrains.kotlin.descriptors.ClassKind @@ -1275,24 +1276,9 @@ internal object Devirtualization { .filter { it.key.irCallSite != null } .associate { it.key.irCallSite!! to it.value } devirtualize(irModule, context, externalModulesDFG, devirtualizedCallSites) - removeRedundantCoercions(irModule, context) return devirtualizationAnalysisResult } - /** - * TODO: JVM inliner crashed on attempt inline this function from transform.kt with: - * j.l.IllegalStateException: Couldn't obtain compiled function body for - * public inline fun kotlin.collections.MutableList.transform... - */ - private inline fun MutableList.transform(transformation: (T) -> IrElement) { - forEachIndexed { i, item -> - set(i, transformation(item) as T) - } - } - - private fun IrExpression.isBoxOrUnboxCall() = - (this is IrCall && symbol.owner.origin == DECLARATION_ORIGIN_INLINE_CLASS_SPECIAL_FUNCTION) - private fun devirtualize(irModule: IrModuleFragment, context: Context, externalModulesDFG: ExternalModulesDFG, devirtualizedCallSites: Map) { val symbols = context.ir.symbols @@ -1558,224 +1544,4 @@ internal object Devirtualization { } }) } - - private fun removeRedundantCoercions(irModule: IrModuleFragment, context: Context) { - - class PossiblyFoldedExpression(val expression: IrExpression, val folded: Boolean) { - fun getFullExpression(coercion: IrCall, cast: IrTypeOperatorCall?): IrExpression { - if (folded) return expression - assert (coercion.dispatchReceiver == null && coercion.extensionReceiver == null) { - "Expected either or function without any receivers" - } - val castedExpression = - if (cast == null) - expression - else with (cast) { - IrTypeOperatorCallImpl(startOffset, endOffset, type, operator, - typeOperand, expression) - } - with (coercion) { - return IrCallImpl(startOffset, endOffset, type, symbol, typeArgumentsCount, origin).apply { - putValueArgument(0, castedExpression) - } - } - } - } - - // Possible values of a returnable block. - val returnableBlockValues = mutableMapOf>() - - irModule.acceptChildrenVoid(object: IrElementVisitorVoid { - override fun visitElement(element: IrElement) { - element.acceptChildrenVoid(this) - } - - override fun visitContainerExpression(expression: IrContainerExpression) { - if (expression is IrReturnableBlock) - returnableBlockValues[expression] = mutableListOf() - - super.visitContainerExpression(expression) - } - - override fun visitReturn(expression: IrReturn) { - val returnableBlock = expression.returnTargetSymbol.owner as? IrReturnableBlock - if (returnableBlock != null) - returnableBlockValues[returnableBlock]!!.add(expression.value) - - super.visitReturn(expression) - } - - }) - - irModule.transformChildrenVoid(object: IrElementTransformerVoid() { - - fun IrFunction.getCoercedClass(): IrClass { - if (name.asString().endsWith("-box>")) - return valueParameters[0].type.classifierOrFail.owner as IrClass - if (name.asString().endsWith("-unbox>")) - return returnType.classifierOrFail.owner as IrClass - error("Unexpected coercion: ${this.dump()}") - } - - fun IrExpression.unwrapImplicitCasts(): IrExpression { - var expression = this - while (expression is IrTypeOperatorCall && expression.operator == IrTypeOperator.IMPLICIT_CAST) - expression = expression.argument - return expression - } - - fun fold(expression: IrExpression, coercion: IrCall, cast: IrTypeOperatorCall?, - transformRecursively: Boolean): PossiblyFoldedExpression { - - val transformer = this - - fun IrExpression.transformIfAsked() = - if (transformRecursively) this.transform(transformer, data = null) else this - - fun IrElement.transformIfAsked() = - if (transformRecursively) this.transform(transformer, data = null) else this - - val coercionDeclaringClass = coercion.symbol.owner.getCoercedClass() - expression.unwrapImplicitCasts().let { - if (it.isBoxOrUnboxCall()) { - val result = - if (coercionDeclaringClass == (it as IrCall).symbol.owner.getCoercedClass()) - it.getArguments().single().second - else expression - - return PossiblyFoldedExpression(result.transformIfAsked(), result != expression) - } - } - return when (expression) { - is IrReturnableBlock -> { - val foldedReturnableBlockValues = returnableBlockValues[expression]!!.associate { - it to fold(it, coercion, cast, false) - } - val someoneFolded = foldedReturnableBlockValues.any { it.value.folded } - val transformedReturnableBlock = - if (!someoneFolded) - expression - else { - val oldSymbol = expression.symbol - val newSymbol = IrReturnableBlockSymbolImpl(expression.descriptor) - val transformedReturnableBlock = with(expression) { - IrReturnableBlockImpl( - startOffset = startOffset, - endOffset = endOffset, - type = coercion.type, - symbol = newSymbol, - origin = origin, - statements = statements, - inlineFunctionSymbol = inlineFunctionSymbol) - } - transformedReturnableBlock.transformChildrenVoid(object: IrElementTransformerVoid() { - override fun visitExpression(expression: IrExpression): IrExpression { - foldedReturnableBlockValues[expression]?.let { - return it.getFullExpression(coercion, cast) - } - return super.visitExpression(expression) - } - - override fun visitReturn(expression: IrReturn): IrExpression { - expression.transformChildrenVoid(this) - return if (expression.returnTargetSymbol != oldSymbol) - expression - else with(expression) { - IrReturnImpl( - startOffset = startOffset, - endOffset = endOffset, - type = context.irBuiltIns.nothingType, - returnTargetSymbol = newSymbol, - value = value) - } - } - }) - transformedReturnableBlock - } - if (transformRecursively) - transformedReturnableBlock.transformChildrenVoid(this) - PossiblyFoldedExpression(transformedReturnableBlock, someoneFolded) - } - - is IrBlock -> { - val statements = expression.statements - if (statements.isEmpty()) - PossiblyFoldedExpression(expression, false) - else { - val lastStatement = statements.last() as IrExpression - val foldedLastStatement = fold(lastStatement, coercion, cast, transformRecursively) - statements.transform { - if (it == lastStatement) - foldedLastStatement.expression - else - it.transformIfAsked() - } - val transformedBlock = - if (!foldedLastStatement.folded) - expression - else with(expression) { - IrBlockImpl( - startOffset = startOffset, - endOffset = endOffset, - type = coercion.type, - origin = origin, - statements = statements) - } - PossiblyFoldedExpression(transformedBlock, foldedLastStatement.folded) - } - } - - is IrWhen -> { - val foldedBranches = expression.branches.map { fold(it.result, coercion, cast, transformRecursively) } - val someoneFolded = foldedBranches.any { it.folded } - val transformedWhen = with(expression) { - IrWhenImpl(startOffset, endOffset, if (someoneFolded) coercion.type else type, origin, - branches.asSequence().withIndex().map { (index, branch) -> - IrBranchImpl( - startOffset = branch.startOffset, - endOffset = branch.endOffset, - condition = branch.condition.transformIfAsked(), - result = if (someoneFolded) - foldedBranches[index].getFullExpression(coercion, cast) - else foldedBranches[index].expression) - }.toList()) - } - return PossiblyFoldedExpression(transformedWhen, someoneFolded) - } - - is IrTypeOperatorCall -> - if (expression.operator != IrTypeOperator.CAST - && expression.operator != IrTypeOperator.IMPLICIT_CAST - && expression.operator != IrTypeOperator.SAFE_CAST) - PossiblyFoldedExpression(expression.transformIfAsked(), false) - else { - if (expression.typeOperand.getInlinedClassNative() != coercionDeclaringClass) - PossiblyFoldedExpression(expression.transformIfAsked(), false) - else { - val foldedArgument = fold(expression.argument, coercion, expression, transformRecursively) - if (foldedArgument.folded) - foldedArgument - else - PossiblyFoldedExpression(expression.apply { argument = foldedArgument.expression }, false) - } - } - - else -> PossiblyFoldedExpression(expression.transformIfAsked(), false) - } - } - - override fun visitCall(expression: IrCall): IrExpression { - if (!expression.isBoxOrUnboxCall()) - return super.visitCall(expression) - - val argument = expression.getArguments().single().second - val foldedArgument = fold( - expression = argument, - coercion = expression, - cast = null, - transformRecursively = true) - return foldedArgument.getFullExpression(expression, null) - } - }) - } }