Call graph building using devirtualization analysis results

This commit is contained in:
Igor Chevdar
2018-04-11 15:16:02 +05:00
parent afe9f04487
commit c633e879a7
3 changed files with 82 additions and 42 deletions
@@ -87,7 +87,7 @@ internal fun emitLLVM(context: Context) {
}
@Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE")
var devirtualizationAnalysisResult: Map<DataFlowIR.Node.VirtualCall, Devirtualization.DevirtualizedCallSite>? = 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)
}
@@ -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<DataFlowIR.FunctionSymbol> {
@@ -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<CallSite>()
}
@@ -59,7 +57,9 @@ internal class CallGraph(val directEdges: Map<DataFlowIR.FunctionSymbol, CallGra
internal class CallGraphBuilder(val context: Context,
val moduleDFG: ModuleDFG,
val externalModulesDFG: ExternalModulesDFG) {
val externalModulesDFG: ExternalModulesDFG,
devirtualizationAnalysisResult: Devirtualization.AnalysisResult?,
val gotoExternal: Boolean) {
private val DEBUG = 0
@@ -67,15 +67,7 @@ internal class CallGraphBuilder(val context: Context,
if (DEBUG > 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<DataFlowIR.FunctionSymbol>()
private val directEdges = mutableMapOf<DataFlowIR.FunctionSymbol, CallGraphNode>()
private val reversedEdges = mutableMapOf<DataFlowIR.FunctionSymbol, MutableList<DataFlowIR.FunctionSymbol>>()
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<DataFlowIR.FunctionSymbol.Public>()
}
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<DataFlowIR.FunctionSymbol>()
reversedEdges.put(symbol, list)
val function = moduleDFG.functions[symbol] ?: externalModulesDFG.functionDFGs[symbol]
val body = function!!.body
body.nodes.filterIsInstance<DataFlowIR.Node.Call>()
.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<DataFlowIR.Node.Call>()
.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<DataFlowIR.Node.Call>()
.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)
}
}
}
}
}
}
@@ -888,8 +888,10 @@ internal object Devirtualization {
class DevirtualizedCallSite(val possibleCallees: List<DevirtualizedCallee>)
class AnalysisResult(val devirtualizedCallSites: Map<DataFlowIR.Node.VirtualCall, DevirtualizedCallSite>)
fun run(irModule: IrModuleFragment, context: Context, moduleDFG: ModuleDFG, externalModulesDFG: ExternalModulesDFG)
: Map<DataFlowIR.Node.VirtualCall, DevirtualizedCallSite> {
: 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,