diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt index 93d3c53704d..cb80748d097 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt @@ -19,6 +19,7 @@ import llvm.LLVMLinkModules2 import llvm.LLVMWriteBitcodeToFile import org.jetbrains.kotlin.backend.konan.library.impl.buildLibrary import org.jetbrains.kotlin.backend.konan.llvm.parseBitcodeFile +import org.jetbrains.kotlin.backend.konan.util.getValueOrNull import org.jetbrains.kotlin.konan.target.CompilerOutputKind internal fun produceOutput(context: Context) { @@ -71,7 +72,8 @@ internal fun produceOutput(context: Context) { llvmModule, nopack, manifest, - context.moduleEscapeAnalysisResult.build().toByteArray()) + context.escapeAnalysisResult.getValueOrNull()?.build()?.toByteArray(), + context.dataFlowGraph) context.library = library context.bitcodeFileName = library.mainBitcodeFileName diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index 288dba71a35..c052dd58500 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -175,9 +175,8 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) { // But we have to wait until the code generation phase, // to dump this information into generated file. var serializedLinkData: LinkData? = null - val moduleEscapeAnalysisResult: ModuleEscapeAnalysisResult.ModuleEAResult.Builder by lazy { - ModuleEscapeAnalysisResult.ModuleEAResult.newBuilder() - } + val escapeAnalysisResult = lazy { ModuleEscapeAnalysisResult.ModuleEAResult.newBuilder() } + var dataFlowGraph: ByteArray? = null @Deprecated("") lateinit var psi2IrGeneratorContext: GeneratorContext diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/EscapeAnalysis.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/EscapeAnalysis.kt index 8e08814b987..64a1b296f3c 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/EscapeAnalysis.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/EscapeAnalysis.kt @@ -1334,7 +1334,7 @@ internal object EscapeAnalysis { functionEAResultBuilder.addPointsTo(pointsToMask) } functionEAResultBuilder.escapes = escapes - context.moduleEscapeAnalysisResult.addFunctionEAResults(functionEAResultBuilder) + context.escapeAnalysisResult.value.addFunctionEAResults(functionEAResultBuilder) } } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt index 45415eb3cbf..ea1f78722ef 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt @@ -60,7 +60,9 @@ enum class KonanPhase(val description: String, /* ... */ BITCODE("LLVM BitCode Generation"), /* ... ... */ RTTI("RTTI Generation"), /* ... ... */ BUILD_DFG("Data flow graph building"), - /* ... ... */ ESCAPE_ANALYSIS("Escape analysis", BUILD_DFG), + /* ... ... */ SERIALIZE_DFG("Data flow graph serializing", BUILD_DFG), + /* ... ... */ DESERIALIZE_DFG("Data flow graph deserializing"), + /* ... ... */ ESCAPE_ANALYSIS("Escape analysis", BUILD_DFG, DESERIALIZE_DFG), /* ... ... */ CODEGEN("Code Generation"), /* ... ... */ BITCODE_LINKER("Bitcode linking"), /* */ LINK_STAGE("Link stage"), diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/KonanLibrary.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/KonanLibrary.kt index f9ec2a87105..dd04c82bd79 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/KonanLibrary.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/KonanLibrary.kt @@ -45,6 +45,8 @@ interface KonanLibraryLayout { get() = File(linkdataDir, "module") val escapeAnalysisFile get() = File(linkdataDir, "module_escape_analysis") + val dataFlowGraphFile + get() = File(linkdataDir, "module_data_flow_graph") fun packageFile(packageName: String) = File(linkdataDir, if (packageName == "") "root_package.knm" else "package_$packageName.knm") } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/KonanLibraryReader.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/KonanLibraryReader.kt index 40966838ca8..6d691ef7c0f 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/KonanLibraryReader.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/KonanLibraryReader.kt @@ -27,6 +27,7 @@ interface KonanLibraryReader { val includedPaths: List val linkerOpts: List val unresolvedDependencies: List + val dataFlowGraph: ByteArray? val escapeAnalysis: ByteArray? val isDefaultLibrary: Boolean get() = false val isNeededForLink: Boolean get() = true diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/KonanLibraryWriter.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/KonanLibraryWriter.kt index e18c71d7c35..82c11e86430 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/KonanLibraryWriter.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/KonanLibraryWriter.kt @@ -26,6 +26,7 @@ interface KonanLibraryWriter { fun addLinkDependencies(libraries: List) fun addManifestAddend(path: String) fun addEscapeAnalysis(escapeAnalysis: ByteArray) + fun addDataFlowGraph(dataFlowGraph: ByteArray) val mainBitcodeFileName: String fun commit() } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/impl/KonanLibraryReaderImpl.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/impl/KonanLibraryReaderImpl.kt index a7838d1c2cd..fe71e1f4d68 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/impl/KonanLibraryReaderImpl.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/impl/KonanLibraryReaderImpl.kt @@ -18,7 +18,6 @@ package org.jetbrains.kotlin.backend.konan.library.impl import org.jetbrains.kotlin.backend.konan.KonanConfig import org.jetbrains.kotlin.backend.konan.library.KonanLibraryReader -import org.jetbrains.kotlin.backend.konan.createInteropLibrary import org.jetbrains.kotlin.backend.konan.serialization.emptyPackages import org.jetbrains.kotlin.backend.konan.serialization.deserializeModule import org.jetbrains.kotlin.konan.file.File @@ -52,7 +51,8 @@ class LibraryReaderImpl(var libraryFile: File, val currentAbiVersion: Int, } val targetList = inPlace.targetsDir.listFiles.map{it.name} - override val escapeAnalysis: ByteArray? by lazy { inPlace.escapeAnalysisFile.let { if (it.exists) it.readBytes() else null } } + 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 libraryName get() = inPlace.libraryName diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/impl/KonanLibraryWriterImpl.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/impl/KonanLibraryWriterImpl.kt index 8f6ecab485d..5c8324e3a03 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/impl/KonanLibraryWriterImpl.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/impl/KonanLibraryWriterImpl.kt @@ -105,6 +105,10 @@ class LibraryWriterImpl(override val libDir: File, moduleName: String, currentAb escapeAnalysisFile.writeBytes(escapeAnalysis) } + override fun addDataFlowGraph(dataFlowGraph: ByteArray) { + dataFlowGraphFile.writeBytes(dataFlowGraph) + } + override fun commit() { manifestProperties.saveToFile(manifestFile) if (!nopack) { @@ -126,7 +130,8 @@ internal fun buildLibrary( llvmModule: LLVMModuleRef, nopack: Boolean, manifest: String?, - escapeAnalysis: ByteArray?): KonanLibraryWriter { + escapeAnalysis: ByteArray?, + dataFlowGraph: ByteArray?): KonanLibraryWriter { val library = LibraryWriterImpl(output, moduleName, abiVersion, target, nopack) @@ -141,6 +146,7 @@ internal fun buildLibrary( manifest ?.let { library.addManifestAddend(it) } library.addLinkDependencies(linkDependencies) escapeAnalysis?.let { library.addEscapeAnalysis(it) } + dataFlowGraph?.let { library.addDataFlowGraph(it) } library.commit() return library 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 34fdba717d9..258c10973b9 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 @@ -81,6 +81,15 @@ internal fun emitLLVM(context: Context) { moduleDFG = ModuleDFGBuilder(context, irModule).build() } + phaser.phase(KonanPhase.SERIALIZE_DFG) { + DFGSerializer.serialize(context, moduleDFG!!) + } + + var externalModulesDFG: ExternalModulesDFG? = null + phaser.phase(KonanPhase.DESERIALIZE_DFG) { + externalModulesDFG = DFGSerializer.deserialize(context, moduleDFG!!.symbolTable.privateTypeIndex, moduleDFG!!.symbolTable.privateFunIndex) + } + val lifetimes = mutableMapOf() val codegenVisitor = CodeGeneratorVisitor(context, lifetimes) phaser.phase(KonanPhase.ESCAPE_ANALYSIS) { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt index 79d7ecad349..02f49b2ef60 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt @@ -197,6 +197,10 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag if (DEBUG > severity) block() } + private val TAKE_NAMES = false // Take fqNames for all functions and types (for debug purposes). + + private inline fun takeName(block: () -> String) = if (TAKE_NAMES) block() else null + private val module = DataFlowIR.Module(irModule.descriptor) private val symbolTable = DataFlowIR.SymbolTable(context, irModule, module) @@ -542,11 +546,13 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag is IrGetField -> { val receiver = value.receiver?.let { expressionToEdge(it) } val receiverType = value.receiver?.let { symbolTable.mapType(it.type) } + val name = value.descriptor.name.asString() DataFlowIR.Node.FieldRead( receiver, DataFlowIR.Field( receiverType, - value.descriptor.name.asString() + name.localHash.value, + takeName { name } ) ) } @@ -554,11 +560,13 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag is IrSetField -> { val receiver = value.receiver?.let { expressionToEdge(it) } val receiverType = value.receiver?.let { symbolTable.mapType(it.type) } + val name = value.descriptor.name.asString() DataFlowIR.Node.FieldWrite( receiver, DataFlowIR.Field( receiverType, - value.descriptor.name.asString() + name.localHash.value, + takeName { name } ), expressionToEdge(value.value) ) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGSerializer.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGSerializer.kt new file mode 100644 index 00000000000..3c1e0328e4f --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGSerializer.kt @@ -0,0 +1,1000 @@ +package org.jetbrains.kotlin.backend.konan.optimizations + +import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.config.CommonConfigurationKeys +import sun.misc.Unsafe + +internal class ExternalModulesDFG(val allTypes: List, + val publicTypes: Map, + val publicFunctions: Map, + val functionDFGs: Map) + +private val theUnsafe = Unsafe::class.java.getDeclaredField("theUnsafe").let { + it.isAccessible = true + it.get(null) as Unsafe +} + +private val byteArrayDataOffset = theUnsafe.arrayBaseOffset(ByteArray::class.java).toLong() +private val intArrayDataOffset = theUnsafe.arrayBaseOffset(IntArray::class.java).toLong() +private val charArrayDataOffset = theUnsafe.arrayBaseOffset(CharArray::class.java).toLong() +private val stringValueOffset = theUnsafe.objectFieldOffset(String::class.java.getDeclaredField("value")) + +private val VERSION = 1 + +internal object DFGSerializer { + + class ArraySlice(var array: ByteArray, var index: Int = 0) { + + //------------Write------------------------------------------------------------------// + + fun writeByte(value: Byte) { + ensureSize(1) + theUnsafe.putByte(array, byteArrayDataOffset + index, value) + index++ + } + + fun writeInt(value: Int) { + ensureSize(4) + theUnsafe.putInt(array, byteArrayDataOffset + index, value) + index += 4 + } + + fun writeNullableInt(value: Int?) = writeNullable(value) { this.writeInt(it) } + + fun writeLong(value: Long) { + ensureSize(8) + theUnsafe.putLong(array, byteArrayDataOffset + index, value) + index += 8 + } + + fun writeBoolean(value: Boolean) { + ensureSize(1) + theUnsafe.putBoolean(array, byteArrayDataOffset + index, value) + index++ + } + + //------------Read------------------------------------------------------------------// + + fun readByte(): Byte { + checkSize(1) + return theUnsafe.getByte(array, byteArrayDataOffset + index).also { index++ } + } + + fun readInt(): Int { + checkSize(4) + return theUnsafe.getInt(array, byteArrayDataOffset + index).also { index += 4 } + } + + fun readNullableInt() = readNullable { this.readInt() } + + fun readLong(): Long { + checkSize(8) + return theUnsafe.getLong(array, byteArrayDataOffset + index).also { index += 8 } + } + + fun readBoolean(): Boolean { + checkSize(1) + return theUnsafe.getBoolean(array, byteArrayDataOffset + index).also { index++ } + } + + //------------Write arrays------------------------------------------------------------------// + + fun writeIntArray(source: IntArray) { + writeInt(source.size) + val dataSize = source.size * 4 + ensureSize(dataSize) + theUnsafe.copyMemory(source, intArrayDataOffset, array, byteArrayDataOffset + index, dataSize.toLong()) + index += dataSize + } + + fun writeString(s: String) { + val value = theUnsafe.getObject(s, stringValueOffset) as CharArray + writeInt(value.size) + val dataSize = value.size * 2 + ensureSize(dataSize) + theUnsafe.copyMemory(value, charArrayDataOffset, array, byteArrayDataOffset + index, dataSize.toLong()) + index += dataSize + } + + fun writeNullableString(s: String?) = writeNullable(s) { writeString(it) } + + //------------Read arrays------------------------------------------------------------------// + + fun readIntArray(): IntArray { + val size = readInt() + val result = IntArray(size) + val dataSize = size * 4 + checkSize(dataSize) + theUnsafe.copyMemory(array, byteArrayDataOffset + index, result, intArrayDataOffset, dataSize.toLong()) + index += dataSize + return result + } + + fun readString(): String { + val size = readInt() + val data = CharArray(size) + val dataSize = size * 2 + checkSize(dataSize) + theUnsafe.copyMemory(array, byteArrayDataOffset + index, data, charArrayDataOffset, dataSize.toLong()) + index += dataSize + val str = theUnsafe.allocateInstance(String::class.java) as String + theUnsafe.putObject(str, stringValueOffset, data) + return str + } + + fun readNullableString() = readNullable { readString() } + + inline fun readArray(itemReader: ArraySlice.() -> T) = + Array(readInt()) { this.itemReader() } + + inline fun writeArray(array: Array, itemWriter: ArraySlice.(T) -> Unit) { + writeInt(array.size) + array.forEach { this.itemWriter(it) } + } + + inline fun readNullable(valueReader: ArraySlice.() -> T) = + if (readBoolean()) this.valueReader() else null + + inline fun writeNullable(value: T?, valueWriter: ArraySlice.(T) -> Unit) { + writeBoolean(value != null) + if (value != null) + this.valueWriter(value) + } + + fun trim() { + if (array.size > index) { + val newArray = ByteArray(index) + theUnsafe.copyMemory(array, byteArrayDataOffset, newArray, byteArrayDataOffset, index.toLong()) + array = newArray + } + } + + private fun ensureSize(size: Int) { + if (index + size > array.size) { + var newSize = array.size + while (newSize < index + size) + newSize *= 2 + val newArray = ByteArray(newSize) + theUnsafe.copyMemory(array, byteArrayDataOffset, newArray, byteArrayDataOffset, array.size.toLong()) + array = newArray + } + } + + private fun checkSize(size: Int) { + if (index + size > array.size) + error("Unexpected end of data") + } + } + + class ExternalType(val hash: Long, val name: String?) { + + constructor(data: ArraySlice) : this(data.readLong(), data.readNullableString()) + + fun write(result: ArraySlice) { + result.writeLong(hash) + result.writeNullableString(name) + } + } + + class ItableSlot(val hash: Long, val impl: Int) { + + constructor(data: ArraySlice) : this(data.readLong(), data.readInt()) + + fun write(result: ArraySlice) { + result.writeLong(hash) + result.writeInt(impl) + } + } + + class DeclaredType(val isFinal: Boolean, val isAbstract: Boolean, val superTypes: IntArray, + val vtable: IntArray, val itable: Array) { + + constructor(data: ArraySlice) : this(data.readBoolean(), data.readBoolean(), data.readIntArray(), + data.readIntArray(), data.readArray { ItableSlot(this) }) + + fun write(result: ArraySlice) { + result.writeBoolean(isFinal) + result.writeBoolean(isAbstract) + result.writeIntArray(superTypes) + result.writeIntArray(vtable) + result.writeArray(itable) { it.write(this) } + } + } + + class PublicType(val hash: Long, val intestines: DeclaredType, val name: String?) { + + constructor(data: ArraySlice) : this(data.readLong(), DeclaredType(data), data.readNullableString()) + + fun write(result: ArraySlice) { + result.writeLong(hash) + intestines.write(result) + result.writeNullableString(name) + } + } + + class PrivateType(val index: Int, val intestines: DeclaredType, val name: String?) { + + constructor(data: ArraySlice) : this(data.readInt(), DeclaredType(data), data.readNullableString()) + + fun write(result: ArraySlice) { + result.writeInt(index) + intestines.write(result) + result.writeNullableString(name) + } + } + + class Type(val external: ExternalType?, val public: PublicType?, val private: PrivateType?, val virtual: Boolean) { + + fun write(result: ArraySlice) { + result.writeByte( + when { + external != null -> 1 + public != null -> 2 + private != null -> 3 + else -> 0 + }.toByte() + ) + external?.write(result) + public?.write(result) + private?.write(result) + } + + companion object { + fun external(hash: Long, name: String?) = Type(ExternalType(hash, name), null, null, false) + + fun public(hash: Long, intestines: DeclaredType, name: String?) = Type(null, PublicType(hash, intestines, name), null, false) + + fun private(index: Int, intestines: DeclaredType, name: String?) = Type(null, null, PrivateType(index, intestines, name), false) + + fun virtual() = Type(null, null, null, true) + + fun read(data: ArraySlice): Type { + val tag = data.readByte().toInt() + return when (tag) { + 1 -> Type(ExternalType(data), null, null, false) + 2 -> Type(null, PublicType(data), null, false) + 3 -> Type(null, null, PrivateType(data), false) + else -> Type(null, null, null, true) + } + } + } + } + + class ExternalFunctionSymbol(val hash: Long, val name: String?) { + + constructor(data: ArraySlice) : this(data.readLong(), data.readNullableString()) + + fun write(result: ArraySlice) { + result.writeLong(hash) + result.writeNullableString(name) + } + } + + class PublicFunctionSymbol(val hash: Long, val index: Int, val name: String?) { + + constructor(data: ArraySlice) : this(data.readLong(), data.readInt(), data.readNullableString()) + + fun write(result: ArraySlice) { + result.writeLong(hash) + result.writeInt(index) + result.writeNullableString(name) + } + } + + class PrivateFunctionSymbol(val index: Int, val name: String?) { + + constructor(data: ArraySlice) : this(data.readInt(), data.readNullableString()) + + fun write(result: ArraySlice) { + result.writeInt(index) + result.writeNullableString(name) + } + } + + class FunctionSymbol(val external: ExternalFunctionSymbol?, val public: PublicFunctionSymbol?, val private: PrivateFunctionSymbol?) { + + fun write(result: ArraySlice) { + result.writeByte( + when { + external != null -> 1 + public != null -> 2 + private != null -> 3 + else -> 0 + }.toByte() + ) + external?.write(result) + public?.write(result) + private?.write(result) + } + + companion object { + fun external(hash: Long, name: String?) = FunctionSymbol(ExternalFunctionSymbol(hash, name), null, null) + + fun public(hash: Long, index: Int, name: String?) = FunctionSymbol(null, PublicFunctionSymbol(hash, index, name), null) + + fun private(index: Int, name: String?) = FunctionSymbol(null, null, PrivateFunctionSymbol(index, name)) + + fun read(data: ArraySlice): FunctionSymbol { + val tag = data.readByte().toInt() + return when (tag) { + 1 -> FunctionSymbol(ExternalFunctionSymbol(data), null, null) + 2 -> FunctionSymbol(null, PublicFunctionSymbol(data), null) + 3 -> FunctionSymbol(null, null, PrivateFunctionSymbol(data)) + else -> FunctionSymbol(null, null, null) + } + } + } + } + + class SymbolTable(val types: Array, val functionSymbols: Array) { + + constructor(data: ArraySlice) : this(data.readArray { Type.read(this) }, data.readArray { FunctionSymbol.read(this) }) + + fun write(result: ArraySlice) { + result.writeArray(types) { it.write(this) } + result.writeArray(functionSymbols) { it.write(this) } + } + } + + class Field(val type: Int?, val hash: Long, val name: String?) { + + constructor(data: ArraySlice) : this(data.readNullableInt(), data.readLong(), data.readNullable { readString() }) + + fun write(result: ArraySlice) { + result.writeNullableInt(type) + result.writeLong(hash) + result.writeNullable(name) { writeString(it) } + } + } + + class Edge(val node: Int, val castToType: Int?) { + + constructor(data: ArraySlice) : this(data.readInt(), data.readNullableInt()) + + fun write(result: ArraySlice) { + result.writeInt(node) + result.writeNullableInt(castToType) + } + } + + class Parameter(val index: Int) { + + constructor(data: ArraySlice) : this(data.readInt()) + + fun write(result: ArraySlice) { + result.writeInt(index) + } + } + + class Const(val type: Int) { + + constructor(data: ArraySlice) : this(data.readInt()) + + fun write(result: ArraySlice) { + result.writeInt(type) + } + } + + class Call(val callee: Int, val arguments: Array, val returnType: Int) { + + constructor(data: ArraySlice) : this(data.readInt(), data.readArray { Edge(this) }, data.readInt()) + + fun write(result: ArraySlice) { + result.writeInt(callee) + result.writeArray(arguments) { it.write(this) } + result.writeInt(returnType) + } + } + + class StaticCall(val call: Call, val receiverType: Int?) { + + constructor(data: ArraySlice) : this(Call(data), data.readNullableInt()) + + fun write(result: ArraySlice) { + call.write(result) + result.writeNullableInt(receiverType) + } + } + + class NewObject(val call: Call) { + + constructor(data: ArraySlice) : this(Call(data)) + + fun write(result: ArraySlice) { + call.write(result) + } + } + + class VirtualCall(val call: Call, val receiverType: Int) { + + constructor(data: ArraySlice) : this(Call(data), data.readInt()) + + fun write(result: ArraySlice) { + call.write(result) + result.writeInt(receiverType) + } + } + + class VtableCall(val virtualCall: VirtualCall, val calleeVtableIndex: Int) { + + constructor(data: ArraySlice) : this(VirtualCall(data), data.readInt()) + + fun write(result: ArraySlice) { + virtualCall.write(result) + result.writeInt(calleeVtableIndex) + } + } + + class ItableCall(val virtualCall: VirtualCall, val calleeHash: Long) { + + constructor(data: ArraySlice) : this(VirtualCall(data), data.readLong()) + + fun write(result: ArraySlice) { + virtualCall.write(result) + result.writeLong(calleeHash) + } + } + + class Singleton(val type: Int, val constructor: Int?) { + + constructor(data: ArraySlice) : this(data.readInt(), data.readNullableInt()) + + fun write(result: ArraySlice) { + result.writeInt(type) + result.writeNullableInt(constructor) + } + } + + class FieldRead(val receiver: Edge?, val field: Field) { + + constructor(data: ArraySlice) : this(data.readNullable { Edge(this) }, Field(data)) + + fun write(result: ArraySlice) { + result.writeNullable(receiver) { it.write(this) } + field.write(result) + } + } + + class FieldWrite(val receiver: Edge?, val field: Field, val value: Edge) { + + constructor(data: ArraySlice) : this(data.readNullable { Edge(this) }, Field(data), Edge(data)) + + fun write(result: ArraySlice) { + result.writeNullable(receiver) { it.write(this) } + field.write(result) + value.write(result) + } + } + + class Variable(val values: Array, val temp: Boolean) { + + constructor(data: ArraySlice) : this(data.readArray { Edge(this) }, data.readBoolean()) + + fun write(result: ArraySlice) { + result.writeArray(values) { it.write(this) } + result.writeBoolean(temp) + } + } + + enum class NodeType { + UNKNOWN, + PARAMETER, + CONST, + STATIC_CALL, + NEW_OBJECT, + VTABLE_CALL, + ITABLE_CALL, + SINGLETON, + FIELD_READ, + FIELD_WRITE, + VARIABLE + } + + class Node { + var parameter : Parameter? = null + var const : Const? = null + var staticCall : StaticCall? = null + var newObject : NewObject? = null + var vtableCall : VtableCall? = null + var itableCall : ItableCall? = null + var singleton : Singleton? = null + var fieldRead : FieldRead? = null + var fieldWrite : FieldWrite? = null + var variable : Variable? = null + + val type get() = when { + parameter != null -> NodeType.PARAMETER + const != null -> NodeType.CONST + staticCall != null -> NodeType.STATIC_CALL + newObject != null -> NodeType.NEW_OBJECT + vtableCall != null -> NodeType.VTABLE_CALL + itableCall != null -> NodeType.ITABLE_CALL + singleton != null -> NodeType.SINGLETON + fieldRead != null -> NodeType.FIELD_READ + fieldWrite != null -> NodeType.FIELD_WRITE + variable != null -> NodeType.VARIABLE + else -> NodeType.UNKNOWN + } + + fun write(result: ArraySlice) { + result.writeByte(type.ordinal.toByte()) + parameter ?.write(result) + const ?.write(result) + staticCall ?.write(result) + newObject ?.write(result) + vtableCall ?.write(result) + itableCall ?.write(result) + singleton ?.write(result) + fieldRead ?.write(result) + fieldWrite ?.write(result) + variable ?.write(result) + } + + companion object { + fun parameter(index: Int) = + Node().also { it.parameter = Parameter(index) } + + fun const(type: Int) = + Node().also { it.const = Const(type) } + + fun staticCall(call: Call, receiverType: Int?) = + Node().also { it.staticCall = StaticCall(call, receiverType) } + + fun newObject(call: Call) = + Node().also { it.newObject = NewObject(call) } + + fun vtableCall(virtualCall: VirtualCall, calleeVtableIndex: Int) = + Node().also { it.vtableCall = VtableCall(virtualCall, calleeVtableIndex) } + + fun itableCall(virtualCall: VirtualCall, calleeHash: Long) = + Node().also { it.itableCall = ItableCall(virtualCall, calleeHash) } + + fun singleton(type: Int, constructor: Int?) = + Node().also { it.singleton = Singleton(type, constructor) } + + fun fieldRead(receiver: Edge?, field: Field) = + Node().also { it.fieldRead = FieldRead(receiver, field) } + + fun fieldWrite(receiver: Edge?, field: Field, value: Edge) = + Node().also { it.fieldWrite = FieldWrite(receiver, field, value) } + + fun variable(values: Array, temp: Boolean) = + Node().also { it.variable = Variable(values, temp) } + + fun read(data: ArraySlice): Node { + val type = enumValues()[data.readByte().toInt()] + val result = Node() + when (type) { + NodeType.PARAMETER -> result.parameter = Parameter (data) + NodeType.CONST -> result.const = Const (data) + NodeType.STATIC_CALL -> result.staticCall = StaticCall (data) + NodeType.NEW_OBJECT -> result.newObject = NewObject (data) + NodeType.VTABLE_CALL -> result.vtableCall = VtableCall (data) + NodeType.ITABLE_CALL -> result.itableCall = ItableCall (data) + NodeType.SINGLETON -> result.singleton = Singleton (data) + NodeType.FIELD_READ -> result.fieldRead = FieldRead (data) + NodeType.FIELD_WRITE -> result.fieldWrite = FieldWrite (data) + NodeType.VARIABLE -> result.variable = Variable (data) + else -> { } + } + return result + } + } + } + + class FunctionBody(val nodes: Array, val returns: Int) { + + constructor(data: ArraySlice) : this(data.readArray { Node.read(this) }, data.readInt()) + + fun write(result: ArraySlice) { + result.writeArray(nodes) { it.write(this) } + result.writeInt(returns) + } + } + + class Function(val symbol: Int, val isGlobalInitializer: Boolean, + val numberOfParameters: Int, val body: FunctionBody) { + + constructor(data: ArraySlice) : this(data.readInt(), data.readBoolean(), data.readInt(), FunctionBody(data)) + + fun write(result: ArraySlice) { + result.writeInt(symbol) + result.writeBoolean(isGlobalInitializer) + result.writeInt(numberOfParameters) + body.write(result) + } + } + + class Module(val symbolTable: SymbolTable, val functions: Array) { + + constructor(data: ArraySlice) : this(SymbolTable(data), data.readArray { Function(this) }) + + fun write(result: ArraySlice) { + symbolTable.write(result) + result.writeArray(functions) { it.write(this) } + } + } + + private val DEBUG = 0 + + private inline fun DEBUG_OUTPUT(severity: Int, block: () -> Unit) { + if (DEBUG > severity) block() + } + + fun serialize(context: Context, moduleDFG: ModuleDFG) { + val symbolTable = moduleDFG.symbolTable + val typeMap = (symbolTable.classMap.values + DataFlowIR.Type.Virtual).distinct().withIndex().associateBy({ it.value }, { it.index }) + val functionSymbolMap = symbolTable.functionMap.values.distinct().withIndex().associateBy({ it.value }, { it.index }) + DEBUG_OUTPUT(1) { + println("TYPES: ${typeMap.size}, " + + "FUNCTIONS: ${functionSymbolMap.size}, " + + "PRIVATE FUNCTIONS: ${functionSymbolMap.keys.count { it is DataFlowIR.FunctionSymbol.Private }}, " + + "FUNCTION TABLE SIZE: ${symbolTable.couldBeCalledVirtuallyIndex}" + ) + } + val types = typeMap.entries + .sortedBy { it.value } + .map { + fun buildTypeIntestines(type: DataFlowIR.Type.Declared) = + DeclaredType( + type.isFinal, + type.isAbstract, + type.superTypes.map { typeMap[it]!! }.toIntArray(), + type.vtable.map { functionSymbolMap[it]!! }.toIntArray(), + type.itable.map { (hash, symbol) -> ItableSlot(hash, functionSymbolMap[symbol]!!) }.toTypedArray() + ) + + val type = it.key + when (type) { + DataFlowIR.Type.Virtual -> Type.virtual() + + is DataFlowIR.Type.External -> Type.external(type.hash, type.name) + + is DataFlowIR.Type.Public -> Type.public(type.hash, buildTypeIntestines(type), type.name) + + is DataFlowIR.Type.Private -> Type.private(type.index, buildTypeIntestines(type), type.name) + + else -> error("Unknown type $type") + } + } + .toTypedArray() + val functionSymbols = functionSymbolMap.entries + .sortedBy { it.value } + .map { + val functionSymbol = it.key + when (functionSymbol) { + is DataFlowIR.FunctionSymbol.External -> + FunctionSymbol.external(functionSymbol.hash, functionSymbol.name) + + is DataFlowIR.FunctionSymbol.Public -> + FunctionSymbol.public(functionSymbol.hash, functionSymbol.symbolTableIndex, functionSymbol.name) + + is DataFlowIR.FunctionSymbol.Private -> + FunctionSymbol.private(functionSymbol.symbolTableIndex, functionSymbol.name) + + else -> error("Unknown function symbol $functionSymbol") + } + } + .toTypedArray() + val functions = moduleDFG.functions.values + .map { function -> + val body = function.body + val nodeMap = body.nodes.withIndex().associateBy({ it.value }, { it.index }) + val nodes = nodeMap.entries + .sortedBy { it.value } + .map { + val node = it.key + + fun buildEdge(edge: DataFlowIR.Edge) = + Edge(nodeMap[edge.node]!!, edge.castToType?.let { typeMap[it]!! }) + + fun buildCall(call: DataFlowIR.Node.Call) = + Call( + functionSymbolMap[call.callee]!!, + call.arguments.map { buildEdge(it) }.toTypedArray(), + typeMap[call.returnType]!! + ) + + fun buildVirtualCall(virtualCall: DataFlowIR.Node.VirtualCall) = + VirtualCall(buildCall(virtualCall), typeMap[virtualCall.receiverType]!!) + + fun buildField(field: DataFlowIR.Field) = + Field(field.type?.let { typeMap[it]!! }, field.hash, field.name) + + when (node) { + is DataFlowIR.Node.Parameter -> Node.parameter(node.index) + + is DataFlowIR.Node.Const -> Node.const(typeMap[node.type]!!) + + is DataFlowIR.Node.StaticCall -> + Node.staticCall(buildCall(node), node.receiverType?.let { typeMap[it]!! }) + + is DataFlowIR.Node.NewObject -> Node.newObject(buildCall(node)) + + is DataFlowIR.Node.VtableCall -> + Node.vtableCall(buildVirtualCall(node), node.calleeVtableIndex) + + is DataFlowIR.Node.ItableCall -> + Node.itableCall(buildVirtualCall(node), node.calleeHash) + + is DataFlowIR.Node.Singleton -> + Node.singleton(typeMap[node.type]!!, node.constructor?.let { functionSymbolMap[it]!! }) + + is DataFlowIR.Node.FieldRead -> + Node.fieldRead(node.receiver?.let { buildEdge(it) }, buildField(node.field)) + + is DataFlowIR.Node.FieldWrite -> + Node.fieldWrite(node.receiver?.let { buildEdge(it) }, buildField(node.field), buildEdge(node.value)) + + is DataFlowIR.Node.Variable -> + Node.variable(node.values.map { buildEdge(it) }.toTypedArray(), node.temp) + + else -> error("Unknown node $node") + } + } + .toTypedArray() + Function( + functionSymbolMap[function.symbol]!!, + function.isGlobalInitializer, + function.numberOfParameters, + FunctionBody(nodes, nodeMap[body.returns]!!) + ) + } + .toTypedArray() + val module = Module(SymbolTable(types, functionSymbols), functions) + val writer = ArraySlice(ByteArray(1024)) + writer.writeInt(VERSION) + module.write(writer) + writer.trim() + context.dataFlowGraph = writer.array + } + + // TODO: Deserialize functions bodies lazily. + fun deserialize(context: Context, startPrivateTypeIndex: Int, startPrivateFunIndex: Int): ExternalModulesDFG { + var privateTypeIndex = startPrivateTypeIndex + var privateFunIndex = startPrivateFunIndex + val publicTypesMap = mutableMapOf() + val allTypes = mutableListOf() + val publicFunctionsMap = mutableMapOf() + val functions = mutableMapOf() + val specifics = context.config.configuration.get(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS)!! + context.librariesWithDependencies.forEach { library -> + val libraryDataFlowGraph = library.dataFlowGraph + + DEBUG_OUTPUT(1) { + println("Data flow graph size for lib '${library.libraryName}': ${libraryDataFlowGraph?.size ?: 0}") + } + + if (libraryDataFlowGraph != null) { + val module = DataFlowIR.Module(library.moduleDescriptor(specifics)) + val reader = ArraySlice(libraryDataFlowGraph) + val version = reader.readInt() + if (version != VERSION) + error("Expected version $VERSION but actual is $version") + val moduleDataFlowGraph = Module(reader) + + val symbolTable = moduleDataFlowGraph.symbolTable + val types = symbolTable.types.map { + if (it.virtual) + DataFlowIR.Type.Virtual + else { + val external = it.external + val public = it.public + val private = it.private + when { + external != null -> DataFlowIR.Type.External(external.hash, external.name) + + public != null -> + DataFlowIR.Type.Public(public.hash, public.intestines.isFinal, + public.intestines.isAbstract, public.name).also { + publicTypesMap.put(it.hash, it) + allTypes += it + } + + else -> + DataFlowIR.Type.Private(privateTypeIndex++, private!!.intestines.isFinal, + private.intestines.isAbstract, private.name).also { + allTypes += it + } + } + } + } + + val functionSymbols = symbolTable.functionSymbols.map { + val external = it.external + val public = it.public + val private = it.private + when { + external != null -> DataFlowIR.FunctionSymbol.External(external.hash, external.name) + + public != null -> { + val symbolTableIndex = public.index + if (symbolTableIndex >= 0) + ++module.numberOfFunctions + DataFlowIR.FunctionSymbol.Public(public.hash, module, symbolTableIndex, public.name).also { + publicFunctionsMap.put(it.hash, it) + } + } + + else -> { + val symbolTableIndex = private!!.index + if (symbolTableIndex >= 0) + ++module.numberOfFunctions + DataFlowIR.FunctionSymbol.Private(privateFunIndex++, module, symbolTableIndex, private.name) + } + } + } + + DEBUG_OUTPUT(1) { + println("Lib: ${library.libraryName}, types: ${types.size}, functions: ${functionSymbols.size}") + } + + symbolTable.types.forEachIndexed { index, type -> + val deserializedType = types[index] as? DataFlowIR.Type.Declared + ?: return@forEachIndexed + if (deserializedType == DataFlowIR.Type.Virtual) + return@forEachIndexed + val intestines = if (deserializedType is DataFlowIR.Type.Public) + type.public!!.intestines + else + type.private!!.intestines + deserializedType.superTypes += intestines.superTypes.map { types[it] } + deserializedType.vtable += intestines.vtable.map { functionSymbols[it] } + intestines.itable.forEach { + deserializedType.itable.put(it.hash, functionSymbols[it.impl]) + } + } + + fun deserializeEdge(edge: Edge) = + DataFlowIR.Edge(edge.castToType?.let { types[it] }) + + fun deserializeCall(call: Call) = + DataFlowIR.Node.Call( + functionSymbols[call.callee], + call.arguments.map { deserializeEdge(it) }, + types[call.returnType] + ) + + fun deserializeVirtualCall(virtualCall: VirtualCall): DataFlowIR.Node.VirtualCall { + val call = deserializeCall(virtualCall.call) + return DataFlowIR.Node.VirtualCall( + call.callee, + call.arguments, + call.returnType, + types[virtualCall.receiverType], + null + ) + } + + fun deserializeField(field: Field) = + DataFlowIR.Field(field.type?.let { types[it] }, field.hash, field.name) + + fun deserializeBody(body: FunctionBody): DataFlowIR.FunctionBody { + val nodes = body.nodes.map { + when (it.type) { + NodeType.PARAMETER -> + DataFlowIR.Node.Parameter(it.parameter!!.index) + + NodeType.CONST -> + DataFlowIR.Node.Const(types[it.const!!.type]) + + NodeType.STATIC_CALL -> { + val staticCall = it.staticCall!! + val call = deserializeCall(staticCall.call) + val receiverType = staticCall.receiverType?.let { types[it] } + DataFlowIR.Node.StaticCall(call.callee, call.arguments, call.returnType, receiverType) + } + + NodeType.NEW_OBJECT -> { + val call = deserializeCall(it.newObject!!.call) + DataFlowIR.Node.NewObject(call.callee, call.arguments, call.returnType) + } + + NodeType.VTABLE_CALL -> { + val vtableCall = it.vtableCall!! + val virtualCall = deserializeVirtualCall(vtableCall.virtualCall) + DataFlowIR.Node.VtableCall( + virtualCall.callee, + virtualCall.receiverType, + vtableCall.calleeVtableIndex, + virtualCall.arguments, + virtualCall.returnType, + virtualCall.callSite + ) + } + + NodeType.ITABLE_CALL -> { + val itableCall = it.itableCall!! + val virtualCall = deserializeVirtualCall(itableCall.virtualCall) + DataFlowIR.Node.ItableCall( + virtualCall.callee, + virtualCall.receiverType, + itableCall.calleeHash, + virtualCall.arguments, + virtualCall.returnType, + virtualCall.callSite + ) + } + + NodeType.SINGLETON -> { + val singleton = it.singleton!! + DataFlowIR.Node.Singleton(types[singleton.type], singleton.constructor?.let { functionSymbols[it] }) + } + + NodeType.FIELD_READ -> { + val fieldRead = it.fieldRead!! + val receiver = fieldRead.receiver?.let { deserializeEdge(it) } + DataFlowIR.Node.FieldRead(receiver, deserializeField(fieldRead.field)) + } + + NodeType.FIELD_WRITE -> { + val fieldWrite = it.fieldWrite!! + val receiver = fieldWrite.receiver?.let { deserializeEdge(it) } + DataFlowIR.Node.FieldWrite(receiver, deserializeField(fieldWrite.field), deserializeEdge(fieldWrite.value)) + } + + NodeType.VARIABLE -> { + val variable = it.variable!! + DataFlowIR.Node.Variable(variable.values.map { deserializeEdge(it) }, variable.temp) + } + + else -> error("Unknown node: $it") + } + } + + body.nodes.forEachIndexed { index, node -> + val deserializedNode = nodes[index] + when (node.type) { + NodeType.STATIC_CALL -> + node.staticCall!!.call.arguments.forEachIndexed { i, arg -> + (deserializedNode as DataFlowIR.Node.StaticCall).arguments[i].node = nodes[arg.node] + } + + NodeType.NEW_OBJECT -> + node.newObject!!.call.arguments.forEachIndexed { i, arg -> + (deserializedNode as DataFlowIR.Node.NewObject).arguments[i].node = nodes[arg.node] + } + + NodeType.VTABLE_CALL -> + node.vtableCall!!.virtualCall.call.arguments.forEachIndexed { i, arg -> + (deserializedNode as DataFlowIR.Node.VtableCall).arguments[i].node = nodes[arg.node] + } + + NodeType.ITABLE_CALL -> + node.itableCall!!.virtualCall.call.arguments.forEachIndexed { i, arg -> + (deserializedNode as DataFlowIR.Node.ItableCall).arguments[i].node = nodes[arg.node] + } + + NodeType.FIELD_READ -> + node.fieldRead!!.receiver?.let { + (deserializedNode as DataFlowIR.Node.FieldRead).receiver!!.node = nodes[it.node] + } + + NodeType.FIELD_WRITE -> { + val deserializedFieldWrite = deserializedNode as DataFlowIR.Node.FieldWrite + val fieldWrite = node.fieldWrite!! + fieldWrite.receiver?.let { deserializedFieldWrite.receiver!!.node = nodes[it.node] } + deserializedFieldWrite.value.node = nodes[fieldWrite.value.node] + } + + NodeType.VARIABLE -> + node.variable!!.values.forEachIndexed { i, value -> + (deserializedNode as DataFlowIR.Node.Variable).values[i].node = nodes[value.node] + } + + else -> { } + } + } + return DataFlowIR.FunctionBody(nodes, nodes[body.returns] as DataFlowIR.Node.Variable) + } + + moduleDataFlowGraph.functions.forEach { + val symbol = functionSymbols[it.symbol] + functions.put(symbol, DataFlowIR.Function(symbol, it.isGlobalInitializer, it.numberOfParameters, deserializeBody(it.body))) + } + } + } + + return ExternalModulesDFG(allTypes, publicTypesMap, publicFunctionsMap, functions) + } +} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowIR.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowIR.kt index 584d1f095ac..cc170fb6791 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowIR.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowIR.kt @@ -31,20 +31,20 @@ internal object DataFlowIR { // Special marker type forbidding devirtualization on its instances. object Virtual : Declared(false, true) - class External(val name: String) : Type() { + class External(val hash: Long, val name: String? = null) : Type() { override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is External) return false - return name == other.name + return hash == other.hash } override fun hashCode(): Int { - return name.hashCode() + return hash.hashCode() } override fun toString(): String { - return "ExternalType(name='$name')" + return "ExternalType(hash='$hash', name='$name')" } } @@ -54,24 +54,24 @@ internal object DataFlowIR { val itable = mutableMapOf() } - class Public(val name: String, isFinal: Boolean, isAbstract: Boolean) : Declared(isFinal, isAbstract) { + class Public(val hash: Long, isFinal: Boolean, isAbstract: Boolean, val name: String? = null) : Declared(isFinal, isAbstract) { override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is Public) return false - return name == other.name + return hash == other.hash } override fun hashCode(): Int { - return name.hashCode() + return hash.hashCode() } override fun toString(): String { - return "PublicType(name='$name')" + return "PublicType(hash='$hash', name='$name')" } } - class Private(val name: String, val index: Int, isFinal: Boolean, isAbstract: Boolean) : Declared(isFinal, isAbstract) { + class Private(val index: Int, isFinal: Boolean, isAbstract: Boolean, val name: String? = null) : Declared(isFinal, isAbstract) { override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is Private) return false @@ -94,43 +94,43 @@ internal object DataFlowIR { } abstract class FunctionSymbol { - class External(val name: String) : FunctionSymbol() { + class External(val hash: Long, val name: String? = null) : FunctionSymbol() { override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is External) return false - return name == other.name + return hash == other.hash } override fun hashCode(): Int { - return name.hashCode() + return hash.hashCode() } override fun toString(): String { - return "ExternalFunction(name='$name')" + return "ExternalFunction(hash='$hash', name='$name')" } } abstract class Declared(val module: Module, val symbolTableIndex: Int) : FunctionSymbol() - class Public(val name: String, module: Module, symbolTableIndex: Int) : Declared(module, symbolTableIndex) { + class Public(val hash: Long, module: Module, symbolTableIndex: Int, val name: String? = null) : Declared(module, symbolTableIndex) { override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is Public) return false - return name == other.name + return hash == other.hash } override fun hashCode(): Int { - return name.hashCode() + return hash.hashCode() } override fun toString(): String { - return "PublicFunction(name='$name')" + return "PublicFunction(hash='$hash', name='$name')" } } - class Private(val name: String, val index: Int, module: Module, symbolTableIndex: Int) : Declared(module, symbolTableIndex) { + class Private(val index: Int, module: Module, symbolTableIndex: Int, val name: String? = null) : Declared(module, symbolTableIndex) { override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is Private) return false @@ -148,7 +148,7 @@ internal object DataFlowIR { } } - data class Field(val type: Type?, val name: String) + data class Field(val type: Type?, val hash: Long, val name: String? = null) class Edge(val castToType: Type?) { @@ -316,6 +316,11 @@ internal object DataFlowIR { } class SymbolTable(val context: Context, val irModule: IrModuleFragment, val module: Module) { + + private val TAKE_NAMES = false // Take fqNames for all functions and types (for debug purposes). + + private inline fun takeName(block: () -> String) = if (TAKE_NAMES) block() else null + val classMap = mutableMapOf() val functionMap = mutableMapOf() @@ -354,16 +359,16 @@ internal object DataFlowIR { val name = descriptor.fqNameSafe.asString() if (descriptor.module != irModule.descriptor) - return classMap.getOrPut(descriptor) { Type.External(name) } + return classMap.getOrPut(descriptor) { Type.External(name.localHash.value, takeName { name }) } classMap[descriptor]?.let { return it } val isFinal = descriptor.isFinal() val isAbstract = descriptor.isAbstract() val type = if (descriptor.isExported()) - Type.Public(name, isFinal, isAbstract) + Type.Public(name.localHash.value, isFinal, isAbstract, takeName { name }) else - Type.Private(name, privateTypeIndex++, isFinal, isAbstract) + Type.Private(privateTypeIndex++, isFinal, isAbstract, takeName { name }) if (!descriptor.isInterface) { val vtableBuilder = context.getVtableBuilder(descriptor) type.vtable += vtableBuilder.vtableEntries.map { mapFunction(it.getImplementation(context)) } @@ -405,11 +410,11 @@ internal object DataFlowIR { functionMap.getOrPut(it) { when (it) { is PropertyDescriptor -> - FunctionSymbol.Private("${it.symbolName}_init", privateFunIndex++, module, -1) + FunctionSymbol.Private(privateFunIndex++, module, -1, takeName { "${it.symbolName}_init" }) is FunctionDescriptor -> { if (it.module != irModule.descriptor || it.externalOrIntrinsic()) - FunctionSymbol.External(it.symbolName) + FunctionSymbol.External(it.symbolName.localHash.value, takeName { it.symbolName }) else { val isAbstract = it.modality == Modality.ABSTRACT val classDescriptor = it.containingDeclaration as? ClassDescriptor @@ -420,9 +425,9 @@ internal object DataFlowIR { ++module.numberOfFunctions val symbolTableIndex = if (!placeToFunctionsTable) -1 else couldBeCalledVirtuallyIndex++ if (it.isExported()) - FunctionSymbol.Public(it.symbolName, module, symbolTableIndex) + FunctionSymbol.Public(it.symbolName.localHash.value, module, symbolTableIndex, takeName { it.symbolName }) else - FunctionSymbol.Private(getFqName(it).asString() + "#internal", privateFunIndex++, module, symbolTableIndex) + FunctionSymbol.Private(privateFunIndex++, module, symbolTableIndex, takeName { getFqName(it).asString() + "#internal" }) } }