Rewrote escape analysis to use DFG
This commit is contained in:
-1
@@ -89,7 +89,6 @@ internal fun produceOutput(context: Context) {
|
|||||||
llvmModule,
|
llvmModule,
|
||||||
nopack,
|
nopack,
|
||||||
manifest,
|
manifest,
|
||||||
context.escapeAnalysisResult.getValueOrNull()?.build()?.toByteArray(),
|
|
||||||
context.dataFlowGraph)
|
context.dataFlowGraph)
|
||||||
|
|
||||||
context.library = library
|
context.library = library
|
||||||
|
|||||||
-1
@@ -176,7 +176,6 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
|
|||||||
// But we have to wait until the code generation phase,
|
// But we have to wait until the code generation phase,
|
||||||
// to dump this information into generated file.
|
// to dump this information into generated file.
|
||||||
var serializedLinkData: LinkData? = null
|
var serializedLinkData: LinkData? = null
|
||||||
val escapeAnalysisResult = lazy { ModuleEscapeAnalysisResult.ModuleEAResult.newBuilder() }
|
|
||||||
var dataFlowGraph: ByteArray? = null
|
var dataFlowGraph: ByteArray? = null
|
||||||
|
|
||||||
@Deprecated("")
|
@Deprecated("")
|
||||||
|
|||||||
+204
-875
File diff suppressed because it is too large
Load Diff
-15
@@ -1,15 +0,0 @@
|
|||||||
syntax = "proto2";
|
|
||||||
package org.jetbrains.kotlin.backend.konan;
|
|
||||||
|
|
||||||
option java_outer_classname = "ModuleEscapeAnalysisResult";
|
|
||||||
option optimize_for = LITE_RUNTIME;
|
|
||||||
|
|
||||||
message FunctionEAResult {
|
|
||||||
required string fqName = 1;
|
|
||||||
required int32 escapes = 2;
|
|
||||||
repeated int32 pointsTo = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
message ModuleEAResult {
|
|
||||||
repeated FunctionEAResult functionEAResults = 1;
|
|
||||||
}
|
|
||||||
+1
-1
@@ -31,7 +31,7 @@ internal class DirectedGraphMultiNode<out K>(val nodes: Set<K>)
|
|||||||
|
|
||||||
internal class DirectedGraphCondensation<out K>(val topologicalOrder: List<DirectedGraphMultiNode<K>>)
|
internal class DirectedGraphCondensation<out K>(val topologicalOrder: List<DirectedGraphMultiNode<K>>)
|
||||||
|
|
||||||
internal class DirectedGraphCondensationBuilder<K, out N: DirectedGraphNode<K>>(val graph: DirectedGraph<K, N>) {
|
internal class DirectedGraphCondensationBuilder<K, out N: DirectedGraphNode<K>>(private val graph: DirectedGraph<K, N>) {
|
||||||
private val visited = mutableSetOf<K>()
|
private val visited = mutableSetOf<K>()
|
||||||
private val order = mutableListOf<N>()
|
private val order = mutableListOf<N>()
|
||||||
private val nodeToMultiNodeMap = mutableMapOf<N, DirectedGraphMultiNode<K>>()
|
private val nodeToMultiNodeMap = mutableMapOf<N, DirectedGraphMultiNode<K>>()
|
||||||
|
|||||||
-2
@@ -44,8 +44,6 @@ interface KonanLibraryLayout {
|
|||||||
get() = File(libDir, "linkdata")
|
get() = File(libDir, "linkdata")
|
||||||
val moduleHeaderFile
|
val moduleHeaderFile
|
||||||
get() = File(linkdataDir, "module")
|
get() = File(linkdataDir, "module")
|
||||||
val escapeAnalysisFile
|
|
||||||
get() = File(linkdataDir, "module_escape_analysis")
|
|
||||||
val dataFlowGraphFile
|
val dataFlowGraphFile
|
||||||
get() = File(linkdataDir, "module_data_flow_graph")
|
get() = File(linkdataDir, "module_data_flow_graph")
|
||||||
fun packageFile(packageName: String)
|
fun packageFile(packageName: String)
|
||||||
|
|||||||
-1
@@ -28,7 +28,6 @@ interface KonanLibraryReader {
|
|||||||
val linkerOpts: List<String>
|
val linkerOpts: List<String>
|
||||||
val unresolvedDependencies: List<String>
|
val unresolvedDependencies: List<String>
|
||||||
val dataFlowGraph: ByteArray?
|
val dataFlowGraph: ByteArray?
|
||||||
val escapeAnalysis: ByteArray?
|
|
||||||
val isDefaultLibrary: Boolean get() = false
|
val isDefaultLibrary: Boolean get() = false
|
||||||
val isNeededForLink: Boolean get() = true
|
val isNeededForLink: Boolean get() = true
|
||||||
val manifestProperties: Properties
|
val manifestProperties: Properties
|
||||||
|
|||||||
-1
@@ -25,7 +25,6 @@ interface KonanLibraryWriter {
|
|||||||
fun addKotlinBitcode(llvmModule: LLVMModuleRef)
|
fun addKotlinBitcode(llvmModule: LLVMModuleRef)
|
||||||
fun addLinkDependencies(libraries: List<KonanLibraryReader>)
|
fun addLinkDependencies(libraries: List<KonanLibraryReader>)
|
||||||
fun addManifestAddend(path: String)
|
fun addManifestAddend(path: String)
|
||||||
fun addEscapeAnalysis(escapeAnalysis: ByteArray)
|
|
||||||
fun addDataFlowGraph(dataFlowGraph: ByteArray)
|
fun addDataFlowGraph(dataFlowGraph: ByteArray)
|
||||||
val mainBitcodeFileName: String
|
val mainBitcodeFileName: String
|
||||||
fun commit()
|
fun commit()
|
||||||
|
|||||||
-1
@@ -51,7 +51,6 @@ class LibraryReaderImpl(var libraryFile: File, val currentAbiVersion: Int,
|
|||||||
}
|
}
|
||||||
|
|
||||||
val targetList = inPlace.targetsDir.listFiles.map{it.name}
|
val targetList = inPlace.targetsDir.listFiles.map{it.name}
|
||||||
override val escapeAnalysis by lazy { inPlace.escapeAnalysisFile.let { if (it.exists) it.readBytes() else null } }
|
|
||||||
override val dataFlowGraph by lazy { inPlace.dataFlowGraphFile.let { if (it.exists) it.readBytes() else null } }
|
override val dataFlowGraph by lazy { inPlace.dataFlowGraphFile.let { if (it.exists) it.readBytes() else null } }
|
||||||
|
|
||||||
override val libraryName
|
override val libraryName
|
||||||
|
|||||||
-6
@@ -101,10 +101,6 @@ class LibraryWriterImpl(override val libDir: File, moduleName: String, currentAb
|
|||||||
manifestProperties.putAll(properties)
|
manifestProperties.putAll(properties)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun addEscapeAnalysis(escapeAnalysis: ByteArray) {
|
|
||||||
escapeAnalysisFile.writeBytes(escapeAnalysis)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addDataFlowGraph(dataFlowGraph: ByteArray) {
|
override fun addDataFlowGraph(dataFlowGraph: ByteArray) {
|
||||||
dataFlowGraphFile.writeBytes(dataFlowGraph)
|
dataFlowGraphFile.writeBytes(dataFlowGraph)
|
||||||
}
|
}
|
||||||
@@ -130,7 +126,6 @@ internal fun buildLibrary(
|
|||||||
llvmModule: LLVMModuleRef,
|
llvmModule: LLVMModuleRef,
|
||||||
nopack: Boolean,
|
nopack: Boolean,
|
||||||
manifest: String?,
|
manifest: String?,
|
||||||
escapeAnalysis: ByteArray?,
|
|
||||||
dataFlowGraph: ByteArray?): KonanLibraryWriter {
|
dataFlowGraph: ByteArray?): KonanLibraryWriter {
|
||||||
|
|
||||||
val library = LibraryWriterImpl(output, moduleName, abiVersion, target, nopack)
|
val library = LibraryWriterImpl(output, moduleName, abiVersion, target, nopack)
|
||||||
@@ -145,7 +140,6 @@ internal fun buildLibrary(
|
|||||||
}
|
}
|
||||||
manifest ?.let { library.addManifestAddend(it) }
|
manifest ?.let { library.addManifestAddend(it) }
|
||||||
library.addLinkDependencies(linkDependencies)
|
library.addLinkDependencies(linkDependencies)
|
||||||
escapeAnalysis?.let { library.addEscapeAnalysis(it) }
|
|
||||||
dataFlowGraph?.let { library.addDataFlowGraph(it) }
|
dataFlowGraph?.let { library.addDataFlowGraph(it) }
|
||||||
|
|
||||||
library.commit()
|
library.commit()
|
||||||
|
|||||||
+2
-3
@@ -24,10 +24,8 @@ import org.jetbrains.kotlin.backend.common.ir.ir2string
|
|||||||
import org.jetbrains.kotlin.backend.konan.*
|
import org.jetbrains.kotlin.backend.konan.*
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.*
|
import org.jetbrains.kotlin.backend.konan.descriptors.*
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.*
|
import org.jetbrains.kotlin.backend.konan.ir.*
|
||||||
import org.jetbrains.kotlin.backend.konan.library.impl.buildLibrary
|
|
||||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport
|
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport
|
||||||
import org.jetbrains.kotlin.backend.konan.optimizations.*
|
import org.jetbrains.kotlin.backend.konan.optimizations.*
|
||||||
import org.jetbrains.kotlin.backend.konan.util.getValueOrNull
|
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
@@ -94,7 +92,8 @@ internal fun emitLLVM(context: Context) {
|
|||||||
val lifetimes = mutableMapOf<IrElement, Lifetime>()
|
val lifetimes = mutableMapOf<IrElement, Lifetime>()
|
||||||
val codegenVisitor = CodeGeneratorVisitor(context, lifetimes)
|
val codegenVisitor = CodeGeneratorVisitor(context, lifetimes)
|
||||||
phaser.phase(KonanPhase.ESCAPE_ANALYSIS) {
|
phaser.phase(KonanPhase.ESCAPE_ANALYSIS) {
|
||||||
EscapeAnalysis.computeLifetimes(irModule, context, codegenVisitor.codegen, lifetimes)
|
val callGraph = CallGraphBuilder(context, moduleDFG!!, externalModulesDFG!!).build()
|
||||||
|
EscapeAnalysis.computeLifetimes(moduleDFG!!, externalModulesDFG!!, callGraph, lifetimes)
|
||||||
}
|
}
|
||||||
|
|
||||||
phaser.phase(KonanPhase.CODEGEN) {
|
phaser.phase(KonanPhase.CODEGEN) {
|
||||||
|
|||||||
+113
@@ -0,0 +1,113 @@
|
|||||||
|
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.backend.konan.llvm.findMainEntryPoint
|
||||||
|
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
||||||
|
|
||||||
|
internal class CallGraphNode(val graph: CallGraph, val symbol: DataFlowIR.FunctionSymbol)
|
||||||
|
: DirectedGraphNode<DataFlowIR.FunctionSymbol> {
|
||||||
|
|
||||||
|
override val key get() = symbol
|
||||||
|
|
||||||
|
override val directEdges: List<DataFlowIR.FunctionSymbol> by lazy {
|
||||||
|
graph.directEdges[symbol]!!.callSites
|
||||||
|
.map { it.actualCallee }
|
||||||
|
.filter { graph.reversedEdges.containsKey(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override val reversedEdges: List<DataFlowIR.FunctionSymbol> by lazy {
|
||||||
|
graph.reversedEdges[symbol]!!
|
||||||
|
}
|
||||||
|
|
||||||
|
class CallSite(val call: DataFlowIR.Node.Call, val actualCallee: DataFlowIR.FunctionSymbol)
|
||||||
|
|
||||||
|
val callSites = mutableListOf<CallSite>()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class CallGraph(val directEdges: Map<DataFlowIR.FunctionSymbol, CallGraphNode>,
|
||||||
|
val reversedEdges: Map<DataFlowIR.FunctionSymbol, MutableList<DataFlowIR.FunctionSymbol>>)
|
||||||
|
: DirectedGraph<DataFlowIR.FunctionSymbol, CallGraphNode> {
|
||||||
|
|
||||||
|
override val nodes get() = directEdges.values
|
||||||
|
|
||||||
|
override fun get(key: DataFlowIR.FunctionSymbol) = directEdges[key]!!
|
||||||
|
|
||||||
|
fun addEdge(caller: DataFlowIR.FunctionSymbol, callSite: CallGraphNode.CallSite) {
|
||||||
|
directEdges[caller]!!.callSites += callSite
|
||||||
|
reversedEdges[callSite.actualCallee]?.add(caller)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class CallGraphBuilder(val context: Context,
|
||||||
|
val moduleDFG: ModuleDFG,
|
||||||
|
val externalModulesDFG: ExternalModulesDFG) {
|
||||||
|
|
||||||
|
private val DEBUG = 0
|
||||||
|
|
||||||
|
private inline fun DEBUG_OUTPUT(severity: Int, block: () -> Unit) {
|
||||||
|
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 fun DataFlowIR.FunctionSymbol.resolved(): DataFlowIR.FunctionSymbol {
|
||||||
|
if (this is DataFlowIR.FunctionSymbol.External)
|
||||||
|
return externalModulesDFG.publicFunctions[this.hash] ?: this
|
||||||
|
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 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(findMainEntryPoint(context)!!).resolved()) +
|
||||||
|
moduleDFG.functions
|
||||||
|
.filter { it.value.isGlobalInitializer }
|
||||||
|
.map { it.key }
|
||||||
|
} else {
|
||||||
|
moduleDFG.functions.keys.filterIsInstance<DataFlowIR.FunctionSymbol.Public>()
|
||||||
|
}
|
||||||
|
@Suppress("LoopToCallChain")
|
||||||
|
for (symbol in rootSet) {
|
||||||
|
if (!directEdges.containsKey(symbol))
|
||||||
|
dfs(symbol)
|
||||||
|
}
|
||||||
|
return callGraph
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun dfs(symbol: DataFlowIR.FunctionSymbol) {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
-6
@@ -262,13 +262,16 @@ internal object DFGSerializer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ExternalFunctionSymbol(val hash: Long, val numberOfParameters: Int, val name: String?) {
|
class ExternalFunctionSymbol(val hash: Long, val numberOfParameters: Int, val escapes: Int?, val pointsTo: IntArray?, val name: String?) {
|
||||||
|
|
||||||
constructor(data: ArraySlice) : this(data.readLong(), data.readInt(), data.readNullableString())
|
constructor(data: ArraySlice) : this(data.readLong(), data.readInt(), data.readNullableInt(),
|
||||||
|
data.readNullable { readIntArray() }, data.readNullableString())
|
||||||
|
|
||||||
fun write(result: ArraySlice) {
|
fun write(result: ArraySlice) {
|
||||||
result.writeLong(hash)
|
result.writeLong(hash)
|
||||||
result.writeInt(numberOfParameters)
|
result.writeInt(numberOfParameters)
|
||||||
|
result.writeNullableInt(escapes)
|
||||||
|
result.writeNullable(pointsTo) { writeIntArray(it) }
|
||||||
result.writeNullableString(name)
|
result.writeNullableString(name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -313,8 +316,8 @@ internal object DFGSerializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun external(hash: Long, numberOfParameters: Int, name: String?) =
|
fun external(hash: Long, numberOfParameters: Int, escapes: Int?, pointsTo: IntArray?, name: String?) =
|
||||||
FunctionSymbol(ExternalFunctionSymbol(hash, numberOfParameters, name), null, null)
|
FunctionSymbol(ExternalFunctionSymbol(hash, numberOfParameters, escapes, pointsTo, name), null, null)
|
||||||
|
|
||||||
fun public(hash: Long, numberOfParameters: Int, index: Int, name: String?) =
|
fun public(hash: Long, numberOfParameters: Int, index: Int, name: String?) =
|
||||||
FunctionSymbol(null, PublicFunctionSymbol(hash, numberOfParameters, index, name), null)
|
FunctionSymbol(null, PublicFunctionSymbol(hash, numberOfParameters, index, name), null)
|
||||||
@@ -675,7 +678,7 @@ internal object DFGSerializer {
|
|||||||
val name = functionSymbol.name
|
val name = functionSymbol.name
|
||||||
when (functionSymbol) {
|
when (functionSymbol) {
|
||||||
is DataFlowIR.FunctionSymbol.External ->
|
is DataFlowIR.FunctionSymbol.External ->
|
||||||
FunctionSymbol.external(functionSymbol.hash, numberOfParameters, name)
|
FunctionSymbol.external(functionSymbol.hash, numberOfParameters, functionSymbol.escapes, functionSymbol.pointsTo, name)
|
||||||
|
|
||||||
is DataFlowIR.FunctionSymbol.Public ->
|
is DataFlowIR.FunctionSymbol.Public ->
|
||||||
FunctionSymbol.public(functionSymbol.hash, numberOfParameters, functionSymbol.symbolTableIndex, name)
|
FunctionSymbol.public(functionSymbol.hash, numberOfParameters, functionSymbol.symbolTableIndex, name)
|
||||||
@@ -817,7 +820,7 @@ internal object DFGSerializer {
|
|||||||
val private = it.private
|
val private = it.private
|
||||||
when {
|
when {
|
||||||
external != null ->
|
external != null ->
|
||||||
DataFlowIR.FunctionSymbol.External(external.hash, external.numberOfParameters, external.name)
|
DataFlowIR.FunctionSymbol.External(external.hash, external.numberOfParameters, external.escapes, external.pointsTo, external.name)
|
||||||
|
|
||||||
public != null -> {
|
public != null -> {
|
||||||
val symbolTableIndex = public.index
|
val symbolTableIndex = public.index
|
||||||
|
|||||||
+147
-107
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.backend.konan.llvm.isExported
|
|||||||
import org.jetbrains.kotlin.backend.konan.llvm.localHash
|
import org.jetbrains.kotlin.backend.konan.llvm.localHash
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.symbolName
|
import org.jetbrains.kotlin.backend.konan.llvm.symbolName
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrField
|
import org.jetbrains.kotlin.ir.declarations.IrField
|
||||||
@@ -22,6 +23,8 @@ import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
|||||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.resolve.constants.ConstantValue
|
||||||
|
import org.jetbrains.kotlin.resolve.constants.IntValue
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
@@ -96,7 +99,7 @@ internal object DataFlowIR {
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract class FunctionSymbol(val numberOfParameters: Int, val name: String?) {
|
abstract class FunctionSymbol(val numberOfParameters: Int, val name: String?) {
|
||||||
class External(val hash: Long, numberOfParameters: Int, name: String? = null)
|
class External(val hash: Long, numberOfParameters: Int, val escapes: Int?, val pointsTo: IntArray?, name: String? = null)
|
||||||
: FunctionSymbol(numberOfParameters, name) {
|
: FunctionSymbol(numberOfParameters, name) {
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
@@ -110,7 +113,7 @@ internal object DataFlowIR {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "ExternalFunction(hash='$hash', name='$name')"
|
return "ExternalFunction(hash='$hash', name='$name', escapes='$escapes', pointsTo='${pointsTo?.contentToString()}')"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,109 +214,8 @@ internal object DataFlowIR {
|
|||||||
val body: FunctionBody) {
|
val body: FunctionBody) {
|
||||||
|
|
||||||
|
|
||||||
fun printNode(node: Node, ids: Map<Node, Int>) {
|
|
||||||
when (node) {
|
|
||||||
is Node.Const ->
|
|
||||||
println(" CONST ${node.type}")
|
|
||||||
|
|
||||||
is Node.Parameter ->
|
|
||||||
println(" PARAM ${node.index}")
|
|
||||||
|
|
||||||
is Node.Singleton ->
|
|
||||||
println(" SINGLETON ${node.type}")
|
|
||||||
|
|
||||||
is Node.StaticCall -> {
|
|
||||||
println(" STATIC CALL ${node.callee}")
|
|
||||||
node.arguments.forEach {
|
|
||||||
print(" ARG #${ids[it.node]!!}")
|
|
||||||
if (it.castToType == null)
|
|
||||||
println()
|
|
||||||
else
|
|
||||||
println(" CASTED TO ${it.castToType}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is Node.VtableCall -> {
|
|
||||||
println(" VIRTUAL CALL ${node.callee}")
|
|
||||||
println(" RECEIVER: ${node.receiverType}")
|
|
||||||
println(" VTABLE INDEX: ${node.calleeVtableIndex}")
|
|
||||||
node.arguments.forEach {
|
|
||||||
print(" ARG #${ids[it.node]!!}")
|
|
||||||
if (it.castToType == null)
|
|
||||||
println()
|
|
||||||
else
|
|
||||||
println(" CASTED TO ${it.castToType}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is Node.ItableCall -> {
|
|
||||||
println(" INTERFACE CALL ${node.callee}")
|
|
||||||
println(" RECEIVER: ${node.receiverType}")
|
|
||||||
println(" METHOD HASH: ${node.calleeHash}")
|
|
||||||
node.arguments.forEach {
|
|
||||||
print(" ARG #${ids[it.node]!!}")
|
|
||||||
if (it.castToType == null)
|
|
||||||
println()
|
|
||||||
else
|
|
||||||
println(" CASTED TO ${it.castToType}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is Node.NewObject -> {
|
|
||||||
println(" NEW OBJECT ${node.callee}")
|
|
||||||
println(" TYPE ${node.returnType}")
|
|
||||||
node.arguments.forEach {
|
|
||||||
print(" ARG #${ids[it.node]!!}")
|
|
||||||
if (it.castToType == null)
|
|
||||||
println()
|
|
||||||
else
|
|
||||||
println(" CASTED TO ${it.castToType}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is Node.FieldRead -> {
|
|
||||||
println(" FIELD READ ${node.field}")
|
|
||||||
print(" RECEIVER #${node.receiver?.node?.let { ids[it]!! } ?: "null"}")
|
|
||||||
if (node.receiver?.castToType == null)
|
|
||||||
println()
|
|
||||||
else
|
|
||||||
println(" CASTED TO ${node.receiver.castToType}")
|
|
||||||
}
|
|
||||||
|
|
||||||
is Node.FieldWrite -> {
|
|
||||||
println(" FIELD WRITE ${node.field}")
|
|
||||||
print(" RECEIVER #${node.receiver?.node?.let { ids[it]!! } ?: "null"}")
|
|
||||||
if (node.receiver?.castToType == null)
|
|
||||||
println()
|
|
||||||
else
|
|
||||||
println(" CASTED TO ${node.receiver.castToType}")
|
|
||||||
print(" VALUE #${ids[node.value.node]!!}")
|
|
||||||
if (node.value.castToType == null)
|
|
||||||
println()
|
|
||||||
else
|
|
||||||
println(" CASTED TO ${node.value.castToType}")
|
|
||||||
}
|
|
||||||
|
|
||||||
is Node.Variable -> {
|
|
||||||
println(" ${if (node.temp) "TEMP VAR" else "VARIABLE"} ")
|
|
||||||
node.values.forEach {
|
|
||||||
print(" VAL #${ids[it.node]!!}")
|
|
||||||
if (it.castToType == null)
|
|
||||||
println()
|
|
||||||
else
|
|
||||||
println(" CASTED TO ${it.castToType}")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
println(" UNKNOWN: ${node::class.java}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun debugOutput() {
|
fun debugOutput() {
|
||||||
println("FUNCTION TEMPLATE $symbol")
|
println("FUNCTION $symbol")
|
||||||
println("Params: $numberOfParameters")
|
println("Params: $numberOfParameters")
|
||||||
val ids = body.nodes.withIndex().associateBy({ it.value }, { it.index })
|
val ids = body.nodes.withIndex().associateBy({ it.value }, { it.index })
|
||||||
body.nodes.forEach {
|
body.nodes.forEach {
|
||||||
@@ -323,6 +225,122 @@ internal object DataFlowIR {
|
|||||||
println(" RETURNS")
|
println(" RETURNS")
|
||||||
printNode(body.returns, ids)
|
printNode(body.returns, ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun printNode(node: Node, ids: Map<Node, Int>) = print(nodeToString(node, ids))
|
||||||
|
|
||||||
|
fun nodeToString(node: Node, ids: Map<Node, Int>) = when (node) {
|
||||||
|
is Node.Const ->
|
||||||
|
" CONST ${node.type}\n"
|
||||||
|
|
||||||
|
is Node.Parameter ->
|
||||||
|
" PARAM ${node.index}\n"
|
||||||
|
|
||||||
|
is Node.Singleton ->
|
||||||
|
" SINGLETON ${node.type}\n"
|
||||||
|
|
||||||
|
is Node.StaticCall -> {
|
||||||
|
val result = StringBuilder()
|
||||||
|
result.appendln(" STATIC CALL ${node.callee}")
|
||||||
|
node.arguments.forEach {
|
||||||
|
result.append(" ARG #${ids[it.node]!!}")
|
||||||
|
if (it.castToType == null)
|
||||||
|
result.appendln()
|
||||||
|
else
|
||||||
|
result.appendln(" CASTED TO ${it.castToType}")
|
||||||
|
}
|
||||||
|
result.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
is Node.VtableCall -> {
|
||||||
|
val result = StringBuilder()
|
||||||
|
result.appendln(" VIRTUAL CALL ${node.callee}")
|
||||||
|
result.appendln(" RECEIVER: ${node.receiverType}")
|
||||||
|
result.appendln(" VTABLE INDEX: ${node.calleeVtableIndex}")
|
||||||
|
node.arguments.forEach {
|
||||||
|
result.append(" ARG #${ids[it.node]!!}")
|
||||||
|
if (it.castToType == null)
|
||||||
|
result.appendln()
|
||||||
|
else
|
||||||
|
result.appendln(" CASTED TO ${it.castToType}")
|
||||||
|
}
|
||||||
|
result.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
is Node.ItableCall -> {
|
||||||
|
val result = StringBuilder()
|
||||||
|
result.appendln(" INTERFACE CALL ${node.callee}")
|
||||||
|
result.appendln(" RECEIVER: ${node.receiverType}")
|
||||||
|
result.appendln(" METHOD HASH: ${node.calleeHash}")
|
||||||
|
node.arguments.forEach {
|
||||||
|
result.append(" ARG #${ids[it.node]!!}")
|
||||||
|
if (it.castToType == null)
|
||||||
|
result.appendln()
|
||||||
|
else
|
||||||
|
result.appendln(" CASTED TO ${it.castToType}")
|
||||||
|
}
|
||||||
|
result.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
is Node.NewObject -> {
|
||||||
|
val result = StringBuilder()
|
||||||
|
result.appendln(" NEW OBJECT ${node.callee}")
|
||||||
|
result.appendln(" TYPE ${node.returnType}")
|
||||||
|
node.arguments.forEach {
|
||||||
|
result.append(" ARG #${ids[it.node]!!}")
|
||||||
|
if (it.castToType == null)
|
||||||
|
result.appendln()
|
||||||
|
else
|
||||||
|
result.appendln(" CASTED TO ${it.castToType}")
|
||||||
|
}
|
||||||
|
result.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
is Node.FieldRead -> {
|
||||||
|
val result = StringBuilder()
|
||||||
|
result.appendln(" FIELD READ ${node.field}")
|
||||||
|
result.append(" RECEIVER #${node.receiver?.node?.let { ids[it]!! } ?: "null"}")
|
||||||
|
if (node.receiver?.castToType == null)
|
||||||
|
result.appendln()
|
||||||
|
else
|
||||||
|
result.appendln(" CASTED TO ${node.receiver.castToType}")
|
||||||
|
result.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
is Node.FieldWrite -> {
|
||||||
|
val result = StringBuilder()
|
||||||
|
result.appendln(" FIELD WRITE ${node.field}")
|
||||||
|
result.append(" RECEIVER #${node.receiver?.node?.let { ids[it]!! } ?: "null"}")
|
||||||
|
if (node.receiver?.castToType == null)
|
||||||
|
result.appendln()
|
||||||
|
else
|
||||||
|
result.appendln(" CASTED TO ${node.receiver.castToType}")
|
||||||
|
print(" VALUE #${ids[node.value.node]!!}")
|
||||||
|
if (node.value.castToType == null)
|
||||||
|
result.appendln()
|
||||||
|
else
|
||||||
|
result.appendln(" CASTED TO ${node.value.castToType}")
|
||||||
|
result.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
is Node.Variable -> {
|
||||||
|
val result = StringBuilder()
|
||||||
|
result.appendln(" ${if (node.temp) "TEMP VAR" else "VARIABLE"} ")
|
||||||
|
node.values.forEach {
|
||||||
|
result.append(" VAL #${ids[it.node]!!}")
|
||||||
|
if (it.castToType == null)
|
||||||
|
result.appendln()
|
||||||
|
else
|
||||||
|
result.appendln(" CASTED TO ${it.castToType}")
|
||||||
|
}
|
||||||
|
result.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
" UNKNOWN: ${node::class.java}\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SymbolTable(val context: Context, val irModule: IrModuleFragment, val module: Module) {
|
class SymbolTable(val context: Context, val irModule: IrModuleFragment, val module: Module) {
|
||||||
@@ -334,6 +352,21 @@ internal object DataFlowIR {
|
|||||||
val classMap = mutableMapOf<ClassDescriptor, Type>()
|
val classMap = mutableMapOf<ClassDescriptor, Type>()
|
||||||
val functionMap = mutableMapOf<CallableDescriptor, FunctionSymbol>()
|
val functionMap = mutableMapOf<CallableDescriptor, FunctionSymbol>()
|
||||||
|
|
||||||
|
private val NAME_ESCAPES = Name.identifier("Escapes")
|
||||||
|
private val NAME_POINTS_TO = Name.identifier("PointsTo")
|
||||||
|
private val FQ_NAME_KONAN = FqName.fromSegments(listOf("konan"))
|
||||||
|
|
||||||
|
private val FQ_NAME_ESCAPES = FQ_NAME_KONAN.child(NAME_ESCAPES)
|
||||||
|
private val FQ_NAME_POINTS_TO = FQ_NAME_KONAN.child(NAME_POINTS_TO)
|
||||||
|
|
||||||
|
private val konanPackage = context.builtIns.builtInsModule.getPackage(FQ_NAME_KONAN).memberScope
|
||||||
|
private val escapesAnnotationDescriptor = konanPackage.getContributedClassifier(
|
||||||
|
NAME_ESCAPES, NoLookupLocation.FROM_BACKEND) as ClassDescriptor
|
||||||
|
private val escapesWhoDescriptor = escapesAnnotationDescriptor.unsubstitutedPrimaryConstructor!!.valueParameters.single()
|
||||||
|
private val pointsToAnnotationDescriptor = konanPackage.getContributedClassifier(
|
||||||
|
NAME_POINTS_TO, NoLookupLocation.FROM_BACKEND) as ClassDescriptor
|
||||||
|
private val pointsToOnWhomDescriptor = pointsToAnnotationDescriptor.unsubstitutedPrimaryConstructor!!.valueParameters.single()
|
||||||
|
|
||||||
var privateTypeIndex = 0
|
var privateTypeIndex = 0
|
||||||
var privateFunIndex = 0
|
var privateFunIndex = 0
|
||||||
var couldBeCalledVirtuallyIndex = 0
|
var couldBeCalledVirtuallyIndex = 0
|
||||||
@@ -427,9 +460,16 @@ internal object DataFlowIR {
|
|||||||
is FunctionDescriptor -> {
|
is FunctionDescriptor -> {
|
||||||
val name = if (it.isExported()) it.symbolName else it.internalName
|
val name = if (it.isExported()) it.symbolName else it.internalName
|
||||||
val numberOfParameters = it.allParameters.size + if (it.isSuspend) 1 else 0
|
val numberOfParameters = it.allParameters.size + if (it.isSuspend) 1 else 0
|
||||||
if (it.module != irModule.descriptor || it.externalOrIntrinsic())
|
if (it.module != irModule.descriptor || it.externalOrIntrinsic()) {
|
||||||
FunctionSymbol.External(name.localHash.value, numberOfParameters, takeName { name })
|
val escapesAnnotation = it.annotations.findAnnotation(FQ_NAME_ESCAPES)
|
||||||
else {
|
val pointsToAnnotation = it.annotations.findAnnotation(FQ_NAME_POINTS_TO)
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
val escapesBitMask = (escapesAnnotation?.allValueArguments?.get(escapesWhoDescriptor.name) as? ConstantValue<Int>)?.value
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
val pointsToBitMask = (pointsToAnnotation?.allValueArguments?.get(pointsToOnWhomDescriptor.name) as? ConstantValue<List<IntValue>>)?.value
|
||||||
|
FunctionSymbol.External(name.localHash.value, numberOfParameters, escapesBitMask,
|
||||||
|
pointsToBitMask?.let { it.map { it.value }.toIntArray() }, takeName { name })
|
||||||
|
} else {
|
||||||
val isAbstract = it.modality == Modality.ABSTRACT
|
val isAbstract = it.modality == Modality.ABSTRACT
|
||||||
val classDescriptor = it.containingDeclaration as? ClassDescriptor
|
val classDescriptor = it.containingDeclaration as? ClassDescriptor
|
||||||
val placeToFunctionsTable = !isAbstract && it !is ConstructorDescriptor && classDescriptor != null
|
val placeToFunctionsTable = !isAbstract && it !is ConstructorDescriptor && classDescriptor != null
|
||||||
|
|||||||
Reference in New Issue
Block a user