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:
+43
-30
@@ -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.common.lower.irBlock
|
||||||
import org.jetbrains.kotlin.backend.konan.*
|
import org.jetbrains.kotlin.backend.konan.*
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.isBoxOrUnboxCall
|
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.IntArrayList
|
||||||
import org.jetbrains.kotlin.backend.konan.util.LongArrayList
|
import org.jetbrains.kotlin.backend.konan.util.LongArrayList
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
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 DevirtualizedCallee(val receiverType: DataFlowIR.Type, val callee: DataFlowIR.FunctionSymbol)
|
||||||
|
|
||||||
class DevirtualizedCallSite(val callee: DataFlowIR.FunctionSymbol, val possibleCallees: List<DevirtualizedCallee>)
|
class DevirtualizedCallSite(val callee: DataFlowIR.FunctionSymbol, val possibleCallees: List<DevirtualizedCallee>)
|
||||||
@@ -1291,26 +1319,6 @@ internal object DevirtualizationAnalysis {
|
|||||||
return this
|
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 {
|
fun <T : IrElement> IrStatementsBuilder<T>.irTemporary(value: IrExpression, tempName: String, type: IrType): IrVariable {
|
||||||
val temporary = IrVariableImpl(
|
val temporary = IrVariableImpl(
|
||||||
value.startOffset, value.endOffset, IrDeclarationOrigin.IR_TEMPORARY_VARIABLE, IrVariableSymbolImpl(),
|
value.startOffset, value.endOffset, IrDeclarationOrigin.IR_TEMPORARY_VARIABLE, IrVariableSymbolImpl(),
|
||||||
@@ -1323,15 +1331,19 @@ internal object DevirtualizationAnalysis {
|
|||||||
return temporary
|
return temporary
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : IrElement> IrStatementsBuilder<T>.irSplitCoercion(expression: IrExpression, tempName: String, actualType: IrType) =
|
// makes temporary val, in case tempName is specified
|
||||||
if (!expression.isBoxOrUnboxCall())
|
fun <T : IrElement> IrStatementsBuilder<T>.irSplitCoercion(expression: IrExpression, tempName: String?, actualType: IrType) =
|
||||||
PossiblyCoercedValue(irTemporary(expression, tempName, actualType), null)
|
if (expression.isBoxOrUnboxCall()) {
|
||||||
else {
|
|
||||||
val coercion = expression as IrCall
|
val coercion = expression as IrCall
|
||||||
PossiblyCoercedValue(
|
val argument = coercion.getValueArgument(0)!!
|
||||||
irTemporary(coercion.getValueArgument(0)!!, tempName,
|
val symbol = coercion.symbol
|
||||||
coercion.symbol.owner.explicitParameters.single().type)
|
if (tempName != null)
|
||||||
, coercion.symbol)
|
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,
|
fun getTypeConversion(actualType: DataFlowIR.FunctionParameter,
|
||||||
@@ -1457,8 +1469,9 @@ internal object DevirtualizationAnalysis {
|
|||||||
|
|
||||||
optimize && possibleCallees.size == 1 -> { // Monomorphic callsite.
|
optimize && possibleCallees.size == 1 -> { // Monomorphic callsite.
|
||||||
irBlock(expression) {
|
irBlock(expression) {
|
||||||
val parameters = expression.getArgumentsWithSymbols().mapIndexed { index, arg ->
|
val parameters = expression.getArgumentsWithSymbols().map { arg ->
|
||||||
irSplitCoercion(arg.second, "arg$index", arg.first.owner.type)
|
// 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)
|
+irDevirtualizedCall(expression, type, possibleCallees[0], parameters)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,19 +10,9 @@ fun interface Foo {
|
|||||||
fun baz(x: Any): Int = x.hashCode()
|
fun baz(x: Any): Int = x.hashCode()
|
||||||
|
|
||||||
// CHECK: define void @"kfun:#main(){}"()
|
// 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
|
// 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-box
|
||||||
// CHECK-NOT: Int-unbox
|
// CHECK-NOT: Int-unbox
|
||||||
// CHECK: ret void
|
// CHECK: ret void
|
||||||
|
|||||||
@@ -10,12 +10,8 @@ fun interface Foo<T> {
|
|||||||
fun baz(x: Any): Int = x.hashCode()
|
fun baz(x: Any): Int = x.hashCode()
|
||||||
|
|
||||||
// CHECK: define void @"kfun:#main(){}"()
|
// 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-NOT: Int-box
|
||||||
// CHECK: Int-unbox
|
|
||||||
// CHECK-NOT: Int-unbox
|
// CHECK-NOT: Int-unbox
|
||||||
// CHECK-NOT: Int-box
|
|
||||||
// CHECK: ret void
|
// CHECK: ret void
|
||||||
fun main() {
|
fun main() {
|
||||||
val foo: Foo<Int> = Foo(::baz)
|
val foo: Foo<Int> = Foo(::baz)
|
||||||
|
|||||||
Reference in New Issue
Block a user