diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/CallGraphBuilder.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/CallGraphBuilder.kt index b7fd2645bcd..56527365a7a 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/CallGraphBuilder.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/CallGraphBuilder.kt @@ -131,15 +131,6 @@ internal class CallGraphBuilder( node ) - is DataFlowIR.Node.FunctionReference -> - block(DataFlowIR.Node.Call( - callee = node.symbol, - arguments = emptyList(), - returnType = node.symbol.returnParameter.type, - irCallSite = null), - node - ) - else -> { } } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DevirtualizationAnalysis.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DevirtualizationAnalysis.kt index 96bef0db86c..569ed92f307 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DevirtualizationAnalysis.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DevirtualizationAnalysis.kt @@ -65,50 +65,60 @@ internal object DevirtualizationAnalysis { } val entryPoint = context.ir.symbols.entryPoint?.owner - val exportedFunctions = - if (entryPoint != null) - listOf(moduleDFG.symbolTable.mapFunction(entryPoint).resolved()) - else { - // In a library every public function and every function accessible via virtual call belongs to the rootset. - moduleDFG.symbolTable.functionMap.values.filter { - it is DataFlowIR.FunctionSymbol.Public - || (it as? DataFlowIR.FunctionSymbol.External)?.isExported == true - } + - moduleDFG.symbolTable.classMap.values - .filterIsInstance() - .flatMap { it.vtable + it.itable.values.flatten() } - .filterIsInstance() - .filter { moduleDFG.functions.containsKey(it) } - } + val exported = if (entryPoint != null) + listOf(moduleDFG.symbolTable.mapFunction(entryPoint).resolved()) + else { + // In a library every public function and every function accessible via virtual call belongs to the rootset. + moduleDFG.symbolTable.functionMap.values.filter { + it is DataFlowIR.FunctionSymbol.Public + || (it as? DataFlowIR.FunctionSymbol.External)?.isExported == true + } + + moduleDFG.symbolTable.classMap.values + .filterIsInstance() + .flatMap { it.vtable + it.itable.values.flatten() } + .filterIsInstance() + .filter { moduleDFG.functions.containsKey(it) } + } + // TODO: Are globals initializers always called whether they are actually reachable from roots or not? // TODO: With the changed semantics of global initializers this is no longer the case - rework. val globalInitializers = moduleDFG.symbolTable.functionMap.values.filter { it.isTopLevelFieldInitializer || it.isGlobalInitializer } + - externalModulesDFG.functionDFGs.keys.filter { it.isTopLevelFieldInitializer || it.isGlobalInitializer } + externalModulesDFG.functionDFGs.keys.filter { it.isTopLevelFieldInitializer || it.isGlobalInitializer } - val explicitlyExportedFunctions = + val explicitlyExported = moduleDFG.symbolTable.functionMap.values.filter { it.explicitlyExported } + - externalModulesDFG.functionDFGs.keys.filter { it.explicitlyExported } + externalModulesDFG.functionDFGs.keys.filter { it.explicitlyExported } // Conservatively assume each associated object could be called. // Note: for constructors there is additional parameter () and its type will be added // to instantiating classes since all objects are final types. - val associatedObjects = mutableListOf() - context.irModule!!.acceptChildrenVoid(object: IrElementVisitorVoid { + val associatedObjectConstructors = mutableListOf() + // At this point all function references are lowered except those leaking to the native world. + // Conservatively assume them belonging of the root set. + val leakingThroughFunctionReferences = mutableListOf() + context.irModule!!.acceptChildrenVoid(object : IrElementVisitorVoid { override fun visitElement(element: IrElement) { element.acceptChildrenVoid(this) } override fun visitClass(declaration: IrClass) { + declaration.acceptChildrenVoid(this) + context.getLayoutBuilder(declaration).associatedObjects.values.forEach { - assert (it.kind == ClassKind.OBJECT) { "An object expected but was ${it.dump()}" } - associatedObjects += moduleDFG.symbolTable.mapFunction(it.constructors.single()) + assert(it.kind == ClassKind.OBJECT) { "An object expected but was ${it.dump()}" } + associatedObjectConstructors += moduleDFG.symbolTable.mapFunction(it.constructors.single()) } - super.visitClass(declaration) + } + + override fun visitFunctionReference(expression: IrFunctionReference) { + expression.acceptChildrenVoid(this) + + leakingThroughFunctionReferences.add(moduleDFG.symbolTable.mapFunction(expression.symbol.owner)) } }) - return (exportedFunctions + globalInitializers + explicitlyExportedFunctions + associatedObjects).distinct() + return (exported + globalInitializers + explicitlyExported + associatedObjectConstructors + leakingThroughFunctionReferences).distinct() } fun BitSet.format(allTypes: Array): String {