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") @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE")
var devirtualizationAnalysisResult: Map<DataFlowIR.Node.VirtualCall, Devirtualization.DevirtualizedCallSite>? = null var devirtualizationAnalysisResult: Devirtualization.AnalysisResult? = null
phaser.phase(KonanPhase.DEVIRTUALIZATION) { phaser.phase(KonanPhase.DEVIRTUALIZATION) {
devirtualizationAnalysisResult = Devirtualization.run(irModule, context, moduleDFG!!, externalModulesDFG!!) devirtualizationAnalysisResult = Devirtualization.run(irModule, context, moduleDFG!!, externalModulesDFG!!)
@@ -107,7 +107,7 @@ internal fun emitLLVM(context: Context) {
} }
phaser.phase(KonanPhase.ESCAPE_ANALYSIS) { 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) 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.DirectedGraph
import org.jetbrains.kotlin.backend.konan.DirectedGraphNode import org.jetbrains.kotlin.backend.konan.DirectedGraphNode
import org.jetbrains.kotlin.backend.konan.Context 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) internal class CallGraphNode(val graph: CallGraph, val symbol: DataFlowIR.FunctionSymbol)
: DirectedGraphNode<DataFlowIR.FunctionSymbol> { : DirectedGraphNode<DataFlowIR.FunctionSymbol> {
@@ -37,7 +35,7 @@ internal class CallGraphNode(val graph: CallGraph, val symbol: DataFlowIR.Functi
graph.reversedEdges[symbol]!! 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>() val callSites = mutableListOf<CallSite>()
} }
@@ -59,7 +57,9 @@ internal class CallGraph(val directEdges: Map<DataFlowIR.FunctionSymbol, CallGra
internal class CallGraphBuilder(val context: Context, internal class CallGraphBuilder(val context: Context,
val moduleDFG: ModuleDFG, val moduleDFG: ModuleDFG,
val externalModulesDFG: ExternalModulesDFG) { val externalModulesDFG: ExternalModulesDFG,
devirtualizationAnalysisResult: Devirtualization.AnalysisResult?,
val gotoExternal: Boolean) {
private val DEBUG = 0 private val DEBUG = 0
@@ -67,15 +67,7 @@ internal class CallGraphBuilder(val context: Context,
if (DEBUG > severity) block() if (DEBUG > severity) block()
} }
private val hasMain = context.config.configuration.get(KonanConfigKeys.PRODUCE) == CompilerOutputKind.PROGRAM private val devirtualizedCallSites = devirtualizationAnalysisResult?.devirtualizedCallSites
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 fun DataFlowIR.FunctionSymbol.resolved(): DataFlowIR.FunctionSymbol { private fun DataFlowIR.FunctionSymbol.resolved(): DataFlowIR.FunctionSymbol {
if (this is DataFlowIR.FunctionSymbol.External) if (this is DataFlowIR.FunctionSymbol.External)
@@ -83,46 +75,92 @@ internal class CallGraphBuilder(val context: Context,
return this return this
} }
private fun DataFlowIR.Type.Declared.isSubtypeOf(other: DataFlowIR.Type.Declared): Boolean { private val visitedFunctions = mutableSetOf<DataFlowIR.FunctionSymbol>()
return this == other || this.superTypes.any { it.resolved().isSubtypeOf(other) }
}
private val directEdges = mutableMapOf<DataFlowIR.FunctionSymbol, CallGraphNode>() private val directEdges = mutableMapOf<DataFlowIR.FunctionSymbol, CallGraphNode>()
private val reversedEdges = mutableMapOf<DataFlowIR.FunctionSymbol, MutableList<DataFlowIR.FunctionSymbol>>() private val reversedEdges = mutableMapOf<DataFlowIR.FunctionSymbol, MutableList<DataFlowIR.FunctionSymbol>>()
private val callGraph = CallGraph(directEdges, reversedEdges) private val callGraph = CallGraph(directEdges, reversedEdges)
fun build(): CallGraph { fun build(): CallGraph {
val rootSet = if (hasMain) { val rootSet = Devirtualization.computeRootSet(context, moduleDFG, externalModulesDFG)
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>()
}
@Suppress("LoopToCallChain") @Suppress("LoopToCallChain")
for (symbol in rootSet) { for (symbol in rootSet) {
if (!directEdges.containsKey(symbol)) if (!visitedFunctions.contains(symbol))
dfs(symbol) dfs(symbol)
} }
return callGraph return callGraph
} }
private fun dfs(symbol: DataFlowIR.FunctionSymbol) { private fun addNode(symbol: DataFlowIR.FunctionSymbol) {
if (directEdges.containsKey(symbol))
return
val node = CallGraphNode(callGraph, symbol) val node = CallGraphNode(callGraph, symbol)
directEdges.put(symbol, node) directEdges.put(symbol, node)
val list = mutableListOf<DataFlowIR.FunctionSymbol>() val list = mutableListOf<DataFlowIR.FunctionSymbol>()
reversedEdges.put(symbol, list) reversedEdges.put(symbol, list)
val function = moduleDFG.functions[symbol] ?: externalModulesDFG.functionDFGs[symbol] }
val body = function!!.body
body.nodes.filterIsInstance<DataFlowIR.Node.Call>() private fun dfs(symbol: DataFlowIR.FunctionSymbol) {
.forEach { visitedFunctions += symbol
val callee = it.callee.resolved() if (gotoExternal) {
callGraph.addEdge(symbol, CallGraphNode.CallSite(it, callee)) addNode(symbol)
if (callee is DataFlowIR.FunctionSymbol.Declared val function = moduleDFG.functions[symbol] ?: externalModulesDFG.functionDFGs[symbol]
&& it !is DataFlowIR.Node.VirtualCall val body = function!!.body
&& !directEdges.containsKey(callee)) body.nodes.filterIsInstance<DataFlowIR.Node.Call>()
dfs(callee) .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 DevirtualizedCallSite(val possibleCallees: List<DevirtualizedCallee>)
class AnalysisResult(val devirtualizedCallSites: Map<DataFlowIR.Node.VirtualCall, DevirtualizedCallSite>)
fun run(irModule: IrModuleFragment, context: Context, moduleDFG: ModuleDFG, externalModulesDFG: ExternalModulesDFG) fun run(irModule: IrModuleFragment, context: Context, moduleDFG: ModuleDFG, externalModulesDFG: ExternalModulesDFG)
: Map<DataFlowIR.Node.VirtualCall, DevirtualizedCallSite> { : AnalysisResult {
val devirtualizationAnalysisResult = DevirtualizationAnalysis(context, moduleDFG, externalModulesDFG).analyze() val devirtualizationAnalysisResult = DevirtualizationAnalysis(context, moduleDFG, externalModulesDFG).analyze()
val devirtualizedCallSites = val devirtualizedCallSites =
devirtualizationAnalysisResult devirtualizationAnalysisResult
@@ -897,7 +899,7 @@ internal object Devirtualization {
.filter { it.key.callSite != null } .filter { it.key.callSite != null }
.associate { it.key.callSite!! to it.value } .associate { it.key.callSite!! to it.value }
Devirtualization.devirtualize(irModule, context, devirtualizedCallSites) Devirtualization.devirtualize(irModule, context, devirtualizedCallSites)
return devirtualizationAnalysisResult return AnalysisResult(devirtualizationAnalysisResult)
} }
private fun devirtualize(irModule: IrModuleFragment, context: Context, private fun devirtualize(irModule: IrModuleFragment, context: Context,