Implemented inlining of devirtualized bridges

This commit is contained in:
Igor Chevdar
2018-04-24 15:53:53 +03:00
parent 5212fd8189
commit 5ac3a160ca
7 changed files with 207 additions and 49 deletions
@@ -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")
}
}
@@ -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
@@ -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())
@@ -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()!!
@@ -2009,20 +2009,19 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
//-------------------------------------------------------------------------//
private fun evaluatePrivateFunctionCall(callee: IrPrivateFunctionCall, args: List<LLVMValueRef>, 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)
}
//-------------------------------------------------------------------------//
@@ -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<ClassDescriptor>) {
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<ClassDescriptor>) {
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<ClassDescriptor> {
val result = mutableListOf<ClassDescriptor>()
computeErasure(this, context, result)
computeErasure(this, context.ir, result)
return result
}
@@ -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<DataFlowIR.Node.VirtualCall, Pair<DevirtualizedCallSite, DataFlowIR.FunctionSymbol>>()
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<IrCall, DevirtualizedCallSite>) {
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 <T : IrElement> IrStatementsBuilder<T>.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<ValueParameterDescriptor, PossiblyCoercedValue>) =
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)
}
})
}
}