Don't create unnecessary temp vals during monomorphic callsite devirtualization

Merge-request: KT-MR-6633
Merged-by: Vladimir Sukharev <Vladimir.Sukharev@jetbrains.com>
This commit is contained in:
Vladimir Sukharev
2022-07-15 10:52:45 +00:00
committed by Space
parent 5b6afef67d
commit 2b7ae20ac1
3 changed files with 44 additions and 45 deletions
@@ -13,6 +13,7 @@ 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.optimizations.DevirtualizationAnalysis.irCoerce
import org.jetbrains.kotlin.backend.konan.util.IntArrayList
import org.jetbrains.kotlin.backend.konan.util.LongArrayList
import org.jetbrains.kotlin.descriptors.ClassKind
@@ -1263,6 +1264,33 @@ internal object DevirtualizationAnalysis {
}
private fun IrBuilderWithScope.irCoerce(value: IrExpression, coercion: IrFunctionSymbol?) =
if (coercion == null)
value
else irCall(coercion).apply {
addArguments(listOf(coercion.descriptor.explicitParameters.single() to value))
}
private fun IrBuilderWithScope.irCoerce(value: IrExpression, coercion: DataFlowIR.FunctionSymbol.Declared?) =
if (coercion == null)
value
else irCall(coercion.irFunction!!).apply {
putValueArgument(0, value)
}
sealed class PossiblyCoercedValue(private val coercion: IrFunctionSymbol?) {
abstract fun getValue(irBuilder: IrBuilderWithScope): IrExpression
fun getFullValue(irBuilder: IrBuilderWithScope): IrExpression = irBuilder.run { irCoerce(getValue(this), coercion) }
class OverVariable(val value: IrVariable, coercion: IrFunctionSymbol?) : PossiblyCoercedValue(coercion) {
override fun getValue(irBuilder: IrBuilderWithScope) = irBuilder.run { irGet(value) }
}
class OverExpression(val value: IrExpression, coercion: IrFunctionSymbol?) : PossiblyCoercedValue(coercion) {
override fun getValue(irBuilder: IrBuilderWithScope) = value
}
}
class DevirtualizedCallee(val receiverType: DataFlowIR.Type, val callee: DataFlowIR.FunctionSymbol)
class DevirtualizedCallSite(val callee: DataFlowIR.FunctionSymbol, val possibleCallees: List<DevirtualizedCallee>)
@@ -1291,26 +1319,6 @@ internal object DevirtualizationAnalysis {
return this
}
fun IrBuilderWithScope.irCoerce(value: IrExpression, coercion: IrFunctionSymbol?) =
if (coercion == null)
value
else irCall(coercion).apply {
addArguments(listOf(coercion.descriptor.explicitParameters.single() to value))
}
fun IrBuilderWithScope.irCoerce(value: IrExpression, coercion: DataFlowIR.FunctionSymbol.Declared?) =
if (coercion == null)
value
else irCall(coercion.irFunction!!).apply {
putValueArgument(0, value)
}
class PossiblyCoercedValue(val value: IrVariable, val coercion: IrFunctionSymbol?) {
fun getFullValue(irBuilder: IrBuilderWithScope) = irBuilder.run {
irCoerce(irGet(value), coercion)
}
}
fun <T : IrElement> IrStatementsBuilder<T>.irTemporary(value: IrExpression, tempName: String, type: IrType): IrVariable {
val temporary = IrVariableImpl(
value.startOffset, value.endOffset, IrDeclarationOrigin.IR_TEMPORARY_VARIABLE, IrVariableSymbolImpl(),
@@ -1323,15 +1331,19 @@ internal object DevirtualizationAnalysis {
return temporary
}
fun <T : IrElement> IrStatementsBuilder<T>.irSplitCoercion(expression: IrExpression, tempName: String, actualType: IrType) =
if (!expression.isBoxOrUnboxCall())
PossiblyCoercedValue(irTemporary(expression, tempName, actualType), null)
else {
// makes temporary val, in case tempName is specified
fun <T : IrElement> IrStatementsBuilder<T>.irSplitCoercion(expression: IrExpression, tempName: String?, actualType: IrType) =
if (expression.isBoxOrUnboxCall()) {
val coercion = expression as IrCall
PossiblyCoercedValue(
irTemporary(coercion.getValueArgument(0)!!, tempName,
coercion.symbol.owner.explicitParameters.single().type)
, coercion.symbol)
val argument = coercion.getValueArgument(0)!!
val symbol = coercion.symbol
if (tempName != null)
PossiblyCoercedValue.OverVariable(irTemporary(argument, tempName, symbol.owner.explicitParameters.single().type), symbol)
else PossiblyCoercedValue.OverExpression(argument, symbol)
} else {
if (tempName != null)
PossiblyCoercedValue.OverVariable(irTemporary(expression, tempName, actualType), null)
else PossiblyCoercedValue.OverExpression(expression, null)
}
fun getTypeConversion(actualType: DataFlowIR.FunctionParameter,
@@ -1457,8 +1469,9 @@ internal object DevirtualizationAnalysis {
optimize && possibleCallees.size == 1 -> { // Monomorphic callsite.
irBlock(expression) {
val parameters = expression.getArgumentsWithSymbols().mapIndexed { index, arg ->
irSplitCoercion(arg.second, "arg$index", arg.first.owner.type)
val parameters = expression.getArgumentsWithSymbols().map { arg ->
// Temporary val is not required here for a parameter, since each one is used for only one devirtualized callsite
irSplitCoercion(arg.second, tempName = null, arg.first.owner.type)
}
+irDevirtualizedCall(expression, type, possibleCallees[0], parameters)
}
@@ -10,19 +10,9 @@ fun interface Foo {
fun baz(x: Any): Int = x.hashCode()
// CHECK: define void @"kfun:#main(){}"()
// Boxing/unboxing need to be used now due to non-devirtualized call
// Boxing needs to be used now due to non-devirtualized call
// CHECK: Int-box
// TODO Remove two next checks, when advanced optimization of Int-unbox(Int-box(x)) would be done for snippet like:
// TODO VAR IR_TEMPORARY_VARIABLE name:arg0 type:kotlin.Any [val]
// TODO BLOCK type=kotlin.Any origin=null
// TODO CALL <Int-box>
// TODO GET_VAR 'val arg1: kotlin.Int [val]'
// TODO CALL <Int-unbox>
// TODO GET_VAR 'val arg0: kotlin.Any [val]'
// CHECK: Int-box
// CHECK: Int-unbox
// CHECK-NOT: Int-box
// CHECK-NOT: Int-unbox
// CHECK: ret void
@@ -10,12 +10,8 @@ fun interface Foo<T> {
fun baz(x: Any): Int = x.hashCode()
// CHECK: define void @"kfun:#main(){}"()
// Boxing/unboxing need to be used now due to non-devirtualized call
// CHECK: Int-box
// CHECK-NOT: Int-box
// CHECK: Int-unbox
// CHECK-NOT: Int-unbox
// CHECK-NOT: Int-box
// CHECK: ret void
fun main() {
val foo: Foo<Int> = Foo(::baz)