[IR] Extracted box/unbox folding to a separate pass

This commit is contained in:
Igor Chevdar
2020-10-13 21:59:20 +05:00
parent 68d428afb6
commit 229571b57f
5 changed files with 275 additions and 237 deletions
@@ -380,6 +380,7 @@ internal val bitcodePhase = NamedCompilerPhase(
lower = contextLLVMSetupPhase then
buildDFGPhase then
devirtualizationPhase then
redundantCoercionsCleaningPhase then
dcePhase then
createLLVMDeclarationsPhase then
ghaPhase then
@@ -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))
}
}
}
}
internal fun IrExpression.isBoxOrUnboxCall() =
(this is IrCall && symbol.owner.origin == DECLARATION_ORIGIN_INLINE_CLASS_SPECIAL_FUNCTION)
@@ -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",
@@ -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 <box> or <unbox> 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<IrReturnableBlock, MutableList<IrExpression>>()
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 <reified T : org.jetbrains.kotlin.ir.IrElement> kotlin.collections.MutableList<T>.transform...
*/
private inline fun <reified T : IrElement> MutableList<T>.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)
}
}
}
@@ -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 <reified T : org.jetbrains.kotlin.ir.IrElement> kotlin.collections.MutableList<T>.transform...
*/
private inline fun <reified T : IrElement> MutableList<T>.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<IrCall, DevirtualizedCallSite>) {
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 <box> or <unbox> 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<IrReturnableBlock, MutableList<IrExpression>>()
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)
}
})
}
}