Turned on bridges inlining

This commit is contained in:
Igor Chevdar
2018-09-17 19:06:34 +03:00
parent 973cc7bbdb
commit 2065b79a7b
7 changed files with 206 additions and 149 deletions
@@ -127,7 +127,12 @@ class IrFileImpl(entry: SourceManager.FileEntry) : IrFile {
//-----------------------------------------------------------------------------// //-----------------------------------------------------------------------------//
internal interface IrPrivateFunctionCall : IrCall { internal interface IrPrivateFunctionCall : IrExpression {
val valueArgumentsCount: Int
fun getValueArgument(index: Int): IrExpression?
fun putValueArgument(index: Int, valueArgument: IrExpression?)
fun removeValueArgument(index: Int)
val virtualCallee: IrCall? val virtualCallee: IrCall?
val dfgSymbol: DataFlowIR.FunctionSymbol.Declared val dfgSymbol: DataFlowIR.FunctionSymbol.Declared
val moduleDescriptor: ModuleDescriptor val moduleDescriptor: ModuleDescriptor
@@ -138,31 +143,47 @@ internal interface IrPrivateFunctionCall : IrCall {
internal class IrPrivateFunctionCallImpl(startOffset: Int, internal class IrPrivateFunctionCallImpl(startOffset: Int,
endOffset: Int, endOffset: Int,
type: IrType, type: IrType,
override val symbol: IrFunctionSymbol, override val valueArgumentsCount: Int,
override val descriptor: FunctionDescriptor,
override val virtualCallee: IrCall?, override val virtualCallee: IrCall?,
typeArgumentsCount: Int,
override val dfgSymbol: DataFlowIR.FunctionSymbol.Declared, override val dfgSymbol: DataFlowIR.FunctionSymbol.Declared,
override val moduleDescriptor: ModuleDescriptor, override val moduleDescriptor: ModuleDescriptor,
override val totalFunctions: Int, override val totalFunctions: Int,
override val functionIndex: Int override val functionIndex: Int
) : IrPrivateFunctionCall, IrCallWithIndexedArgumentsBase( ) : IrPrivateFunctionCall, IrExpressionBase(startOffset, endOffset, type) {
startOffset,
endOffset,
type,
typeArgumentsCount = typeArgumentsCount,
valueArgumentsCount = symbol.descriptor.valueParameters.size
) {
override val superQualifierSymbol: IrClassSymbol? override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R {
get() = null return visitor.visitExpression(this, data)
}
override val superQualifier: ClassDescriptor? private val argumentsByParameterIndex: Array<IrExpression?> = arrayOfNulls(valueArgumentsCount)
get() = null
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R = override fun getValueArgument(index: Int): IrExpression? {
visitor.visitCall(this, data) if (index >= valueArgumentsCount) {
throw AssertionError("$this: No such value argument slot: $index")
}
return argumentsByParameterIndex[index]
}
override fun putValueArgument(index: Int, valueArgument: IrExpression?) {
if (index >= valueArgumentsCount) {
throw AssertionError("$this: No such value argument slot: $index")
}
argumentsByParameterIndex[index] = valueArgument
}
override fun removeValueArgument(index: Int) {
argumentsByParameterIndex[index] = null
}
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {
argumentsByParameterIndex.forEach { it?.accept(visitor, data) }
}
override fun <D> transformChildren(transformer: IrElementTransformer<D>, data: D) {
argumentsByParameterIndex.forEachIndexed { i, irExpression ->
argumentsByParameterIndex[i] = irExpression?.transform(transformer, data)
}
}
} }
internal interface IrPrivateClassReference : IrClassReference { internal interface IrPrivateClassReference : IrClassReference {
@@ -258,8 +258,8 @@ internal fun RuntimeAware.getLlvmFunctionType(function: FunctionDescriptor): LLV
} }
internal fun RuntimeAware.getLlvmFunctionType(symbol: DataFlowIR.FunctionSymbol): LLVMTypeRef { internal fun RuntimeAware.getLlvmFunctionType(symbol: DataFlowIR.FunctionSymbol): LLVMTypeRef {
val returnType = if (symbol.returnsUnit) voidType else getLLVMType(symbol.returnType) val returnType = if (symbol.returnsUnit) voidType else getLLVMType(symbol.returnParameter.type)
val paramTypes = ArrayList(symbol.parameterTypes.map { getLLVMType(it) }) val paramTypes = ArrayList(symbol.parameters.map { getLLVMType(it.type) })
if (isObjectType(returnType)) paramTypes.add(kObjHeaderPtrPtr) if (isObjectType(returnType)) paramTypes.add(kObjHeaderPtrPtr)
return functionType(returnType, isVarArg = false, paramTypes = *paramTypes.toTypedArray()) return functionType(returnType, isVarArg = false, paramTypes = *paramTypes.toTypedArray())
@@ -845,6 +845,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
is IrSuspendableExpression -> is IrSuspendableExpression ->
return evaluateSuspendableExpression (value) return evaluateSuspendableExpression (value)
is IrSuspensionPoint -> return evaluateSuspensionPoint (value) is IrSuspensionPoint -> return evaluateSuspensionPoint (value)
is IrPrivateFunctionCall -> return evaluatePrivateFunctionCall (value)
is IrPrivateClassReference -> is IrPrivateClassReference ->
return evaluatePrivateClassReference (value) return evaluatePrivateClassReference (value)
else -> { else -> {
@@ -2092,9 +2093,6 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
return evaluateIntrinsicCall(callee, argsWithContinuationIfNeeded) return evaluateIntrinsicCall(callee, argsWithContinuationIfNeeded)
} }
if (callee is IrPrivateFunctionCall)
return evaluatePrivateFunctionCall(callee, argsWithContinuationIfNeeded, callee.virtualCallee?.let { resultLifetime(it) } ?: resultLifetime)
when { when {
descriptor.origin == IrDeclarationOrigin.IR_BUILTINS_STUB -> descriptor.origin == IrDeclarationOrigin.IR_BUILTINS_STUB ->
return evaluateOperatorCall(callee, argsWithContinuationIfNeeded) return evaluateOperatorCall(callee, argsWithContinuationIfNeeded)
@@ -2108,7 +2106,14 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
private fun evaluatePrivateFunctionCall(callee: IrPrivateFunctionCall, args: List<LLVMValueRef>, resultLifetime: Lifetime): LLVMValueRef { private fun evaluatePrivateFunctionCall(callee: IrPrivateFunctionCall): LLVMValueRef {
val args = (0 until callee.valueArgumentsCount).map { index ->
callee.getValueArgument(index)?.let { evaluateExpression(it) }
?: run {
assert(index == callee.valueArgumentsCount - 1) { "Only last argument may be null - for suspend functions" }
getContinuation()
}
}
val dfgSymbol = callee.dfgSymbol val dfgSymbol = callee.dfgSymbol
val functionIndex = callee.functionIndex val functionIndex = callee.functionIndex
val function = if (callee.moduleDescriptor == context.irModule!!.descriptor) { val function = if (callee.moduleDescriptor == context.irModule!!.descriptor) {
@@ -2121,7 +2126,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
) )
} }
return call(callee.symbol.owner, function, args, resultLifetime) return call(dfgSymbol, function, args, resultLifetime = Lifetime.GLOBAL)
} }
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
@@ -2567,6 +2572,25 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
return result return result
} }
// TODO: it seems to be much more reliable to get args as a mapping from parameter descriptor to LLVM value,
// instead of a plain list.
// In such case it would be possible to check that all args are available and in the correct order.
// However, it currently requires some refactoring to be performed.
private fun call(symbol: DataFlowIR.FunctionSymbol, function: LLVMValueRef, args: List<LLVMValueRef>,
resultLifetime: Lifetime): LLVMValueRef {
val result = call(function, args, resultLifetime)
if (symbol.returnsNothing) {
functionGenerationContext.unreachable()
}
if (LLVMGetReturnType(getFunctionType(function)) == voidType) {
return codegen.theUnitInstanceRef.llvm
}
return result
}
private fun call(function: LLVMValueRef, args: List<LLVMValueRef>, private fun call(function: LLVMValueRef, args: List<LLVMValueRef>,
resultLifetime: Lifetime = Lifetime.IRRELEVANT): LLVMValueRef { resultLifetime: Lifetime = Lifetime.IRRELEVANT): LLVMValueRef {
return functionGenerationContext.call(function, args, resultLifetime, currentCodeContext.exceptionHandler) return functionGenerationContext.call(function, args, resultLifetime, currentCodeContext.exceptionHandler)
@@ -300,13 +300,26 @@ internal object DFGSerializer {
} }
} }
class FunctionSymbolBase(val parameterTypes: IntArray, val returnType: Int, val attributes: Int, val escapes: Int?, val pointsTo: IntArray?) { class FunctionParameter(val type: Int, val boxFunction: Int?, val unboxFunction: Int?) {
constructor(data: ArraySlice) : this(data.readIntArray(), data.readInt(), data.readInt(), data.readNullableInt(), data.readNullable { readIntArray() }) constructor(data: ArraySlice) : this(data.readInt(), data.readNullableInt(), data.readNullableInt())
fun write(result: ArraySlice) { fun write(result: ArraySlice) {
result.writeIntArray(parameterTypes) result.writeInt(type)
result.writeInt(returnType) result.writeNullableInt(boxFunction)
result.writeNullableInt(unboxFunction)
}
}
class FunctionSymbolBase(val parameters: Array<FunctionParameter>, val returnParameter: FunctionParameter,
val attributes: Int, val escapes: Int?, val pointsTo: IntArray?) {
constructor(data: ArraySlice) : this(data.readArray { FunctionParameter(data) }, FunctionParameter(data), data.readInt(),
data.readNullableInt(), data.readNullable { readIntArray() })
fun write(result: ArraySlice) {
result.writeArray(parameters) { it.write(this) }
returnParameter.write(result)
result.writeInt(attributes) result.writeInt(attributes)
result.writeNullableInt(escapes) result.writeNullableInt(escapes)
result.writeNullable(pointsTo) { writeIntArray(it) } result.writeNullable(pointsTo) { writeIntArray(it) }
@@ -814,10 +827,14 @@ internal object DFGSerializer {
.sortedBy { it.value } .sortedBy { it.value }
.map { .map {
fun buildFunctionParameter(parameter: DataFlowIR.FunctionParameter) =
FunctionParameter(typeMap[parameter.type]!!, parameter.boxFunction?.let { functionSymbolMap[it]!! },
parameter.unboxFunction?.let { functionSymbolMap[it]!! })
fun buildFunctionSymbolBase(symbol: DataFlowIR.FunctionSymbol) = fun buildFunctionSymbolBase(symbol: DataFlowIR.FunctionSymbol) =
FunctionSymbolBase( FunctionSymbolBase(
symbol.parameterTypes.map { typeMap[it]!! }.toIntArray(), symbol.parameters.map { buildFunctionParameter(it) }.toTypedArray(),
typeMap[symbol.returnType]!!, buildFunctionParameter(symbol.returnParameter),
symbol.attributes, symbol.attributes,
symbol.escapes, symbol.escapes,
symbol.pointsTo symbol.pointsTo
@@ -1016,8 +1033,6 @@ internal object DFGSerializer {
module, symbolTableIndex, attributes, null, private.name) module, symbolTableIndex, attributes, null, private.name)
} }
}.apply { }.apply {
parameterTypes = it.base.parameterTypes.map { types[it] }.toTypedArray()
returnType = types[it.base.returnType]
escapes = it.base.escapes escapes = it.base.escapes
pointsTo = it.base.pointsTo pointsTo = it.base.pointsTo
} }
@@ -1043,9 +1058,15 @@ internal object DFGSerializer {
} }
} }
fun buildFunctionParameter(parameter: FunctionParameter) =
DataFlowIR.FunctionParameter(types[parameter.type], parameter.boxFunction?.let { functionSymbols[it] },
parameter.unboxFunction?.let { functionSymbols[it] })
symbolTable.functionSymbols.forEachIndexed { index, symbol -> symbolTable.functionSymbols.forEachIndexed { index, symbol ->
val deserializedSymbol = functionSymbols[index] as? DataFlowIR.FunctionSymbol.Declared val deserializedSymbol = functionSymbols[index]
?: return@forEachIndexed deserializedSymbol.parameters = symbol.base.parameters.map { buildFunctionParameter(it) }.toTypedArray()
deserializedSymbol.returnParameter = buildFunctionParameter(symbol.base.returnParameter)
deserializedSymbol as? DataFlowIR.FunctionSymbol.Declared ?: return@forEachIndexed
val bridgeTarget = if (deserializedSymbol is DataFlowIR.FunctionSymbol.Public) val bridgeTarget = if (deserializedSymbol is DataFlowIR.FunctionSymbol.Public)
symbol.public!!.bridgeTarget symbol.public!!.bridgeTarget
else else
@@ -121,9 +121,11 @@ internal object DataFlowIR {
val RETURNS_NOTHING = 4 val RETURNS_NOTHING = 4
} }
class FunctionParameter(val type: Type, val boxFunction: FunctionSymbol?, val unboxFunction: FunctionSymbol?)
abstract class FunctionSymbol(val attributes: Int, val name: String?) { abstract class FunctionSymbol(val attributes: Int, val name: String?) {
lateinit var parameterTypes: Array<Type> lateinit var parameters: Array<FunctionParameter>
lateinit var returnType: Type lateinit var returnParameter: FunctionParameter
val isGlobalInitializer = attributes.and(FunctionAttributes.IS_GLOBAL_INITIALIZER) != 0 val isGlobalInitializer = attributes.and(FunctionAttributes.IS_GLOBAL_INITIALIZER) != 0
val returnsUnit = attributes.and(FunctionAttributes.RETURNS_UNIT) != 0 val returnsUnit = attributes.and(FunctionAttributes.RETURNS_UNIT) != 0
@@ -266,7 +268,7 @@ internal object DataFlowIR {
fun debugOutput() { fun debugOutput() {
println("FUNCTION $symbol") println("FUNCTION $symbol")
println("Params: ${symbol.parameterTypes.contentToString()}") println("Params: ${symbol.parameters.contentToString()}")
val ids = body.nodes.withIndex().associateBy({ it.value }, { it.index }) val ids = body.nodes.withIndex().associateBy({ it.value }, { it.index })
body.nodes.forEach { body.nodes.forEach {
println(" NODE #${ids[it]!!}") println(" NODE #${ids[it]!!}")
@@ -555,6 +557,12 @@ internal object DataFlowIR {
} }
} }
private fun mapTypeToFunctionParameter(type: IrType) =
type.getInlinedClass().let { inlinedClass ->
FunctionParameter(mapType(type), inlinedClass?.let { mapFunction(context.getBoxFunction(it)) },
inlinedClass?.let { mapFunction(context.getUnboxFunction(it)) })
}
// TODO: use from LlvmDeclarations. // TODO: use from LlvmDeclarations.
private fun getFqName(descriptor: DeclarationDescriptor): FqName = private fun getFqName(descriptor: DeclarationDescriptor): FqName =
descriptor.parent.fqNameSafe.child(descriptor.name) descriptor.parent.fqNameSafe.child(descriptor.name)
@@ -602,7 +610,7 @@ internal object DataFlowIR {
val bridgeTargetSymbol = if (isSpecialBridge || bridgeTarget == null) null else mapFunction(bridgeTarget) val bridgeTargetSymbol = if (isSpecialBridge || bridgeTarget == null) null else mapFunction(bridgeTarget)
val placeToFunctionsTable = !isAbstract && it !is ConstructorDescriptor && classDescriptor != null val placeToFunctionsTable = !isAbstract && it !is ConstructorDescriptor && classDescriptor != null
&& !classDescriptor.isNonGeneratedAnnotation() && !classDescriptor.isNonGeneratedAnnotation()
&& (it.isOverridableOrOverrides || bridgeTarget != null || descriptor.name.asString().contains("<bridge-") || !classDescriptor.isFinal()) && (it.isOverridableOrOverrides || bridgeTarget != null || descriptor.isSpecial || !classDescriptor.isFinal())
val symbolTableIndex = if (placeToFunctionsTable) module.numberOfFunctions++ else -1 val symbolTableIndex = if (placeToFunctionsTable) module.numberOfFunctions++ else -1
if (it.isExported()) if (it.isExported())
FunctionSymbol.Public(name.localHash.value, module, symbolTableIndex, attributes, bridgeTargetSymbol, takeName { name }) FunctionSymbol.Public(name.localHash.value, module, symbolTableIndex, attributes, bridgeTargetSymbol, takeName { name })
@@ -612,18 +620,21 @@ internal object DataFlowIR {
} }
functionMap[it] = symbol functionMap[it] = symbol
symbol.parameterTypes = symbol.parameters =
(descriptor.allParameters.map { it.type } + (if (descriptor.isSuspend) listOf(continuationType) else emptyList())) (descriptor.allParameters.map { it.type } + (if (descriptor.isSuspend) listOf(continuationType) else emptyList()))
.map { mapType(it) } .map { mapTypeToFunctionParameter(it) }
.toTypedArray() .toTypedArray()
symbol.returnType = mapType(if (descriptor.isSuspend) symbol.returnParameter = mapTypeToFunctionParameter(if (descriptor.isSuspend)
context.irBuiltIns.anyType context.irBuiltIns.anyType
else else
descriptor.returnType) descriptor.returnType)
return symbol return symbol
} }
private val FunctionDescriptor.isSpecial get() =
name.asString().let { it.contains("<bridge-") || it.contains("<box>") || it.contains("<unbox>") }
private fun mapPropertyInitializer(descriptor: IrField): FunctionSymbol = descriptor.original.let { private fun mapPropertyInitializer(descriptor: IrField): FunctionSymbol = descriptor.original.let {
functionMap[it]?.let { return it } functionMap[it]?.let { return it }
@@ -633,8 +644,8 @@ internal object DataFlowIR {
functionMap[it] = symbol functionMap[it] = symbol
symbol.parameterTypes = emptyArray() symbol.parameters = emptyArray()
symbol.returnType = mapClassReferenceType(context.ir.symbols.unit.owner) symbol.returnParameter = mapTypeToFunctionParameter(context.irBuiltIns.unitType)
return symbol return symbol
} }
@@ -27,9 +27,6 @@ import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptorImpl import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptorImpl
import org.jetbrains.kotlin.ir.expressions.* 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.getValueArgument
import org.jetbrains.kotlin.ir.expressions.impl.IrBranchImpl import org.jetbrains.kotlin.ir.expressions.impl.IrBranchImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrWhenImpl import org.jetbrains.kotlin.ir.expressions.impl.IrWhenImpl
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
@@ -236,8 +233,8 @@ internal object Devirtualization {
// Rapid Type Analysis: find all instantiations and conservatively estimate call graph. // Rapid Type Analysis: find all instantiations and conservatively estimate call graph.
// Add all final parameters of the roots. // Add all final parameters of the roots.
rootSet.forEach { rootSet.forEach {
it.parameterTypes it.parameters
.map { it.resolved() } .map { it.type.resolved() }
.filter { it.isFinal } .filter { it.isFinal }
.forEach { addInstantiatingClass(it) } .forEach { addInstantiatingClass(it) }
} }
@@ -323,7 +320,7 @@ internal object Devirtualization {
DEBUG_OUTPUT(1) { println("Function $resolvedFunctionSymbol is external") } DEBUG_OUTPUT(1) { println("Function $resolvedFunctionSymbol is external") }
val resolvedReturnType = symbol.returnType.resolved() val resolvedReturnType = symbol.returnParameter.type.resolved()
if (resolvedReturnType.isFinal) { if (resolvedReturnType.isFinal) {
DEBUG_OUTPUT(1) { println("Adding return type as it is final") } DEBUG_OUTPUT(1) { println("Adding return type as it is final") }
@@ -373,7 +370,7 @@ internal object Devirtualization {
if (node.receiverType == DataFlowIR.Type.Virtual) if (node.receiverType == DataFlowIR.Type.Virtual)
continue@nodeLoop continue@nodeLoop
val receiverType = node.receiverType.resolved() val receiverType = node.receiverType.resolved()
val vCallReturnType = node.callee.returnType.resolved() val vCallReturnType = node.callee.returnParameter.type.resolved()
DEBUG_OUTPUT(1) { DEBUG_OUTPUT(1) {
println("Adding virtual callsite:") println("Adding virtual callsite:")
@@ -737,11 +734,11 @@ internal object Devirtualization {
if (symbol is DataFlowIR.FunctionSymbol.External) return null if (symbol is DataFlowIR.FunctionSymbol.External) return null
constraintGraph.functions[symbol]?.let { return it } constraintGraph.functions[symbol]?.let { return it }
val parameters = Array(symbol.parameterTypes.size) { ordinaryNode { "Param#$it\$$symbol" } } val parameters = Array(symbol.parameters.size) { ordinaryNode { "Param#$it\$$symbol" } }
if (isRoot) { if (isRoot) {
// Exported function from the current module. // Exported function from the current module.
symbol.parameterTypes.forEachIndexed { index, type -> symbol.parameters.forEachIndexed { index, type ->
val resolvedType = type.resolved() val resolvedType = type.type.resolved()
val node = if (!resolvedType.isFinal) val node = if (!resolvedType.isFinal)
constraintGraph.virtualNode constraintGraph.virtualNode
else else
@@ -858,7 +855,7 @@ internal object Devirtualization {
function.parameters[node.index] function.parameters[node.index]
is DataFlowIR.Node.StaticCall -> is DataFlowIR.Node.StaticCall ->
doCall(node.callee, node.arguments, node.callee.returnType.resolved(), node.receiverType?.resolved()) doCall(node.callee, node.arguments, node.callee.returnParameter.type.resolved(), node.receiverType?.resolved())
is DataFlowIR.Node.NewObject -> { is DataFlowIR.Node.NewObject -> {
val returnType = node.constructedType.resolved() val returnType = node.constructedType.resolved()
@@ -901,7 +898,7 @@ internal object Devirtualization {
println() println()
} }
val returnType = node.callee.returnType.resolved() val returnType = node.callee.returnParameter.type.resolved()
val receiverNode = edgeToConstraintNode(node.arguments[0]) val receiverNode = edgeToConstraintNode(node.arguments[0])
if (receiverType == DataFlowIR.Type.Virtual) if (receiverType == DataFlowIR.Type.Virtual)
constraintGraph.virtualNode.addEdge(receiverNode) constraintGraph.virtualNode.addEdge(receiverNode)
@@ -1001,13 +998,8 @@ internal object Devirtualization {
private fun devirtualize(irModule: IrModuleFragment, context: Context, private fun devirtualize(irModule: IrModuleFragment, context: Context,
moduleDFG: ModuleDFG, externalModulesDFG: ExternalModulesDFG, moduleDFG: ModuleDFG, externalModulesDFG: ExternalModulesDFG,
devirtualizedCallSites: Map<IrCall, DevirtualizedCallSite>) { devirtualizedCallSites: Map<IrCall, DevirtualizedCallSite>) {
val nativePtrType = context.ir.symbols.nativePtrType
val nativePtrEqualityOperatorSymbol = context.ir.symbols.areEqualByValue[PrimitiveBinaryType.POINTER]!! val nativePtrEqualityOperatorSymbol = context.ir.symbols.areEqualByValue[PrimitiveBinaryType.POINTER]!!
val optimize = context.shouldOptimize() 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 { fun DataFlowIR.Type.resolved(): DataFlowIR.Type.Declared {
if (this is DataFlowIR.Type.Declared) return this if (this is DataFlowIR.Type.Declared) return this
@@ -1021,11 +1013,8 @@ internal object Devirtualization {
return this return this
} }
/* // TODO: do it more reliably.
fun IrExpression.isBoxOrUnboxCall() = this is IrCall && (boxFunctions[symbol] != null || unboxFunctions[symbol] != null) fun IrExpression.isBoxOrUnboxCall() = this is IrCall && symbol.owner.name.asString().let { it.contains("<box>") || it.contains("<unbox>") }
*/
fun IrExpression.isBoxOrUnboxCall() = false
fun IrBuilderWithScope.irCoerce(value: IrExpression, coercion: IrFunctionSymbol?) = fun IrBuilderWithScope.irCoerce(value: IrExpression, coercion: IrFunctionSymbol?) =
if (coercion == null) if (coercion == null)
@@ -1034,6 +1023,23 @@ internal object Devirtualization {
addArguments(listOf(coercion.descriptor.explicitParameters.single() to value)) addArguments(listOf(coercion.descriptor.explicitParameters.single() to value))
} }
fun IrBuilderWithScope.irCoerce(value: IrExpression, coercion: DataFlowIR.FunctionSymbol.Declared?) =
if (coercion == null)
value
else IrPrivateFunctionCallImpl(
startOffset = startOffset,
endOffset = endOffset,
type = value.type, // TODO: What type is actually must be here?
valueArgumentsCount = 1,
virtualCallee = null,
dfgSymbol = coercion,
moduleDescriptor = coercion.module.descriptor,
totalFunctions = coercion.module.numberOfFunctions,
functionIndex = coercion.symbolTableIndex
).apply {
putValueArgument(0, value)
}
class PossiblyCoercedValue(val value: IrVariable, val coercion: IrFunctionSymbol?) { class PossiblyCoercedValue(val value: IrVariable, val coercion: IrFunctionSymbol?) {
fun getFullValue(irBuilder: IrBuilderWithScope) = irBuilder.run { fun getFullValue(irBuilder: IrBuilderWithScope) = irBuilder.run {
irCoerce(irGet(value), coercion) irCoerce(irGet(value), coercion)
@@ -1065,89 +1071,74 @@ internal object Devirtualization {
, coercion.symbol) , coercion.symbol)
} }
/* class CoercionPair(val coerceFunction: DataFlowIR.FunctionSymbol.Declared, val uncoerceFunction: DataFlowIR.FunctionSymbol.Declared)
fun assertCoercionsMatch(coercion1: IrFunctionSymbol, coercion2: IrFunctionSymbol) {
boxFunctions[coercion1]?.let { assert (unboxFunctions[coercion2] == it) fun getTypeConversion(actualType: DataFlowIR.FunctionParameter, targetType: DataFlowIR.FunctionParameter): CoercionPair? {
{ "Incosistent coercions: ${coercion1.descriptor}, ${coercion2.descriptor}" } if (actualType.boxFunction == null && targetType.boxFunction == null) return null
} if (actualType.boxFunction != null && targetType.boxFunction != null) {
unboxFunctions[coercion1]?.let { assert (boxFunctions[coercion2] == it) assert (actualType.type.resolved() == targetType.type.resolved())
{ "Incosistent coercions: ${coercion1.descriptor}, ${coercion2.descriptor}" } { "Inconsistent types: ${actualType.type} and ${targetType.type}" }
return null
} }
if (actualType.boxFunction == null)
return CoercionPair(targetType.unboxFunction!!.resolved() as DataFlowIR.FunctionSymbol.Declared,
targetType.boxFunction!!.resolved() as DataFlowIR.FunctionSymbol.Declared)
return CoercionPair(actualType.boxFunction!!.resolved() as DataFlowIR.FunctionSymbol.Declared,
actualType.unboxFunction!!.resolved() as DataFlowIR.FunctionSymbol.Declared)
} }
fun IrBuilderWithScope.irCoerceIfNeeded(type: DataFlowIR.Type.Declared, targetType: DataFlowIR.Type.Declared, fun IrBuilderWithScope.irCoerceIfNeeded(type: DataFlowIR.FunctionParameter, targetType: DataFlowIR.FunctionParameter,
possiblyCoercedValue: PossiblyCoercedValue): IrExpression { possiblyCoercedValue: PossiblyCoercedValue): IrExpression {
val value = possiblyCoercedValue.value val value = possiblyCoercedValue.value
val prevCoercion = possiblyCoercedValue.coercion val prevCoercion = possiblyCoercedValue.coercion
val coercion = context.ir.symbols.getTypeConversion(type.correspondingValueType, targetType.correspondingValueType) val coercion = getTypeConversion(type, targetType)
?: return possiblyCoercedValue.getFullValue(this) ?: return possiblyCoercedValue.getFullValue(this)
if (prevCoercion == null) if (prevCoercion == null)
return irCoerce(irGet(value), coercion) return irCoerce(irGet(value), coercion.coerceFunction)
assertCoercionsMatch(coercion, prevCoercion) val expectedUncoercion = coercion.uncoerceFunction
val actualUncoercion = moduleDFG.symbolTable.mapFunction(prevCoercion.owner).resolved()
assert(actualUncoercion == expectedUncoercion) { "Incosistent coercions: ${expectedUncoercion}, ${actualUncoercion}" }
return irGet(value) return irGet(value)
} }
*/
fun IrBuilderWithScope.irDevirtualizedCall(callee: IrCall, actualType: IrType, fun irDevirtualizedCall(callee: IrCall, actualType: IrType, devirtualizedCallee: DataFlowIR.FunctionSymbol.Declared) =
devirtualizedCallee: DataFlowIR.FunctionSymbol.Declared) =
IrPrivateFunctionCallImpl( IrPrivateFunctionCallImpl(
startOffset = startOffset, startOffset = callee.startOffset,
endOffset = endOffset, endOffset = callee.endOffset,
type = actualType, type = actualType,
symbol = callee.symbol, valueArgumentsCount = devirtualizedCallee.parameters.size,
descriptor = callee.descriptor, virtualCallee = callee,
typeArgumentsCount = callee.typeArgumentsCount, dfgSymbol = devirtualizedCallee,
dfgSymbol = devirtualizedCallee, moduleDescriptor = devirtualizedCallee.module.descriptor,
totalFunctions = devirtualizedCallee.module.numberOfFunctions, totalFunctions = devirtualizedCallee.module.numberOfFunctions,
moduleDescriptor = devirtualizedCallee.module.descriptor, functionIndex = devirtualizedCallee.symbolTableIndex
functionIndex = devirtualizedCallee.symbolTableIndex,
virtualCallee = callee
) )
fun IrBuilderWithScope.irDevirtualizedCall(callee: IrCall, actualType: IrType, fun IrBuilderWithScope.irDevirtualizedCall(callee: IrCall, actualType: IrType,
actualCallee: DataFlowIR.FunctionSymbol.Declared, actualCallee: DataFlowIR.FunctionSymbol.Declared,
receiver: IrVariable, parameters: List<PossiblyCoercedValue>) =
extensionReceiver: PossiblyCoercedValue?,
parameters: Map<ValueParameterDescriptor, PossiblyCoercedValue>) =
actualCallee.bridgeTarget.let { actualCallee.bridgeTarget.let {
// if (it == null) if (it == null)
irDevirtualizedCall(callee, actualType, actualCallee).apply { irDevirtualizedCall(callee, actualType, actualCallee).apply {
this.dispatchReceiver = irGet(receiver) parameters.forEachIndexed { index, value ->
this.extensionReceiver = extensionReceiver?.getFullValue(this@irDevirtualizedCall) putValueArgument(index, value.getFullValue(this@irDevirtualizedCall))
callee.descriptor.valueParameters.forEach {
putValueArgument(it.index, parameters[it]!!.getFullValue(this@irDevirtualizedCall))
} }
} }
/*
else { else {
val bridgeTarget = it.resolved() as DataFlowIR.FunctionSymbol.Declared val bridgeTarget = it.resolved() as DataFlowIR.FunctionSymbol.Declared
val callResult = irDevirtualizedCall(callee, actualType, bridgeTarget).apply { val callResult = irDevirtualizedCall(callee, actualType, bridgeTarget).apply {
this.dispatchReceiver = irGet(receiver) parameters.forEachIndexed { index, value ->
this.extensionReceiver = extensionReceiver?.let { putValueArgument(index, irCoerceIfNeeded(
irCoerceIfNeeded( type = actualCallee.parameters[index],
type = actualCallee.parameterTypes[1].resolved(), targetType = bridgeTarget.parameters[index],
targetType = bridgeTarget.parameterTypes[1].resolved(), possiblyCoercedValue = value
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( val returnCoercion = getTypeConversion(bridgeTarget.returnParameter, actualCallee.returnParameter)
bridgeTarget.returnType.resolved().correspondingValueType, irCoerce(callResult, returnCoercion?.coerceFunction)
actualCallee.returnType.resolved().correspondingValueType)
irCoerce(callResult, returnCoercion)
} }
*/
} }
irModule.transformChildrenVoid(object: IrElementTransformerVoidWithContext() { irModule.transformChildrenVoid(object: IrElementTransformerVoidWithContext() {
@@ -1160,12 +1151,10 @@ internal object Devirtualization {
arg.argument arg.argument
else arg else arg
if (!uncastedArg.isBoxOrUnboxCall()) return expression if (!uncastedArg.isBoxOrUnboxCall()) return expression
/*
val argarg = (uncastedArg as IrCall).getArguments().single().second val argarg = (uncastedArg as IrCall).getArguments().single().second
if (boxFunctions[expression.symbol].let { it != null && it == unboxFunctions[uncastedArg.symbol] } if (expression.symbol.owner.returnType == uncastedArg.symbol.owner.explicitParameters.single().type
|| unboxFunctions[expression.symbol].let { it != null && it == boxFunctions[uncastedArg.symbol] }) && expression.symbol.owner.explicitParameters.single().type == uncastedArg.symbol.owner.returnType)
return argarg return argarg
*/
return expression return expression
} }
@@ -1209,27 +1198,19 @@ internal object Devirtualization {
optimize && possibleCallees.size == 1 -> { // Monomorphic callsite. optimize && possibleCallees.size == 1 -> { // Monomorphic callsite.
val actualCallee = possibleCallees[0].callee as DataFlowIR.FunctionSymbol.Declared val actualCallee = possibleCallees[0].callee as DataFlowIR.FunctionSymbol.Declared
irBlock(expression) { irBlock(expression) {
val receiver = irTemporary(dispatchReceiver, "receiver") val parameters = expression.getArgumentsWithSymbols().mapIndexed { index, arg ->
val extensionReceiver = expression.extensionReceiver?.let { irSplitCoercion(arg.second, "arg$index", arg.first.owner.type)
irSplitCoercion(it, "extensionReceiver", function.extensionReceiverParameter!!.type)
} }
val parameters = expression.descriptor.valueParameters.associate { +irDevirtualizedCall(expression, type, actualCallee, parameters)
it to irSplitCoercion(expression.getValueArgument(it)!!, "arg${it.index}", function.valueParameters[it.index].type)
}
+irDevirtualizedCall(expression, type, actualCallee, receiver, extensionReceiver, parameters)
} }
} }
else -> irBlock(expression) { else -> irBlock(expression) {
val receiver = irTemporary(dispatchReceiver, "receiver") val parameters = expression.getArgumentsWithSymbols().mapIndexed { index, arg ->
val extensionReceiver = expression.extensionReceiver?.let { irSplitCoercion(arg.second, "arg$index", arg.first.owner.type)
irSplitCoercion(it, "extensionReceiver", function.extensionReceiverParameter!!.type)
}
val parameters = expression.descriptor.valueParameters.associate {
it to irSplitCoercion(expression.getValueArgument(it)!!, "arg${it.index}", function.valueParameters[it.index].type)
} }
val typeInfo = irTemporary(irCall(context.ir.symbols.getObjectTypeInfo).apply { val typeInfo = irTemporary(irCall(context.ir.symbols.getObjectTypeInfo).apply {
putValueArgument(0, irGet(receiver)) putValueArgument(0, parameters[0].getFullValue(this@irBlock))
}) })
val branches = mutableListOf<IrBranchImpl>() val branches = mutableListOf<IrBranchImpl>()
@@ -1241,7 +1222,7 @@ internal object Devirtualization {
endOffset = endOffset, endOffset = endOffset,
type = context.ir.symbols.nativePtrType, type = context.ir.symbols.nativePtrType,
symbol = dispatchReceiver.type.getErasedTypeClass(), symbol = dispatchReceiver.type.getErasedTypeClass(),
classType = receiver.type, classType = dispatchReceiver.type,
moduleDescriptor = actualReceiverType.module!!.descriptor, moduleDescriptor = actualReceiverType.module!!.descriptor,
totalClasses = actualReceiverType.module.numberOfClasses, totalClasses = actualReceiverType.module.numberOfClasses,
classIndex = actualReceiverType.symbolTableIndex, classIndex = actualReceiverType.symbolTableIndex,
@@ -1258,7 +1239,7 @@ internal object Devirtualization {
startOffset = startOffset, startOffset = startOffset,
endOffset = endOffset, endOffset = endOffset,
condition = condition, condition = condition,
result = irDevirtualizedCall(expression, type, actualCallee, receiver, extensionReceiver, parameters) result = irDevirtualizedCall(expression, type, actualCallee, parameters)
) )
} }
if (!optimize) { // Add else branch throwing exception for debug purposes. if (!optimize) { // Add else branch throwing exception for debug purposes.
@@ -10,7 +10,6 @@ import org.jetbrains.kotlin.backend.konan.DirectedGraphMultiNode
import org.jetbrains.kotlin.backend.konan.llvm.Lifetime import org.jetbrains.kotlin.backend.konan.llvm.Lifetime
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.ir.util.defaultType
internal object EscapeAnalysis { internal object EscapeAnalysis {
@@ -283,7 +282,7 @@ internal object EscapeAnalysis {
} }
for (functionSymbol in callGraph.directEdges.keys) { for (functionSymbol in callGraph.directEdges.keys) {
val numberOfParameters = functionSymbol.parameterTypes.size val numberOfParameters = functionSymbol.parameters.size
escapeAnalysisResults[functionSymbol] = FunctionEscapeAnalysisResult( escapeAnalysisResults[functionSymbol] = FunctionEscapeAnalysisResult(
// Assume no edges at the beginning. // Assume no edges at the beginning.
// Then iteratively add needed. // Then iteratively add needed.
@@ -396,7 +395,7 @@ internal object EscapeAnalysis {
} }
private fun getConservativeFunctionEAResult(symbol: DataFlowIR.FunctionSymbol): FunctionEscapeAnalysisResult { private fun getConservativeFunctionEAResult(symbol: DataFlowIR.FunctionSymbol): FunctionEscapeAnalysisResult {
val numberOfParameters = symbol.parameterTypes.size val numberOfParameters = symbol.parameters.size
return FunctionEscapeAnalysisResult((0..numberOfParameters).map { return FunctionEscapeAnalysisResult((0..numberOfParameters).map {
ParameterEscapeAnalysisResult( ParameterEscapeAnalysisResult(
escapes = true, escapes = true,
@@ -425,7 +424,7 @@ internal object EscapeAnalysis {
FunctionEscapeAnalysisResult.fromBits( FunctionEscapeAnalysisResult.fromBits(
callee.escapes ?: 0, callee.escapes ?: 0,
(0..callee.parameterTypes.size).map { callee.pointsTo?.elementAtOrNull(it) ?: 0 } (0..callee.parameters.size).map { callee.pointsTo?.elementAtOrNull(it) ?: 0 }
) )
} }