[K/N][optmz] Fixed a problem with call graph
Previously it was considered that function references leaking to the native world are being called at their definitions. This is wrong, as it is impossible to deduce from the Kotlin code alone when they will be called, so just simply assume them a part of the root set.
This commit is contained in:
-9
@@ -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 -> { }
|
||||
}
|
||||
}
|
||||
|
||||
+34
-24
@@ -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<DataFlowIR.Type.Declared>()
|
||||
.flatMap { it.vtable + it.itable.values.flatten() }
|
||||
.filterIsInstance<DataFlowIR.FunctionSymbol.Declared>()
|
||||
.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<DataFlowIR.Type.Declared>()
|
||||
.flatMap { it.vtable + it.itable.values.flatten() }
|
||||
.filterIsInstance<DataFlowIR.FunctionSymbol.Declared>()
|
||||
.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 (<this>) and its type will be added
|
||||
// to instantiating classes since all objects are final types.
|
||||
val associatedObjects = mutableListOf<DataFlowIR.FunctionSymbol>()
|
||||
context.irModule!!.acceptChildrenVoid(object: IrElementVisitorVoid {
|
||||
val associatedObjectConstructors = mutableListOf<DataFlowIR.FunctionSymbol>()
|
||||
// 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<DataFlowIR.FunctionSymbol>()
|
||||
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<DataFlowIR.Type.Declared>): String {
|
||||
|
||||
Reference in New Issue
Block a user