From 5ac3a160cad5aba8d988bf040c859e2a3e25c2e6 Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Tue, 24 Apr 2018 15:53:53 +0300 Subject: [PATCH] Implemented inlining of devirtualized bridges --- .../jetbrains/kotlin/backend/konan/Boxing.kt | 11 +- .../kotlin/backend/konan/ir/KonanIr.kt | 3 + .../backend/konan/llvm/BinaryInterface.kt | 11 + .../kotlin/backend/konan/llvm/DataLayout.kt | 4 + .../kotlin/backend/konan/llvm/IrToBitcode.kt | 7 +- .../backend/konan/optimizations/DFGBuilder.kt | 28 ++- .../konan/optimizations/Devirtualization.kt | 192 ++++++++++++++---- 7 files changed, 207 insertions(+), 49 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Boxing.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Boxing.kt index 1aa2b99c19d..e8f148ce632 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Boxing.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Boxing.kt @@ -26,12 +26,13 @@ import org.jetbrains.kotlin.ir.util.getPropertyGetter import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.types.KotlinType +internal fun KonanSymbols.getTypeConversion(actualType: KotlinType, expectedType: KotlinType) = + getTypeConversion(actualType.correspondingValueType, expectedType.correspondingValueType) + internal fun KonanSymbols.getTypeConversion( - actualType: KotlinType, - expectedType: KotlinType + actualValueType: ValueType?, + expectedValueType: ValueType? ): IrSimpleFunctionSymbol? { - val actualValueType = actualType.correspondingValueType - val expectedValueType = expectedType.correspondingValueType return when { actualValueType == expectedValueType -> null @@ -48,7 +49,7 @@ internal fun KonanSymbols.getTypeConversion( this.boxFunctions[actualValueType]!! } - else -> throw IllegalArgumentException("actual type is $actualType, expected $expectedType") + else -> throw IllegalArgumentException("actual type is $actualValueType, expected $expectedValueType") } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/KonanIr.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/KonanIr.kt index 74afc2e4f48..58f8687812a 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/KonanIr.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/KonanIr.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.backend.konan.ir +import org.jetbrains.kotlin.backend.konan.optimizations.DataFlowIR import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.ir.SourceManager @@ -188,6 +189,7 @@ class IrSuspendableExpressionImpl(startOffset: Int, endOffset: Int, type: Kotlin } internal interface IrPrivateFunctionCall : IrCall { + val dfgSymbol: DataFlowIR.FunctionSymbol.Declared val moduleDescriptor: ModuleDescriptor val totalFunctions: Int val functionIndex: Int @@ -199,6 +201,7 @@ internal class IrPrivateFunctionCallImpl(startOffset: Int, override val symbol: IrFunctionSymbol, override val descriptor: FunctionDescriptor, typeArgumentsCount: Int, + override val dfgSymbol: DataFlowIR.FunctionSymbol.Declared, override val moduleDescriptor: ModuleDescriptor, override val totalFunctions: Int, override val functionIndex: Int diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryInterface.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryInterface.kt index 1f1f52209e1..d51438c08a0 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryInterface.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryInterface.kt @@ -17,10 +17,13 @@ package org.jetbrains.kotlin.backend.konan.llvm import llvm.LLVMTypeRef +import llvm.LLVMVoidType +import org.jetbrains.kotlin.backend.common.descriptors.allParameters import org.jetbrains.kotlin.backend.konan.descriptors.isAbstract import org.jetbrains.kotlin.backend.konan.irasdescriptors.* import org.jetbrains.kotlin.backend.konan.isValueType import org.jetbrains.kotlin.backend.konan.library.KonanLibraryReader +import org.jetbrains.kotlin.backend.konan.optimizations.DataFlowIR import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor @@ -255,6 +258,14 @@ internal fun RuntimeAware.getLlvmFunctionType(function: FunctionDescriptor): LLV return functionType(returnType, isVarArg = false, paramTypes = *paramTypes.toTypedArray()) } +internal fun RuntimeAware.getLlvmFunctionType(symbol: DataFlowIR.FunctionSymbol): LLVMTypeRef { + val returnType = if (symbol.returnsUnit) voidType else getLLVMType(symbol.returnType) + val paramTypes = ArrayList(symbol.parameterTypes.map { getLLVMType(it) }) + if (isObjectType(returnType)) paramTypes.add(kObjHeaderPtrPtr) + + return functionType(returnType, isVarArg = false, paramTypes = *paramTypes.toTypedArray()) +} + internal val ClassDescriptor.typeInfoSymbolName: String get() { assert (this.isExported()) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DataLayout.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DataLayout.kt index c2db4a8f9b8..be277f8737a 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DataLayout.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DataLayout.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.backend.konan.llvm import llvm.* import org.jetbrains.kotlin.backend.konan.ValueType import org.jetbrains.kotlin.backend.konan.isRepresentedAs +import org.jetbrains.kotlin.backend.konan.optimizations.DataFlowIR import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.typeUtil.isNothing import org.jetbrains.kotlin.types.typeUtil.isUnit @@ -50,6 +51,9 @@ internal fun RuntimeAware.getLLVMType(type: KotlinType): LLVMTypeRef { return this.kObjHeaderPtr } +internal fun RuntimeAware.getLLVMType(type: DataFlowIR.Type) = + type.correspondingValueType?.let { valueTypes[it]!! } ?: this.kObjHeaderPtr + internal fun RuntimeAware.getLLVMReturnType(type: KotlinType): LLVMTypeRef { return when { type.isUnit() -> LLVMVoidType()!! diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index ef1e9074078..5d5c9d2f471 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -2009,20 +2009,19 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map, resultLifetime: Lifetime): LLVMValueRef { - val target = callee.symbol.owner as IrFunction - + val dfgSymbol = callee.dfgSymbol val functionIndex = callee.functionIndex val function = if (callee.moduleDescriptor == context.irModule!!.descriptor) { codegen.llvmFunction(context.privateFunctions[functionIndex]) } else { context.llvm.externalFunction( callee.moduleDescriptor.privateFunctionSymbolName(functionIndex), - codegen.getLlvmFunctionType(target), + codegen.getLlvmFunctionType(dfgSymbol), callee.moduleDescriptor.llvmSymbolOrigin ) } - return call(target, function, args, resultLifetime) + return call(callee.symbol.owner as IrFunction, function, args, resultLifetime) } //-------------------------------------------------------------------------// diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt index 0cc8f25ea14..91b7d5d0d86 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt @@ -23,11 +23,15 @@ import org.jetbrains.kotlin.backend.common.pop import org.jetbrains.kotlin.backend.common.push import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.descriptors.allOverriddenDescriptors +import org.jetbrains.kotlin.backend.konan.correspondingValueType import org.jetbrains.kotlin.backend.konan.descriptors.isInterface import org.jetbrains.kotlin.backend.konan.descriptors.target +import org.jetbrains.kotlin.backend.konan.getTypeConversion import org.jetbrains.kotlin.backend.konan.ir.IrSuspendableExpression import org.jetbrains.kotlin.backend.konan.ir.IrSuspensionPoint +import org.jetbrains.kotlin.backend.konan.ir.KonanIr import org.jetbrains.kotlin.backend.konan.irasdescriptors.* +import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols import org.jetbrains.kotlin.backend.konan.llvm.functionName import org.jetbrains.kotlin.backend.konan.llvm.localHash import org.jetbrains.kotlin.descriptors.ClassKind @@ -48,16 +52,32 @@ import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.typeUtil.isNothing import org.jetbrains.kotlin.types.typeUtil.isUnit import org.jetbrains.kotlin.util.OperatorNameConventions +import org.jetbrains.kotlin.types.typeUtil.makeNotNullable -private fun computeErasure(type: KotlinType, context: Context, erasure: MutableList) { - val irClass = context.ir.getClass(type) +private fun getClassWithBoxingIncluded(type: KotlinType, ir: KonanIr): ClassDescriptor? { + /* + * Some primitive types can be null and some can't. Those that can't must be replaced with the corresponding box. + * Int -> Int + * Int? -> IntBox + * but + * CPointer -> CPointer + * CPointer? -> CPointer + */ + return if (type.correspondingValueType == null && type.makeNotNullable().correspondingValueType != null) + ir.getClass(ir.symbols.getTypeConversion(type.makeNotNullable(), type)!!.descriptor.returnType!!)!! + else + ir.getClass(type) +} + +private fun computeErasure(type: KotlinType, ir: KonanIr, erasure: MutableList) { + val irClass = getClassWithBoxingIncluded(type, ir) if (irClass != null) { erasure += irClass } else { val descriptor = type.constructor.declarationDescriptor if (descriptor is TypeParameterDescriptor) { descriptor.upperBounds.forEach { - computeErasure(it, context, erasure) + computeErasure(it, ir, erasure) } } else { TODO(descriptor.toString()) @@ -67,7 +87,7 @@ private fun computeErasure(type: KotlinType, context: Context, erasure: MutableL internal fun KotlinType.erasure(context: Context): List { val result = mutableListOf() - computeErasure(this, context, result) + computeErasure(this, context.ir, result) return result } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/Devirtualization.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/Devirtualization.kt index 467226d4ff3..259d57765d1 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/Devirtualization.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/Devirtualization.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.backend.konan.optimizations import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext +import org.jetbrains.kotlin.backend.common.descriptors.explicitParameters import org.jetbrains.kotlin.backend.common.ir.ir2stringWhole import org.jetbrains.kotlin.backend.common.pop import org.jetbrains.kotlin.backend.common.push @@ -26,20 +27,25 @@ import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.descriptors.isInterface import org.jetbrains.kotlin.backend.konan.ir.IrPrivateClassReferenceImpl import org.jetbrains.kotlin.backend.konan.ir.IrPrivateFunctionCallImpl -import org.jetbrains.kotlin.backend.konan.llvm.* import org.jetbrains.kotlin.backend.konan.lower.nullConst import org.jetbrains.kotlin.backend.konan.objcexport.getErasedTypeClass import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.IrModuleFragment +import org.jetbrains.kotlin.ir.declarations.IrVariable +import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrExpression -import org.jetbrains.kotlin.ir.expressions.copyTypeArgumentsFrom import org.jetbrains.kotlin.ir.expressions.getValueArgument import org.jetbrains.kotlin.ir.expressions.impl.IrBranchImpl import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrWhenImpl +import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.impl.IrClassSymbolImpl +import org.jetbrains.kotlin.ir.util.addArguments +import org.jetbrains.kotlin.ir.util.getArguments import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.types.KotlinType import java.util.* @@ -517,7 +523,7 @@ internal object Devirtualization { } val result = mutableMapOf>() - val nothing = symbolTable.mapClass(context.ir.symbols.nothing.owner) + val nothing = symbolTable.classMap[context.ir.symbols.nothing.owner] functions.values .asSequence() .filter { constraintGraph.functions.containsKey(it.symbol) } @@ -538,7 +544,7 @@ internal object Devirtualization { val possibleReceivers = allTypes.withIndex() .filter { virtualCallSiteReceivers.receiver.types[it.index] } .map { it.value } - .filter { it != nothing } + .filterNot { it == nothing } val map = virtualCallSiteReceivers.devirtualizedCallees.associateBy({ it.receiverType }, { it }) result[virtualCall] = DevirtualizedCallSite(virtualCall.callee.resolved(), possibleReceivers.map { receiverType -> assert (map[receiverType] != null) { @@ -900,20 +906,154 @@ internal object Devirtualization { .asSequence() .filter { it.key.irCallSite != null } .associate { it.key.irCallSite!! to it.value } - Devirtualization.devirtualize(irModule, context, devirtualizedCallSites) + Devirtualization.devirtualize(irModule, context, moduleDFG, externalModulesDFG, devirtualizedCallSites) return AnalysisResult(devirtualizationAnalysisResult) } private fun devirtualize(irModule: IrModuleFragment, context: Context, + moduleDFG: ModuleDFG, externalModulesDFG: ExternalModulesDFG, devirtualizedCallSites: Map) { val nativePtrType = context.builtIns.nativePtr.defaultType val nativePtrEqualityOperatorSymbol = context.ir.symbols.areEqualByValue.single { it.descriptor.valueParameters[0].type == nativePtrType } val optimize = context.shouldOptimize() + val boxFunctions = ValueType.values().associate { context.ir.symbols.boxFunctions[it]!! to it } + val unboxFunctions = ValueType.values().associate { context.ir.symbols.getUnboxFunction(it) to it } + + fun DataFlowIR.Type.resolved(): DataFlowIR.Type.Declared { + if (this is DataFlowIR.Type.Declared) return this + val hash = (this as DataFlowIR.Type.External).hash + return externalModulesDFG.publicTypes[hash] ?: error("Unable to resolve exported type $hash") + } + + fun DataFlowIR.FunctionSymbol.resolved(): DataFlowIR.FunctionSymbol { + if (this is DataFlowIR.FunctionSymbol.External) + return externalModulesDFG.publicFunctions[this.hash] ?: this + return this + } + + fun IrExpression.isBoxOrUnboxCall() = this is IrCall && (boxFunctions[symbol] != null || unboxFunctions[symbol] != null) + + fun IrBuilderWithScope.irCoerce(value: IrExpression, coercion: IrFunctionSymbol?) = + if (coercion == null) + value + else irCall(coercion).apply { + addArguments(listOf(coercion.descriptor.explicitParameters.single() to value)) + } + + class PossiblyCoercedValue(val value: IrVariable, val coercion: IrFunctionSymbol?) { + fun getFullValue(irBuilder: IrBuilderWithScope) = irBuilder.run { + irCoerce(irGet(value.symbol), coercion) + } + } + + fun IrStatementsBuilder.irSplitCoercion(expression: IrExpression) = + if (!expression.isBoxOrUnboxCall()) + PossiblyCoercedValue(irTemporary(expression), null) + else { + val coercion = expression as IrCall + PossiblyCoercedValue( + irTemporary(irImplicitCast(coercion.getArguments().single().second, + coercion.descriptor.explicitParameters.single().type)) + , coercion.symbol) + } + + fun assertCoercionsMatch(coercion1: IrFunctionSymbol, coercion2: IrFunctionSymbol) { + boxFunctions[coercion1]?.let { assert (unboxFunctions[coercion2] == it) + { "Incosistent coercions: ${coercion1.descriptor}, ${coercion2.descriptor}" } + } + unboxFunctions[coercion1]?.let { assert (boxFunctions[coercion2] == it) + { "Incosistent coercions: ${coercion1.descriptor}, ${coercion2.descriptor}" } + } + } + + fun IrBuilderWithScope.irCoerceIfNeeded(type: DataFlowIR.Type.Declared, targetType: DataFlowIR.Type.Declared, + possiblyCoercedValue: PossiblyCoercedValue): IrExpression { + val value = possiblyCoercedValue.value + val prevCoercion = possiblyCoercedValue.coercion + + val coercion = context.ir.symbols.getTypeConversion(type.correspondingValueType, targetType.correspondingValueType) + ?: return possiblyCoercedValue.getFullValue(this) + if (prevCoercion == null) + return irCoerce(irGet(value.symbol), coercion) + assertCoercionsMatch(coercion, prevCoercion) + return irGet(value.symbol) + } + + fun IrBuilderWithScope.irDevirtualizedCall(callee: IrCall, actualType: KotlinType, + devirtualizedCallee: DataFlowIR.FunctionSymbol.Declared) = + IrPrivateFunctionCallImpl( + startOffset = startOffset, + endOffset = endOffset, + type = actualType, + symbol = callee.symbol, + descriptor = callee.descriptor, + typeArgumentsCount = callee.typeArgumentsCount, + dfgSymbol = devirtualizedCallee, + totalFunctions = devirtualizedCallee.module.numberOfFunctions, + moduleDescriptor = devirtualizedCallee.module.descriptor, + functionIndex = devirtualizedCallee.symbolTableIndex + ) + + fun IrBuilderWithScope.irDevirtualizedCall(callee: IrCall, actualType: KotlinType, + actualCallee: DataFlowIR.FunctionSymbol.Declared, + receiver: IrVariable, + extensionReceiver: PossiblyCoercedValue?, + parameters: Map) = + actualCallee.bridgeTarget.let { + if (it == null) + irDevirtualizedCall(callee, actualType, actualCallee).apply { + this.dispatchReceiver = irGet(receiver.symbol) + this.extensionReceiver = extensionReceiver?.getFullValue(this@irDevirtualizedCall) + callee.descriptor.valueParameters.forEach { + putValueArgument(it.index, parameters[it]!!.getFullValue(this@irDevirtualizedCall)) + } + } + else { + val bridgeTarget = it.resolved() as DataFlowIR.FunctionSymbol.Declared + val callResult = irDevirtualizedCall(callee, actualType, bridgeTarget).apply { + this.dispatchReceiver = irGet(receiver.symbol) + this.extensionReceiver = extensionReceiver?.let { + irCoerceIfNeeded( + type = actualCallee.parameterTypes[1].resolved(), + targetType = bridgeTarget.parameterTypes[1].resolved(), + possiblyCoercedValue = it + ) + } + val startIndex = if (extensionReceiver == null) 1 else 2 + callee.descriptor.valueParameters.forEach { + this.putValueArgument(it.index, + irCoerceIfNeeded( + type = actualCallee.parameterTypes[startIndex + it.index].resolved(), + targetType = bridgeTarget.parameterTypes[startIndex + it.index].resolved(), + possiblyCoercedValue = parameters[it]!! + ) + ) + } + } + val returnCoercion = context.ir.symbols.getTypeConversion( + bridgeTarget.returnType.resolved().correspondingValueType, + actualCallee.returnType.resolved().correspondingValueType) + irCoerce(callResult, returnCoercion) + } + } irModule.transformChildrenVoid(object: IrElementTransformerVoidWithContext() { override fun visitCall(expression: IrCall): IrExpression { expression.transformChildrenVoid(this) + if (expression.isBoxOrUnboxCall()) { + val arg = expression.getArguments().single().second + val uncastedArg = if (arg is IrTypeOperatorCall && arg.operator == IrTypeOperator.IMPLICIT_CAST) + arg.argument + else arg + if (!uncastedArg.isBoxOrUnboxCall()) return expression + val argarg = (uncastedArg as IrCall).getArguments().single().second + if (boxFunctions[expression.symbol].let { it != null && it == unboxFunctions[uncastedArg.symbol] } + || unboxFunctions[expression.symbol].let { it != null && it == boxFunctions[uncastedArg.symbol] }) + return argarg + return expression + } + val devirtualizedCallSite = devirtualizedCallSites[expression] val possibleCallees = devirtualizedCallSite?.possibleCallees if (possibleCallees == null @@ -951,19 +1091,22 @@ internal object Devirtualization { optimize && possibleCallees.size == 1 -> { // Monomorphic callsite. val actualCallee = possibleCallees[0].callee as DataFlowIR.FunctionSymbol.Declared - irDevirtualizedCall(expression, type, actualCallee).apply { - this.dispatchReceiver = dispatchReceiver - this.extensionReceiver = expression.extensionReceiver - expression.descriptor.valueParameters.forEach { - this.putValueArgument(it.index, expression.getValueArgument(it)) + irBlock(expression) { + val receiver = irTemporary(dispatchReceiver, "receiver") + val extensionReceiver = expression.extensionReceiver?.let { irSplitCoercion(it) } + val parameters = expression.descriptor.valueParameters.associate { + it to irSplitCoercion(expression.getValueArgument(it)!!) } + +irDevirtualizedCall(expression, type, actualCallee, receiver, extensionReceiver, parameters) } } else -> irBlock(expression) { val receiver = irTemporary(dispatchReceiver, "receiver") - val extensionReceiver = expression.extensionReceiver?.let { irTemporary(it, "extensionReceiver") } - val parameters = expression.descriptor.valueParameters.associate { it to irTemporary(expression.getValueArgument(it)!!) } + val extensionReceiver = expression.extensionReceiver?.let { irSplitCoercion(it) } + val parameters = expression.descriptor.valueParameters.associate { + it to irSplitCoercion(expression.getValueArgument(it)!!) + } val typeInfo = irTemporary(irCall(context.ir.symbols.getObjectTypeInfo).apply { putValueArgument(0, irGet(receiver.symbol)) }) @@ -993,13 +1136,7 @@ internal object Devirtualization { startOffset = startOffset, endOffset = endOffset, condition = condition, - result = irDevirtualizedCall(expression, type, actualCallee).apply { - this.dispatchReceiver = irGet(receiver.symbol) - this.extensionReceiver = extensionReceiver?.let { irGet(it.symbol) } - expression.descriptor.valueParameters.forEach { - putValueArgument(it.index, irGet(parameters[it]!!.symbol)) - } - } + result = irDevirtualizedCall(expression, type, actualCallee, receiver, extensionReceiver, parameters) ) } if (!optimize) { // Add else branch throwing exception for debug purposes. @@ -1030,23 +1167,6 @@ internal object Devirtualization { } } } - - fun IrBuilderWithScope.irDevirtualizedCall(callee: IrCall, actualType: KotlinType, - devirtualizedCallee: DataFlowIR.FunctionSymbol.Declared) = - IrPrivateFunctionCallImpl( - startOffset = startOffset, - endOffset = endOffset, - type = actualType, - symbol = callee.symbol, - descriptor = callee.descriptor, - typeArgumentsCount = callee.typeArgumentsCount, - moduleDescriptor = devirtualizedCallee.module.descriptor, - totalFunctions = devirtualizedCallee.module.numberOfFunctions, - functionIndex = devirtualizedCallee.symbolTableIndex - ).apply { - copyTypeArgumentsFrom(callee) - } - }) } } \ No newline at end of file