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 926fd410586..e431471c3d5 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 @@ -87,7 +87,7 @@ internal fun emitLLVM(context: Context) { } @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - var devirtualizationAnalysisResult: Map? = null + var devirtualizationAnalysisResult: Devirtualization.AnalysisResult? = null phaser.phase(KonanPhase.DEVIRTUALIZATION) { devirtualizationAnalysisResult = Devirtualization.run(irModule, context, moduleDFG!!, externalModulesDFG!!) @@ -107,7 +107,7 @@ internal fun emitLLVM(context: Context) { } phaser.phase(KonanPhase.ESCAPE_ANALYSIS) { - val callGraph = CallGraphBuilder(context, moduleDFG!!, externalModulesDFG!!).build() + val callGraph = CallGraphBuilder(context, moduleDFG!!, externalModulesDFG!!, devirtualizationAnalysisResult, false).build() EscapeAnalysis.computeLifetimes(moduleDFG!!, externalModulesDFG!!, callGraph, lifetimes) } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/CallGraphBuilder.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/CallGraphBuilder.kt index a7d43c5dc5d..e966faf51e0 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/CallGraphBuilder.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/CallGraphBuilder.kt @@ -19,8 +19,6 @@ package org.jetbrains.kotlin.backend.konan.optimizations import org.jetbrains.kotlin.backend.konan.DirectedGraph import org.jetbrains.kotlin.backend.konan.DirectedGraphNode import org.jetbrains.kotlin.backend.konan.Context -import org.jetbrains.kotlin.backend.konan.KonanConfigKeys -import org.jetbrains.kotlin.konan.target.CompilerOutputKind internal class CallGraphNode(val graph: CallGraph, val symbol: DataFlowIR.FunctionSymbol) : DirectedGraphNode { @@ -37,7 +35,7 @@ internal class CallGraphNode(val graph: CallGraph, val symbol: DataFlowIR.Functi graph.reversedEdges[symbol]!! } - class CallSite(val call: DataFlowIR.Node.Call, val actualCallee: DataFlowIR.FunctionSymbol) + class CallSite(val call: DataFlowIR.Node.Call, val isVirtual: Boolean, val actualCallee: DataFlowIR.FunctionSymbol) val callSites = mutableListOf() } @@ -59,7 +57,9 @@ internal class CallGraph(val directEdges: Map severity) block() } - private val hasMain = context.config.configuration.get(KonanConfigKeys.PRODUCE) == CompilerOutputKind.PROGRAM - - private val symbolTable = moduleDFG.symbolTable - - private 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") - } + private val devirtualizedCallSites = devirtualizationAnalysisResult?.devirtualizedCallSites private fun DataFlowIR.FunctionSymbol.resolved(): DataFlowIR.FunctionSymbol { if (this is DataFlowIR.FunctionSymbol.External) @@ -83,46 +75,92 @@ internal class CallGraphBuilder(val context: Context, return this } - private fun DataFlowIR.Type.Declared.isSubtypeOf(other: DataFlowIR.Type.Declared): Boolean { - return this == other || this.superTypes.any { it.resolved().isSubtypeOf(other) } - } - + private val visitedFunctions = mutableSetOf() private val directEdges = mutableMapOf() private val reversedEdges = mutableMapOf>() private val callGraph = CallGraph(directEdges, reversedEdges) fun build(): CallGraph { - val rootSet = if (hasMain) { - listOf(symbolTable.mapFunction(context.ir.symbols.entryPoint!!.owner).resolved()) + - moduleDFG.functions - .map { it.key } - .filter { it.isGlobalInitializer } - } else { - moduleDFG.functions.keys.filterIsInstance() - } + val rootSet = Devirtualization.computeRootSet(context, moduleDFG, externalModulesDFG) @Suppress("LoopToCallChain") for (symbol in rootSet) { - if (!directEdges.containsKey(symbol)) + if (!visitedFunctions.contains(symbol)) dfs(symbol) } return callGraph } - private fun dfs(symbol: DataFlowIR.FunctionSymbol) { + private fun addNode(symbol: DataFlowIR.FunctionSymbol) { + if (directEdges.containsKey(symbol)) + return val node = CallGraphNode(callGraph, symbol) directEdges.put(symbol, node) val list = mutableListOf() reversedEdges.put(symbol, list) - val function = moduleDFG.functions[symbol] ?: externalModulesDFG.functionDFGs[symbol] - val body = function!!.body - body.nodes.filterIsInstance() - .forEach { - val callee = it.callee.resolved() - callGraph.addEdge(symbol, CallGraphNode.CallSite(it, callee)) - if (callee is DataFlowIR.FunctionSymbol.Declared - && it !is DataFlowIR.Node.VirtualCall - && !directEdges.containsKey(callee)) - dfs(callee) - } + } + + private fun dfs(symbol: DataFlowIR.FunctionSymbol) { + visitedFunctions += symbol + if (gotoExternal) { + addNode(symbol) + val function = moduleDFG.functions[symbol] ?: externalModulesDFG.functionDFGs[symbol] + val body = function!!.body + body.nodes.filterIsInstance() + .forEach { call -> + val devirtualizedCallSite = (call as? DataFlowIR.Node.VirtualCall)?.let { devirtualizedCallSites?.get(it) } + if (devirtualizedCallSite == null) { + val callee = call.callee.resolved() + callGraph.addEdge(symbol, CallGraphNode.CallSite(call, call is DataFlowIR.Node.VirtualCall, callee)) + if (callee is DataFlowIR.FunctionSymbol.Declared + && call !is DataFlowIR.Node.VirtualCall + && !directEdges.containsKey(callee)) + dfs(callee) + } else { + devirtualizedCallSite.possibleCallees.forEach { + val callee = it.callee.resolved() + callGraph.addEdge(symbol, CallGraphNode.CallSite(call, false, callee)) + if (callee is DataFlowIR.FunctionSymbol.Declared + && !directEdges.containsKey(callee)) + dfs(callee) + } + } + } + } else { + var function = moduleDFG.functions[symbol] + var local = true + if (function != null) + addNode(symbol) + else { + function = externalModulesDFG.functionDFGs[symbol]!! + local = false + } + val body = function.body + body.nodes.filterIsInstance() + .forEach { call -> + val devirtualizedCallSite = (call as? DataFlowIR.Node.VirtualCall)?.let { devirtualizedCallSites?.get(it) } + if (devirtualizedCallSite == null) { + val callee = call.callee.resolved() + if (moduleDFG.functions.containsKey(callee)) + addNode(callee) + if (local) + callGraph.addEdge(symbol, CallGraphNode.CallSite(call, call is DataFlowIR.Node.VirtualCall, callee)) + if (callee is DataFlowIR.FunctionSymbol.Declared + && call !is DataFlowIR.Node.VirtualCall + && !visitedFunctions.contains(callee)) + dfs(callee) + } else { + devirtualizedCallSite.possibleCallees.forEach { + val callee = it.callee.resolved() + if (moduleDFG.functions.containsKey(callee)) + addNode(callee) + if (local) + callGraph.addEdge(symbol, CallGraphNode.CallSite(call, false, callee)) + if (callee is DataFlowIR.FunctionSymbol.Declared + && !visitedFunctions.contains(callee)) + dfs(callee) + } + } + } + } } } \ No newline at end of file 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 ff91341cf62..97c1fa34436 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 @@ -888,8 +888,10 @@ internal object Devirtualization { class DevirtualizedCallSite(val possibleCallees: List) + class AnalysisResult(val devirtualizedCallSites: Map) + fun run(irModule: IrModuleFragment, context: Context, moduleDFG: ModuleDFG, externalModulesDFG: ExternalModulesDFG) - : Map { + : AnalysisResult { val devirtualizationAnalysisResult = DevirtualizationAnalysis(context, moduleDFG, externalModulesDFG).analyze() val devirtualizedCallSites = devirtualizationAnalysisResult @@ -897,7 +899,7 @@ internal object Devirtualization { .filter { it.key.callSite != null } .associate { it.key.callSite!! to it.value } Devirtualization.devirtualize(irModule, context, devirtualizedCallSites) - return devirtualizationAnalysisResult + return AnalysisResult(devirtualizationAnalysisResult) } private fun devirtualize(irModule: IrModuleFragment, context: Context,