From 2b7ae20ac17ec242b73f96ecdee0c82eaebee983 Mon Sep 17 00:00:00 2001 From: Vladimir Sukharev Date: Fri, 15 Jul 2022 10:52:45 +0000 Subject: [PATCH] Don't create unnecessary temp vals during monomorphic callsite devirtualization Merge-request: KT-MR-6633 Merged-by: Vladimir Sukharev --- .../optimizations/DevirtualizationAnalysis.kt | 73 +++++++++++-------- .../tests/filecheck/kt49847_sam_Any.kt | 12 +-- .../filecheck/kt49847_sam_Any_generic.kt | 4 - 3 files changed, 44 insertions(+), 45 deletions(-) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DevirtualizationAnalysis.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DevirtualizationAnalysis.kt index bdfe747c522..a5744b22b97 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DevirtualizationAnalysis.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DevirtualizationAnalysis.kt @@ -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) @@ -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 IrStatementsBuilder.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 IrStatementsBuilder.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 IrStatementsBuilder.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) } diff --git a/kotlin-native/backend.native/tests/filecheck/kt49847_sam_Any.kt b/kotlin-native/backend.native/tests/filecheck/kt49847_sam_Any.kt index 7ce336a99f6..c213e98e49d 100644 --- a/kotlin-native/backend.native/tests/filecheck/kt49847_sam_Any.kt +++ b/kotlin-native/backend.native/tests/filecheck/kt49847_sam_Any.kt @@ -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 -// TODO GET_VAR 'val arg1: kotlin.Int [val]' -// TODO CALL -// 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 diff --git a/kotlin-native/backend.native/tests/filecheck/kt49847_sam_Any_generic.kt b/kotlin-native/backend.native/tests/filecheck/kt49847_sam_Any_generic.kt index 4303c3c4978..2a93439f4b3 100644 --- a/kotlin-native/backend.native/tests/filecheck/kt49847_sam_Any_generic.kt +++ b/kotlin-native/backend.native/tests/filecheck/kt49847_sam_Any_generic.kt @@ -10,12 +10,8 @@ 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 -// 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 = Foo(::baz)