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.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)
|
||||
|
||||
Reference in New Issue
Block a user