From c88ac7cc86589556bb26c7cfae1e00fe9cef4ebd Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Tue, 4 Oct 2022 20:08:44 +0300 Subject: [PATCH] [K/N][IR][codegen] Build per-file dependencies for caches --- .../library/metadata/KlibModuleOrigin.kt | 12 + .../jetbrains/kotlin/backend/konan/Boxing.kt | 2 + .../kotlin/backend/konan/CacheSupport.kt | 4 +- .../kotlin/backend/konan/CachedLibraries.kt | 167 +++++++++----- .../backend/konan/CheckExternalCalls.kt | 15 +- .../kotlin/backend/konan/CompilerOutput.kt | 2 +- .../jetbrains/kotlin/backend/konan/Linker.kt | 42 +++- .../backend/konan/NativeGenerationState.kt | 32 ++- .../kotlin/backend/konan/OutputFiles.kt | 2 + .../kotlin/backend/konan/ToplevelPhases.kt | 1 + .../konan/driver/phases/TopLevelPhases.kt | 34 ++- .../backend/konan/llvm/CodeGenerator.kt | 29 ++- .../kotlin/backend/konan/llvm/ContextUtils.kt | 207 +++++++++++++----- .../kotlin/backend/konan/llvm/Imports.kt | 7 +- .../backend/konan/llvm/IntrinsicGenerator.kt | 16 +- .../kotlin/backend/konan/llvm/IrToBitcode.kt | 39 ++-- .../llvm/KotlinObjCClassInfoGenerator.kt | 6 +- .../backend/konan/llvm/KotlinStaticData.kt | 5 +- .../konan/llvm/LlvmFunctionPrototype.kt | 9 +- .../kotlin/backend/konan/llvm/LlvmUtils.kt | 24 +- .../backend/konan/llvm/RTTIGenerator.kt | 5 +- .../konan/llvm/objc/ObjCCodeGenerator.kt | 44 ++-- .../konan/llvm/objc/ObjCDataGenerator.kt | 11 +- .../llvm/objcexport/BlockPointerSupport.kt | 21 +- .../objcexport/ObjCExportCodeGenerator.kt | 34 +-- .../backend/konan/lower/CacheInfoBuilder.kt | 60 +++-- .../backend/konan/lower/CachesAbiLowering.kt | 7 +- .../backend/konan/lower/InteropLowering.kt | 6 +- .../konan/serialization/KonanIrlinker.kt | 72 +++++- .../kotlin/kotlin/native/internal/Runtime.kt | 14 ++ 30 files changed, 620 insertions(+), 309 deletions(-) create mode 100644 kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Runtime.kt diff --git a/compiler/util-klib-metadata/src/org/jetbrains/kotlin/library/metadata/KlibModuleOrigin.kt b/compiler/util-klib-metadata/src/org/jetbrains/kotlin/library/metadata/KlibModuleOrigin.kt index fd36974aeed..77757e64c8c 100644 --- a/compiler/util-klib-metadata/src/org/jetbrains/kotlin/library/metadata/KlibModuleOrigin.kt +++ b/compiler/util-klib-metadata/src/org/jetbrains/kotlin/library/metadata/KlibModuleOrigin.kt @@ -35,3 +35,15 @@ val ModuleDescriptor.klibModuleOrigin get() = this.getCapability(KlibModuleOrigi val ModuleDescriptor.kotlinLibrary get() = (this.klibModuleOrigin as DeserializedKlibModuleOrigin) .library + +sealed class CompiledKlibFileOrigin { + object CurrentFile : CompiledKlibFileOrigin() // No dependency should be added. + + object StdlibRuntime : CompiledKlibFileOrigin() + + object StdlibKFunctionImpl : CompiledKlibFileOrigin() + + class EntireModule(val library: KotlinLibrary) : CompiledKlibFileOrigin() + + class CertainFile(val library: KotlinLibrary, val fqName: String, val filePath: String) : CompiledKlibFileOrigin() +} \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Boxing.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Boxing.kt index b154f44731a..1724ae59339 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Boxing.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Boxing.kt @@ -69,6 +69,7 @@ internal fun Context.getBoxFunction(inlinedClass: IrClass): IrSimpleFunction = m returnType = boxedType }.also { function -> function.parent = parent + function.attributeOwnerId = inlinedClass // To be able to get the file. function.addValueParameter { startOffset = inlinedClass.startOffset @@ -103,6 +104,7 @@ internal fun Context.getUnboxFunction(inlinedClass: IrClass): IrSimpleFunction = returnType = unboxedType }.also { function -> function.parent = parent + function.attributeOwnerId = inlinedClass // To be able to get the file. function.addValueParameter { startOffset = inlinedClass.startOffset diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CacheSupport.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CacheSupport.kt index 3bc9747ef14..a208bd1575e 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CacheSupport.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CacheSupport.kt @@ -125,7 +125,7 @@ class CacheSupport( val libraryToAddToCacheFile = File(it) val libraryToAddToCache = getLibrary(libraryToAddToCacheFile) val libraryCache = cachedLibraries.getLibraryCache(libraryToAddToCache) - if (libraryCache != null && libraryCache.granularity == CachedLibraries.Granularity.MODULE) + if (libraryCache is CachedLibraries.Cache.Monolithic) null else { val filesToCache = configuration.get(KonanConfigKeys.FILES_TO_CACHE) @@ -183,7 +183,7 @@ class CacheSupport( // Ensure not making cache for libraries that are already cached: libraryToCache?.klib?.let { val cache = cachedLibraries.getLibraryCache(it) - if (cache != null && cache.granularity == CachedLibraries.Granularity.MODULE) { + if (cache is CachedLibraries.Cache.Monolithic) { configuration.reportCompilationError("can't cache library '${it.libraryName}' " + "that is already cached in '${cache.path}'") } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CachedLibraries.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CachedLibraries.kt index d1bf14f98c2..736498eabb5 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CachedLibraries.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CachedLibraries.kt @@ -5,16 +5,18 @@ package org.jetbrains.kotlin.backend.konan +import org.jetbrains.kotlin.backend.konan.serialization.* import org.jetbrains.kotlin.backend.konan.serialization.ClassFieldsSerializer import org.jetbrains.kotlin.backend.konan.serialization.InlineFunctionBodyReferenceSerializer -import org.jetbrains.kotlin.backend.konan.serialization.SerializedClassFields -import org.jetbrains.kotlin.backend.konan.serialization.SerializedInlineFunctionReference import org.jetbrains.kotlin.konan.file.File import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.library.KotlinLibrary import org.jetbrains.kotlin.library.uniqueName +private fun getArtifactName(target: KonanTarget, baseName: String, kind: CompilerOutputKind) = + "${kind.prefix(target)}$baseName${kind.suffix(target)}" + class CachedLibraries( private val target: KonanTarget, allLibraries: List, @@ -22,68 +24,117 @@ class CachedLibraries( implicitCacheDirectories: List ) { enum class Kind { DYNAMIC, STATIC } - enum class Granularity { MODULE, FILE } - private fun Kind.toCompilerOutputKind(): CompilerOutputKind = when (this) { - Kind.DYNAMIC -> CompilerOutputKind.DYNAMIC_CACHE - Kind.STATIC -> CompilerOutputKind.STATIC_CACHE + sealed class BitcodeDependency(val libName: String) { + class WholeModule(libName: String) : BitcodeDependency(libName) + + class CertainFiles(libName: String, val files: List) : BitcodeDependency(libName) } - inner class Cache(val kind: Kind, val granularity: Granularity, val path: String) { - val fileDirs by lazy { File(path).listFiles.filter { it.isDirectory }.sortedBy { it.name } } + sealed class Cache(protected val target: KonanTarget, val kind: Kind, val path: String) { + val bitcodeDependencies by lazy { computeBitcodeDependencies() } + val binariesPaths by lazy { computeBinariesPaths() } + val serializedInlineFunctionBodies by lazy { computeSerializedInlineFunctionBodies() } + val serializedClassFields by lazy { computeSerializedClassFields() } + val serializedEagerInitializedFiles by lazy { computeSerializedEagerInitializedFiles() } - val bitcodeDependencies by lazy { - when (granularity) { - Granularity.MODULE -> File(path).parentFile.child(BITCODE_DEPENDENCIES_FILE_NAME).readStrings() - Granularity.FILE -> fileDirs.flatMap { - it.child(PER_FILE_CACHE_BINARY_LEVEL_DIR_NAME).child(BITCODE_DEPENDENCIES_FILE_NAME).readStrings() - }.distinct() + protected abstract fun computeBitcodeDependencies(): List + protected abstract fun computeBinariesPaths(): List + protected abstract fun computeSerializedInlineFunctionBodies(): List + protected abstract fun computeSerializedClassFields(): List + protected abstract fun computeSerializedEagerInitializedFiles(): List + + protected fun Kind.toCompilerOutputKind(): CompilerOutputKind = when (this) { + Kind.DYNAMIC -> CompilerOutputKind.DYNAMIC_CACHE + Kind.STATIC -> CompilerOutputKind.STATIC_CACHE + } + + protected fun parseDependencies(dependencies: List): List { + val wholeModuleDependencies = mutableListOf() + val fileDependencies = mutableMapOf>() + for (dependency in dependencies) { + val delimiterIndex = dependency.lastIndexOf(DEPENDENCIES_DELIMITER) + require(delimiterIndex >= 0) { "Invalid dependency $dependency of library $path" } + val libName = dependency.substring(0, delimiterIndex) + val file = dependency.substring(delimiterIndex + 1, dependency.length) + if (file.isEmpty()) + wholeModuleDependencies.add(libName) + else + fileDependencies.getOrPut(libName) { mutableListOf() }.add(file) + } + return wholeModuleDependencies.map { BitcodeDependency.WholeModule(it) } + + fileDependencies.map { (libName, files) -> BitcodeDependency.CertainFiles(libName, files) } + } + + class Monolithic(target: KonanTarget, kind: Kind, path: String) : Cache(target, kind, path) { + override fun computeBitcodeDependencies() = + parseDependencies(File(path).parentFile.child(BITCODE_DEPENDENCIES_FILE_NAME).readStrings()) + + override fun computeBinariesPaths() = listOf(path) + + override fun computeSerializedInlineFunctionBodies() = mutableListOf().also { + val directory = File(path).absoluteFile.parentFile.parentFile + val data = directory.child(PER_FILE_CACHE_IR_LEVEL_DIR_NAME).child(INLINE_FUNCTION_BODIES_FILE_NAME).readBytes() + InlineFunctionBodyReferenceSerializer.deserializeTo(data, it) + } + + override fun computeSerializedClassFields() = mutableListOf().also { + val directory = File(path).absoluteFile.parentFile.parentFile + val data = directory.child(PER_FILE_CACHE_IR_LEVEL_DIR_NAME).child(CLASS_FIELDS_FILE_NAME).readBytes() + ClassFieldsSerializer.deserializeTo(data, it) + } + + override fun computeSerializedEagerInitializedFiles() = mutableListOf().also { + val directory = File(path).absoluteFile.parentFile.parentFile + val data = directory.child(PER_FILE_CACHE_IR_LEVEL_DIR_NAME).child(EAGER_INITIALIZED_PROPERTIES_FILE_NAME).readBytes() + EagerInitializedPropertySerializer.deserializeTo(data, it) } } - val binariesPaths by lazy { - when (granularity) { - Granularity.MODULE -> listOf(path) - Granularity.FILE -> fileDirs.map { - it.child(PER_FILE_CACHE_BINARY_LEVEL_DIR_NAME).child(getArtifactName(it.name, kind.toCompilerOutputKind())).absolutePath + class PerFile(target: KonanTarget, kind: Kind, path: String) : Cache(target, kind, path) { + private val fileDirs by lazy { File(path).listFiles.filter { it.isDirectory }.sortedBy { it.name } } + + private val perFileBitcodeDependencies by lazy { + fileDirs.associate { + it.name to parseDependencies(it.child(PER_FILE_CACHE_BINARY_LEVEL_DIR_NAME).child(BITCODE_DEPENDENCIES_FILE_NAME).readStrings()) } } - } - val serializedInlineFunctionBodies by lazy { - val result = mutableListOf() - when (granularity) { - Granularity.MODULE -> { - val directory = File(path).absoluteFile.parentFile.parentFile - val data = directory.child(PER_FILE_CACHE_IR_LEVEL_DIR_NAME).child(INLINE_FUNCTION_BODIES_FILE_NAME).readBytes() - InlineFunctionBodyReferenceSerializer.deserializeTo(data, result) - } - Granularity.FILE -> { - fileDirs.forEach { fileDir -> - val data = fileDir.child(PER_FILE_CACHE_IR_LEVEL_DIR_NAME).child(INLINE_FUNCTION_BODIES_FILE_NAME).readBytes() - InlineFunctionBodyReferenceSerializer.deserializeTo(data, result) + fun getFileDependencies(file: String) = + perFileBitcodeDependencies[file] ?: error("File $file is not found in cache $path") + + fun getFileBinaryPath(file: String) = + File(path).child(file).child(PER_FILE_CACHE_BINARY_LEVEL_DIR_NAME).child(getArtifactName(target, file, kind.toCompilerOutputKind())).let { + require(it.exists) { "File $file is not found in cache $path" } + it.absolutePath } - } - } - result - } - val serializedClassFields by lazy { - val result = mutableListOf() - when (granularity) { - Granularity.MODULE -> { - val directory = File(path).absoluteFile.parentFile.parentFile - val data = directory.child(PER_FILE_CACHE_IR_LEVEL_DIR_NAME).child(CLASS_FIELDS_FILE_NAME).readBytes() - ClassFieldsSerializer.deserializeTo(data, result) - } - Granularity.FILE -> { - fileDirs.forEach { fileDir -> - val data = fileDir.child(PER_FILE_CACHE_IR_LEVEL_DIR_NAME).child(CLASS_FIELDS_FILE_NAME).readBytes() - ClassFieldsSerializer.deserializeTo(data, result) - } + override fun computeBitcodeDependencies() = perFileBitcodeDependencies.values.flatten() + + override fun computeBinariesPaths() = fileDirs.map { + it.child(PER_FILE_CACHE_BINARY_LEVEL_DIR_NAME).child(getArtifactName(target, it.name, kind.toCompilerOutputKind())).absolutePath + } + + override fun computeSerializedInlineFunctionBodies() = mutableListOf().also { + fileDirs.forEach { fileDir -> + val data = fileDir.child(PER_FILE_CACHE_IR_LEVEL_DIR_NAME).child(INLINE_FUNCTION_BODIES_FILE_NAME).readBytes() + InlineFunctionBodyReferenceSerializer.deserializeTo(data, it) + } + } + + override fun computeSerializedClassFields() = mutableListOf().also { + fileDirs.forEach { fileDir -> + val data = fileDir.child(PER_FILE_CACHE_IR_LEVEL_DIR_NAME).child(CLASS_FIELDS_FILE_NAME).readBytes() + ClassFieldsSerializer.deserializeTo(data, it) + } + } + + override fun computeSerializedEagerInitializedFiles() = mutableListOf().also { + fileDirs.forEach { fileDir -> + val data = fileDir.child(PER_FILE_CACHE_IR_LEVEL_DIR_NAME).child(EAGER_INITIALIZED_PROPERTIES_FILE_NAME).readBytes() + EagerInitializedPropertySerializer.deserializeTo(data, it) } } - result } } @@ -100,16 +151,16 @@ class CachedLibraries( cacheBinaryPartDir.listFilesOrEmpty.map { it.absolutePath }.toSet() } val baseName = getCachedLibraryName(library) - val dynamicFile = cacheBinaryPartDir.child(getArtifactName(baseName, CompilerOutputKind.DYNAMIC_CACHE)) - val staticFile = cacheBinaryPartDir.child(getArtifactName(baseName, CompilerOutputKind.STATIC_CACHE)) + val dynamicFile = cacheBinaryPartDir.child(getArtifactName(target, baseName, CompilerOutputKind.DYNAMIC_CACHE)) + val staticFile = cacheBinaryPartDir.child(getArtifactName(target, baseName, CompilerOutputKind.STATIC_CACHE)) if (dynamicFile.absolutePath in cacheBinaryPartDirContents && staticFile.absolutePath in cacheBinaryPartDirContents) error("Both dynamic and static caches files cannot be in the same directory." + " Library: ${library.libraryName}, path to cache: ${cacheDir.absolutePath}") return when { - dynamicFile.absolutePath in cacheBinaryPartDirContents -> Cache(Kind.DYNAMIC, Granularity.MODULE, dynamicFile.absolutePath) - staticFile.absolutePath in cacheBinaryPartDirContents -> Cache(Kind.STATIC, Granularity.MODULE, staticFile.absolutePath) - else -> Cache(Kind.STATIC, Granularity.FILE, cacheDir.absolutePath) + dynamicFile.absolutePath in cacheBinaryPartDirContents -> Cache.Monolithic(target, Kind.DYNAMIC, dynamicFile.absolutePath) + staticFile.absolutePath in cacheBinaryPartDirContents -> Cache.Monolithic(target, Kind.STATIC, staticFile.absolutePath) + else -> Cache.PerFile(target, Kind.STATIC, cacheDir.absolutePath) } } @@ -129,9 +180,6 @@ class CachedLibraries( cache?.let { library to it } }.toMap() - private fun getArtifactName(baseName: String, kind: CompilerOutputKind) = - "${kind.prefix(target)}$baseName${kind.suffix(target)}" - fun isLibraryCached(library: KotlinLibrary): Boolean = getLibraryCache(library) != null @@ -163,5 +211,8 @@ class CachedLibraries( const val BITCODE_DEPENDENCIES_FILE_NAME = "bitcode_deps" const val INLINE_FUNCTION_BODIES_FILE_NAME = "inline_bodies" const val CLASS_FIELDS_FILE_NAME = "class_fields" + const val EAGER_INITIALIZED_PROPERTIES_FILE_NAME = "eager_init" + + const val DEPENDENCIES_DELIMITER = '|' } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CheckExternalCalls.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CheckExternalCalls.kt index fe14dd1d99c..5f84e99566b 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CheckExternalCalls.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CheckExternalCalls.kt @@ -37,25 +37,22 @@ private class CallsChecker(generationState: NativeGenerationState, goodFunctions private fun moduleFunction(name: String) = LLVMGetNamedFunction(llvm.module, name) ?: throw IllegalStateException("$name function is not available") - val getMethodImpl = llvm.externalFunction(LlvmFunctionProto( + val getMethodImpl = llvm.externalNativeRuntimeFunction( "class_getMethodImplementation", LlvmRetType(pointerType(functionType(llvm.voidType, false))), - listOf(LlvmParamType(llvm.int8PtrType), LlvmParamType(llvm.int8PtrType)), - origin = context.stdlibModule.llvmSymbolOrigin) + listOf(LlvmParamType(llvm.int8PtrType), LlvmParamType(llvm.int8PtrType)) ) - val getClass = llvm.externalFunction(LlvmFunctionProto( + val getClass = llvm.externalNativeRuntimeFunction( "object_getClass", LlvmRetType(llvm.int8PtrType), - listOf(LlvmParamType(llvm.int8PtrType)), - origin = context.stdlibModule.llvmSymbolOrigin) + listOf(LlvmParamType(llvm.int8PtrType)) ) - val getSuperClass = llvm.externalFunction(LlvmFunctionProto( + val getSuperClass = llvm.externalNativeRuntimeFunction( "class_getSuperclass", LlvmRetType(llvm.int8PtrType), - listOf(LlvmParamType(llvm.int8PtrType)), - origin = context.stdlibModule.llvmSymbolOrigin) + listOf(LlvmParamType(llvm.int8PtrType)) ) val checkerFunction = moduleFunction("Kotlin_mm_checkStateAtExternalFunctionCall") diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt index 29e22e48d16..a02f87ce7c4 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt @@ -60,7 +60,7 @@ internal val NativeGenerationState.shouldDefineCachedBoxes: Boolean internal val NativeGenerationState.shouldLinkRuntimeNativeLibraries: Boolean get() = producedLlvmModuleContainsStdlib && - cacheDeserializationStrategy?.contains(KonanFqNames.packageName, "Runtime.kt") != false + cacheDeserializationStrategy?.contains(KonanFqNames.internalPackageName, "Runtime.kt") != false val KonanConfig.involvesLinkStage: Boolean get() = when (this.produce) { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Linker.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Linker.kt index ac33d555180..75e9943e6bf 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Linker.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Linker.kt @@ -1,7 +1,9 @@ package org.jetbrains.kotlin.backend.konan import org.jetbrains.kotlin.backend.konan.driver.PhaseContext +import org.jetbrains.kotlin.backend.konan.llvm.Llvm import org.jetbrains.kotlin.backend.konan.serialization.ClassFieldsSerializer +import org.jetbrains.kotlin.backend.konan.serialization.EagerInitializedPropertySerializer import org.jetbrains.kotlin.backend.konan.serialization.InlineFunctionBodyReferenceSerializer import org.jetbrains.kotlin.konan.KonanExternalToolFailure import org.jetbrains.kotlin.konan.exec.Command @@ -55,17 +57,33 @@ internal class CacheStorage(val generationState: NativeGenerationState) { saveCacheBitcodeDependencies() saveInlineFunctionBodies() saveClassFields() + saveEagerInitializedProperties() } private fun saveCacheBitcodeDependencies() { - val bitcodeDependencies = config.resolvedLibraries + val libraryToCache = config.cacheSupport.libraryToCache!! + val usedBitcode = generationState.llvmImports.usedBitcode().groupBy { it.library } + val bitcodeModuleDependencies = mutableListOf() + val bitcodeFileDependencies = mutableListOf() + val strategy = libraryToCache.strategy as? CacheDeserializationStrategy.SingleFile + config.resolvedLibraries .getFullList(TopologicalLibraryOrder) - .map { it as KonanLibrary } - .filter { - generationState.llvmImports.bitcodeIsUsed(it) - && it != config.cacheSupport.libraryToCache?.klib // Skip loops. + .forEach { library -> + require(library is KonanLibrary) + val filesUsed = usedBitcode[library] + if (filesUsed == null && generationState.llvmImports.bitcodeIsUsed(library) + && library != libraryToCache.klib /* Skip loops */) { + // Dependency on the entire library. + bitcodeModuleDependencies.add("${library.uniqueName}${CachedLibraries.DEPENDENCIES_DELIMITER}") + } + filesUsed?.forEach { libraryFile -> + if (library != libraryToCache.klib || strategy?.filePath != libraryFile.filePath /* Skip loops */) { + val fileId = CacheSupport.cacheFileId(libraryFile.fqName, libraryFile.filePath) + bitcodeFileDependencies.add("${library.uniqueName}${CachedLibraries.DEPENDENCIES_DELIMITER}$fileId") + } + } } - outputFiles.bitcodeDependenciesFile!!.writeLines(bitcodeDependencies.map { it.uniqueName }) + outputFiles.bitcodeDependenciesFile!!.writeLines(bitcodeModuleDependencies + bitcodeFileDependencies) } private fun saveInlineFunctionBodies() { @@ -77,6 +95,11 @@ internal class CacheStorage(val generationState: NativeGenerationState) { outputFiles.classFieldsFile!!.writeBytes( ClassFieldsSerializer.serialize(generationState.classFields)) } + + private fun saveEagerInitializedProperties() { + outputFiles.eagerInitializedPropertiesFile!!.writeBytes( + EagerInitializedPropertySerializer.serialize(generationState.eagerInitializedFiles)) + } } // TODO: We have a Linker.kt file in the shared module. @@ -239,7 +262,8 @@ private fun determineCachesToLink(generationState: NativeGenerationState): Cache val staticCaches = mutableListOf() val dynamicCaches = mutableListOf() - generationState.llvm.allCachedBitcodeDependencies.forEach { library -> + generationState.llvm.allCachedBitcodeDependencies.forEach { dependency -> + val library = dependency.library val currentBinaryContainsLibrary = generationState.llvmModuleSpecification.containsLibrary(library) val cache = generationState.config.cachedLibraries.getLibraryCache(library) ?: error("Library $library is expected to be cached") @@ -253,7 +277,9 @@ private fun determineCachesToLink(generationState: NativeGenerationState): Cache CachedLibraries.Kind.STATIC -> staticCaches } - list += cache.binariesPaths + list += if (dependency is Llvm.CachedBitcodeDependency.CertainFiles && cache is CachedLibraries.Cache.PerFile) + dependency.files.map { cache.getFileBinaryPath(it) } + else cache.binariesPaths } return CachesToLink(static = staticCaches, dynamic = dynamicCaches) } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/NativeGenerationState.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/NativeGenerationState.kt index d69102cfa28..9f5d740b4ee 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/NativeGenerationState.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/NativeGenerationState.kt @@ -6,18 +6,22 @@ package org.jetbrains.kotlin.backend.konan import llvm.* +import org.jetbrains.kotlin.backend.konan.descriptors.isFromInteropLibrary import org.jetbrains.kotlin.backend.konan.driver.BasicPhaseContext +import org.jetbrains.kotlin.backend.konan.ir.llvmSymbolOrigin import org.jetbrains.kotlin.backend.konan.llvm.* import org.jetbrains.kotlin.backend.konan.llvm.coverage.CoverageManager import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport import org.jetbrains.kotlin.backend.konan.serialization.SerializedClassFields +import org.jetbrains.kotlin.backend.konan.serialization.SerializedEagerInitializedFile import org.jetbrains.kotlin.backend.konan.serialization.SerializedInlineFunctionReference -import org.jetbrains.kotlin.ir.declarations.IrAttributeContainer -import org.jetbrains.kotlin.ir.declarations.IrClass -import org.jetbrains.kotlin.ir.declarations.IrFile -import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.util.getPackageFragment import org.jetbrains.kotlin.konan.TempFiles import org.jetbrains.kotlin.konan.file.File +import org.jetbrains.kotlin.library.metadata.CompiledKlibFileOrigin +import org.jetbrains.kotlin.library.metadata.CurrentKlibModuleOrigin +import org.jetbrains.kotlin.library.metadata.DeserializedKlibModuleOrigin internal class InlineFunctionOriginInfo(val irFunction: IrFunction, val irFile: IrFile, val startOffset: Int, val endOffset: Int) @@ -61,6 +65,7 @@ internal class NativeGenerationState( val inlineFunctionBodies = mutableListOf() val classFields = mutableListOf() + val eagerInitializedFiles = mutableListOf() val calledFromExportedInlineFunctions = mutableSetOf() val constructedFromExportedInlineFunctions = mutableSetOf() val loweredInlineFunctions = mutableMapOf() @@ -105,6 +110,25 @@ internal class NativeGenerationState( lateinit var objCExport: ObjCExport + fun computeOrigin(declaration: IrDeclaration): CompiledKlibFileOrigin { + val packageFragment = declaration.getPackageFragment() + return if (packageFragment.isFunctionInterfaceFile) + CompiledKlibFileOrigin.StdlibKFunctionImpl + else { + val library = when (val origin = declaration.llvmSymbolOrigin) { + CurrentKlibModuleOrigin -> config.libraryToCache?.klib?.takeIf { config.producePerFileCache } + else -> (origin as DeserializedKlibModuleOrigin).library + } + when { + library == null -> CompiledKlibFileOrigin.CurrentFile + packageFragment.packageFragmentDescriptor.containingDeclaration.isFromInteropLibrary() -> + CompiledKlibFileOrigin.EntireModule(library) + else -> CompiledKlibFileOrigin.CertainFile(library, packageFragment.fqName.asString(), + context.irLinker.getExternalDeclarationFileName(declaration)) + } + } + } + fun hasDebugInfo() = debugInfoDelegate.isInitialized() fun verifyBitCode() { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/OutputFiles.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/OutputFiles.kt index 1706f0b2e3a..0ebd63565ac 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/OutputFiles.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/OutputFiles.kt @@ -73,6 +73,8 @@ class OutputFiles(val outputName: String, target: KonanTarget, val produce: Comp val classFieldsFile = tempCacheDirectory?.cacheIrPart()?.child(CachedLibraries.CLASS_FIELDS_FILE_NAME) + val eagerInitializedPropertiesFile = tempCacheDirectory?.cacheIrPart()?.child(CachedLibraries.EAGER_INITIALIZED_PROPERTIES_FILE_NAME) + private fun String.fullOutputName() = prefixBaseNameIfNeeded(prefix).suffixIfNeeded(suffix) private fun String.prefixBaseNameIfNeeded(prefix: String) = diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt index 11deaa20d8c..5279c263bca 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt @@ -17,6 +17,7 @@ import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.konan.target.CompilerOutputKind +import org.jetbrains.kotlin.library.metadata.DeserializedKlibModuleOrigin import org.jetbrains.kotlin.name.FqName internal fun moduleValidationCallback(state: ActionState, module: IrModuleFragment, context: Context) { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/TopLevelPhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/TopLevelPhases.kt index 6b56b3ee7ff..3686a57d571 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/TopLevelPhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/TopLevelPhases.kt @@ -11,11 +11,17 @@ import org.jetbrains.kotlin.backend.konan.driver.PhaseEngine import org.jetbrains.kotlin.backend.konan.driver.runPhaseInParentContext import org.jetbrains.kotlin.backend.konan.llvm.linkBitcodeDependenciesPhase import org.jetbrains.kotlin.backend.konan.llvm.printBitcodePhase +import org.jetbrains.kotlin.backend.konan.llvm.standardLlvmSymbolsOrigin import org.jetbrains.kotlin.backend.konan.llvm.verifyBitcodePhase import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrDeclaration import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.declarations.path +import org.jetbrains.kotlin.ir.util.hasAnnotation import org.jetbrains.kotlin.konan.target.CompilerOutputKind +import org.jetbrains.kotlin.library.metadata.CompiledKlibFileOrigin +import org.jetbrains.kotlin.library.metadata.DeserializedKlibModuleOrigin internal fun PhaseEngine.runFrontend(config: KonanConfig, environment: KotlinCoreEnvironment): FrontendPhaseOutput.Full? { val frontendOutput = useContext(FrontendContextImpl(config)) { it.runFrontend(environment) } @@ -57,6 +63,14 @@ internal fun PhaseEngine.runBackend(backendContext: Contex } } +private fun isReferencedByNativeRuntime(declarations: List): Boolean = + declarations.any { + it.hasAnnotation(RuntimeNames.exportTypeInfoAnnotation) + || it.hasAnnotation(RuntimeNames.exportForCppRuntime) + } || declarations.any { + it is IrClass && isReferencedByNativeRuntime(it.declarations) + } + internal fun PhaseEngine.processModuleFragments( input: IrModuleFragment, action: (NativeGenerationState, IrModuleFragment) -> Unit @@ -66,21 +80,35 @@ internal fun PhaseEngine.processModuleFragments( val files = module.files.toList() module.files.clear() - val functionInterfaceFiles = files.filter { it.isFunctionInterfaceFile } + + val stdlibIsBeingCached = module.descriptor == context.stdlibModule + val functionInterfaceFiles = files.takeIf { stdlibIsBeingCached } + ?.filter { it.isFunctionInterfaceFile }.orEmpty() + val filesReferencedByNativeRuntime = files.takeIf { stdlibIsBeingCached } + ?.filter { isReferencedByNativeRuntime(it.declarations) }.orEmpty() for (file in files) { if (file.isFunctionInterfaceFile) continue - context.generationState = NativeGenerationState( + val generationState = NativeGenerationState( context.config, context, CacheDeserializationStrategy.SingleFile(file.path, file.fqName.asString()) ) + context.generationState = generationState module.files += file - if (context.generationState.shouldDefineFunctionClasses) + if (generationState.shouldDefineFunctionClasses) module.files += functionInterfaceFiles + if (generationState.shouldLinkRuntimeNativeLibraries) { + val stdlib = (context.standardLlvmSymbolsOrigin as DeserializedKlibModuleOrigin).library + filesReferencedByNativeRuntime.forEach { + generationState.llvmImports.add( + CompiledKlibFileOrigin.CertainFile(stdlib, it.fqName.asString(), it.path)) + } + } + action(context.generationState, input) module.files.clear() diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt index d5010d3128f..e686a3437f5 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt @@ -17,11 +17,11 @@ import org.jetbrains.kotlin.backend.konan.llvm.ThreadState.Runnable import org.jetbrains.kotlin.backend.konan.llvm.objc.* import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.library.metadata.CompiledKlibFileOrigin import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.konan.ForeignExceptionMode import org.jetbrains.kotlin.konan.target.CompilerOutputKind -import org.jetbrains.kotlin.library.metadata.CompiledKlibModuleOrigin internal class CodeGenerator(override val generationState: NativeGenerationState) : ContextUtils { @@ -1265,21 +1265,20 @@ internal abstract class FunctionGenerationContext( assert(!irClass.isInterface) return if (irClass.isExternalObjCClass()) { - val llvmSymbolOrigin = irClass.llvmSymbolOrigin + val origin = generationState.computeOrigin(irClass) if (irClass.isObjCMetaClass()) { val name = irClass.descriptor.getExternalObjCMetaClassBinaryName() - val objCClass = getObjCClass(name, llvmSymbolOrigin) + val objCClass = getObjCClass(name, origin) - val getClass = llvm.externalFunction(LlvmFunctionProto( + val getClass = llvm.externalNativeRuntimeFunction( "object_getClass", LlvmRetType(llvm.int8PtrType), - listOf(LlvmParamType(llvm.int8PtrType)), - origin = context.standardLlvmSymbolsOrigin - )) + listOf(LlvmParamType(llvm.int8PtrType)) + ) call(getClass, listOf(objCClass), exceptionHandler = exceptionHandler) } else { - getObjCClass(irClass.descriptor.getExternalObjCClassBinaryName(), llvmSymbolOrigin) + getObjCClass(irClass.descriptor.getExternalObjCClassBinaryName(), origin) } } else { if (irClass.isObjCMetaClass()) { @@ -1303,8 +1302,8 @@ internal abstract class FunctionGenerationContext( } } - fun getObjCClass(binaryName: String, llvmSymbolOrigin: CompiledKlibModuleOrigin): LLVMValueRef { - llvm.imports.add(llvmSymbolOrigin) + fun getObjCClass(binaryName: String, origin: CompiledKlibFileOrigin): LLVMValueRef { + llvm.imports.add(origin) return load(codegen.objCDataGenerator!!.genClassRef(binaryName).llvm) } @@ -1467,17 +1466,15 @@ internal abstract class FunctionGenerationContext( } private val kotlinExceptionRtti: ConstPointer - get() = constPointer(importGlobal( + get() = constPointer(importNativeRuntimeGlobal( "_ZTI18ExceptionObjHolder", // typeinfo for ObjHolder - llvm.int8PtrType, - origin = context.stdlibModule.llvmSymbolOrigin + llvm.int8PtrType )).bitcast(llvm.int8PtrType) private val objcNSExceptionRtti: ConstPointer by lazy { - constPointer(importGlobal( + constPointer(importNativeRuntimeGlobal( "OBJC_EHTYPE_\$_NSException", // typeinfo for NSException* - llvm.int8PtrType, - origin = context.stdlibModule.llvmSymbolOrigin + llvm.int8PtrType )).bitcast(llvm.int8PtrType) } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt index ff17acb6c5d..e2b9b24bdb0 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt @@ -8,18 +8,24 @@ package org.jetbrains.kotlin.backend.konan.llvm import kotlinx.cinterop.toCValues import kotlinx.cinterop.toKString import llvm.* +import org.jetbrains.kotlin.backend.common.atMostOne import org.jetbrains.kotlin.backend.konan.* +import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.descriptors.isInteropLibrary +import org.jetbrains.kotlin.library.metadata.resolver.TopologicalLibraryOrder import org.jetbrains.kotlin.backend.konan.ir.llvmSymbolOrigin +import org.jetbrains.kotlin.descriptors.konan.* import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.konan.library.KonanLibrary import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.library.KotlinLibrary import org.jetbrains.kotlin.library.metadata.CompiledKlibModuleOrigin +import org.jetbrains.kotlin.library.metadata.CompiledKlibFileOrigin import org.jetbrains.kotlin.library.metadata.CurrentKlibModuleOrigin import org.jetbrains.kotlin.library.metadata.DeserializedKlibModuleOrigin -import org.jetbrains.kotlin.library.metadata.resolver.TopologicalLibraryOrder import org.jetbrains.kotlin.library.uniqueName +import org.jetbrains.kotlin.name.FqName import kotlin.properties.ReadOnlyProperty import kotlin.reflect.KProperty @@ -215,8 +221,7 @@ internal interface ContextUtils : RuntimeAware { this.computePrivateTypeInfoSymbolName(context.irLinker.getExternalDeclarationFileName(this)) } - constPointer(importGlobal(typeInfoSymbolName, runtime.typeInfoType, - origin = this.llvmSymbolOrigin)) + constPointer(importGlobal(typeInfoSymbolName, runtime.typeInfoType, generationState.computeOrigin(this))) } else { generationState.llvmDeclarations.forClass(this).typeInfo } @@ -359,6 +364,21 @@ internal class Llvm(private val generationState: NativeGenerationState, val modu } } + internal fun externalNativeRuntimeFunction( + name: String, + returnType: LlvmRetType, + parameterTypes: List = emptyList(), + functionAttributes: List = emptyList(), + isVararg: Boolean = false + ) = externalFunction( + LlvmFunctionProto(name, returnType, parameterTypes, functionAttributes, + origin = CompiledKlibFileOrigin.StdlibRuntime, + isVararg, independent = false) + ) + + internal fun externalNativeRuntimeFunction(name: String, signature: LlvmFunctionSignature) = + externalNativeRuntimeFunction(name, signature.returnType, signature.parameterTypes, signature.functionAttributes, signature.isVararg) + val imports get() = generationState.llvmImports class ImportsImpl(private val context: Context) : LlvmImports { @@ -366,27 +386,48 @@ internal class Llvm(private val generationState: NativeGenerationState, val modu private val usedBitcode = mutableSetOf() private val usedNativeDependencies = mutableSetOf() + private val usedBitcodeOfFile = mutableSetOf() + private val allLibraries by lazy { context.librariesWithDependencies.toSet() } - override fun add(origin: CompiledKlibModuleOrigin, onlyBitcode: Boolean) { - val library = when (origin) { - CurrentKlibModuleOrigin -> return - is DeserializedKlibModuleOrigin -> origin.library - } + private fun findStdlibFile(fqName: FqName, fileName: String): LlvmImports.LibraryFile { + val stdlib = (context.standardLlvmSymbolsOrigin as? DeserializedKlibModuleOrigin)?.library + ?: error("Can't find stdlib") + val stdlibDeserializer = context.irLinker.moduleDeserializers[context.stdlibModule] + ?: error("No deserializer for stdlib") + val file = stdlibDeserializer.files.atMostOne { it.fqName == fqName && it.name == fileName } + ?: error("Can't find $fqName:$fileName in stdlib") + return LlvmImports.LibraryFile(stdlib, file.fqName.asString(), file.path) + } - if (library !in allLibraries) { - error("Library (${library.libraryName}) is used but not requested.\nRequested libraries: ${allLibraries.joinToString { it.libraryName }}") + private val stdlibRuntime by lazy { findStdlibFile(KonanFqNames.internalPackageName, "Runtime.kt") } + private val stdlibKFunctionImpl by lazy { findStdlibFile(KonanFqNames.internalPackageName, "KFunctionImpl.kt") } + + override fun add(origin: CompiledKlibFileOrigin, onlyBitcode: Boolean) { + val libraryFile = when (origin) { + CompiledKlibFileOrigin.CurrentFile -> return + is CompiledKlibFileOrigin.EntireModule -> null + is CompiledKlibFileOrigin.CertainFile -> LlvmImports.LibraryFile(origin.library, origin.fqName, origin.filePath) + CompiledKlibFileOrigin.StdlibRuntime -> stdlibRuntime + CompiledKlibFileOrigin.StdlibKFunctionImpl -> stdlibKFunctionImpl } + val library = libraryFile?.library ?: (origin as CompiledKlibFileOrigin.EntireModule).library + if (library !in allLibraries) + error("Library (${library.libraryName}) is used but not requested.\nRequested libraries: ${allLibraries.joinToString { it.libraryName }}") usedBitcode.add(library) if (!onlyBitcode) { usedNativeDependencies.add(library) } + + libraryFile?.let { usedBitcodeOfFile.add(it) } } override fun bitcodeIsUsed(library: KonanLibrary) = library in usedBitcode override fun nativeDependenciesAreUsed(library: KonanLibrary) = library in usedNativeDependencies + + override fun usedBitcode(): List = usedBitcodeOfFile.toList() } val nativeDependenciesToLink: List by lazy { @@ -403,47 +444,117 @@ internal class Llvm(private val generationState: NativeGenerationState, val modu .filter { (!it.isDefault && !context.config.purgeUserLibs) || imports.bitcodeIsUsed(it) } } - val allCachedBitcodeDependencies: List by lazy { - val allLibraries = context.config.resolvedLibraries.getFullList().associateBy { it.uniqueName } - val result = mutableSetOf() + sealed class CachedBitcodeDependency(val library: KonanLibrary) { + class WholeModule(library: KonanLibrary) : CachedBitcodeDependency(library) - fun addDependencies(cachedLibrary: CachedLibraries.Cache) { - cachedLibrary.bitcodeDependencies.forEach { - val library = allLibraries[it] ?: error("Bitcode dependency to an unknown library: $it") - result.add(library as KonanLibrary) - addDependencies(context.config.cachedLibraries.getLibraryCache(library) - ?: error("Library $it is expected to be cached")) + class CertainFiles(library: KonanLibrary, val files: List) : CachedBitcodeDependency(library) + } + + private inner class CachedBitcodeDependenciesComputer { + private val allLibraries = context.config.resolvedLibraries.getFullList().associateBy { it.uniqueName } + private val usedBitcode = imports.usedBitcode().groupBy { it.library } + + private val moduleDependencies = mutableSetOf() + private val fileDependencies = mutableMapOf>() + + val allDependencies: List + + init { + val moduleDeserializers = context.irLinker.moduleDeserializers.values.associateBy { it.klib } + for (library in immediateBitcodeDependencies) { + if (library == context.config.libraryToCache?.klib) continue + val cache = context.config.cachedLibraries.getLibraryCache(library) + + if (cache != null) { + val filesUsed = buildList { + usedBitcode[library]?.forEach { + add(CacheSupport.cacheFileId(it.fqName, it.filePath)) + } + val moduleDeserializer = moduleDeserializers[library] + if (moduleDeserializer == null) { + require(library.isInteropLibrary()) { "No module deserializer for cached library ${library.uniqueName}" } + } else { + moduleDeserializer.eagerInitializedFiles.forEach { + add(CacheSupport.cacheFileId(it.fqName.asString(), it.path)) + } + } + } + + if (filesUsed.isEmpty()) { + // This is the case when we depend on the whole module rather than on a number of files. + moduleDependencies.add(library) + addAllDependencies(cache) + } else { + fileDependencies.getOrPut(library) { mutableSetOf() }.addAll(filesUsed) + addDependencies(cache, filesUsed) + } + } } + + allDependencies = moduleDependencies.map { CachedBitcodeDependency.WholeModule(it) } + + fileDependencies.filterNot { it.key in moduleDependencies } + .map { (library, files) -> CachedBitcodeDependency.CertainFiles(library, files.toList()) } } - for (library in immediateBitcodeDependencies) { - if (library == context.config.libraryToCache?.klib) continue - val cache = context.config.cachedLibraries.getLibraryCache(library) - if (cache != null) { - result += library - addDependencies(cache) - } + private fun addAllDependencies(cachedLibrary: CachedLibraries.Cache) { + cachedLibrary.bitcodeDependencies.forEach { addDependency(it) } } - result.toList() + private fun addDependencies(cachedLibrary: CachedLibraries.Cache, files: List) = when (cachedLibrary) { + is CachedLibraries.Cache.Monolithic -> addAllDependencies(cachedLibrary) + + is CachedLibraries.Cache.PerFile -> + files.forEach { file -> + cachedLibrary.getFileDependencies(file).forEach { addDependency(it) } + } + } + + private fun addDependency(dependency: CachedLibraries.BitcodeDependency) { + val dependencyLib = (allLibraries[dependency.libName] + ?: error("Bitcode dependency to an unknown library: ${dependency.libName}")) as KonanLibrary + if (dependencyLib in moduleDependencies) return + val cachedDependency = context.config.cachedLibraries.getLibraryCache(dependencyLib) + ?: error("Library ${dependency.libName} is expected to be cached") + + when (dependency) { + is CachedLibraries.BitcodeDependency.WholeModule -> { + moduleDependencies.add(dependencyLib) + addAllDependencies(cachedDependency) + } + is CachedLibraries.BitcodeDependency.CertainFiles -> { + val handledFiles = fileDependencies.getOrPut(dependencyLib) { mutableSetOf() } + val notHandledFiles = dependency.files.toMutableSet() + notHandledFiles.removeAll(handledFiles) + handledFiles.addAll(notHandledFiles) + if (notHandledFiles.isNotEmpty()) + addDependencies(cachedDependency, notHandledFiles.toList()) + } + } + } + } + + val allCachedBitcodeDependencies: List by lazy { + CachedBitcodeDependenciesComputer().allDependencies } val allNativeDependencies: List by lazy { - (nativeDependenciesToLink + allCachedBitcodeDependencies).distinct() + (nativeDependenciesToLink + allCachedBitcodeDependencies.map { it.library } /* Native dependencies are per library */).distinct() } - val allBitcodeDependencies: List by lazy { - val allNonCachedDependencies = context.librariesWithDependencies.filter { - context.config.cachedLibraries.getLibraryCache(it) == null || it == context.config.libraryToCache?.klib + val allBitcodeDependencies: List by lazy { + val allBitcodeDependencies = mutableMapOf() + for (library in context.librariesWithDependencies) { + if (context.config.cachedLibraries.getLibraryCache(library) == null || library == context.config.libraryToCache?.klib) + allBitcodeDependencies[library] = CachedBitcodeDependency.WholeModule(library) } - val set = (allNonCachedDependencies + allCachedBitcodeDependencies).toSet() + for (dependency in allCachedBitcodeDependencies) + allBitcodeDependencies[dependency.library] = dependency // This list is used in particular to build the libraries' initializers chain. // The initializers must be called in the topological order, so make sure that the // libraries list being returned is also toposorted. context.config.resolvedLibraries .getFullList(TopologicalLibraryOrder) - .map { it as KonanLibrary } - .filter { it in set } + .mapNotNull { allBitcodeDependencies[it as KonanLibrary] } } val bitcodeToLink: List by lazy { @@ -663,35 +774,31 @@ internal class Llvm(private val generationState: NativeGenerationState, val modu else -> "__gxx_personality_v0" } - val cxxStdTerminate = externalFunction(LlvmFunctionProto( + val cxxStdTerminate = externalNativeRuntimeFunction( "_ZSt9terminatev", // mangled C++ 'std::terminate' returnType = LlvmRetType(voidType), - functionAttributes = listOf(LlvmFunctionAttribute.NoUnwind), - origin = context.standardLlvmSymbolsOrigin - )) + functionAttributes = listOf(LlvmFunctionAttribute.NoUnwind) + ) - val gxxPersonalityFunction = externalFunction(LlvmFunctionProto( + val gxxPersonalityFunction = externalNativeRuntimeFunction( personalityFunctionName, returnType = LlvmRetType(int32Type), functionAttributes = listOf(LlvmFunctionAttribute.NoUnwind), - isVararg = true, - origin = context.standardLlvmSymbolsOrigin - )) + isVararg = true + ) - val cxaBeginCatchFunction = externalFunction(LlvmFunctionProto( + val cxaBeginCatchFunction = externalNativeRuntimeFunction( "__cxa_begin_catch", returnType = LlvmRetType(int8PtrType), functionAttributes = listOf(LlvmFunctionAttribute.NoUnwind), - parameterTypes = listOf(LlvmParamType(int8PtrType)), - origin = context.standardLlvmSymbolsOrigin - )) + parameterTypes = listOf(LlvmParamType(int8PtrType)) + ) - val cxaEndCatchFunction = externalFunction(LlvmFunctionProto( + val cxaEndCatchFunction = externalNativeRuntimeFunction( "__cxa_end_catch", returnType = LlvmRetType(voidType), - functionAttributes = listOf(LlvmFunctionAttribute.NoUnwind), - origin = context.standardLlvmSymbolsOrigin - )) + functionAttributes = listOf(LlvmFunctionAttribute.NoUnwind) + ) private fun getSizeOfReturnTypeInBits(functionPointer: LLVMValueRef): Long { // LLVMGetElementType is called because we need to dereference a pointer to function. diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/Imports.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/Imports.kt index a411f9382e3..0c13c4b8157 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/Imports.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/Imports.kt @@ -12,12 +12,17 @@ import org.jetbrains.kotlin.konan.library.KonanLibrary import org.jetbrains.kotlin.library.metadata.CompiledKlibModuleOrigin import org.jetbrains.kotlin.library.metadata.SyntheticModulesOrigin import org.jetbrains.kotlin.library.metadata.klibModuleOrigin +import org.jetbrains.kotlin.library.metadata.CompiledKlibFileOrigin +import org.jetbrains.kotlin.library.KotlinLibrary import org.jetbrains.kotlin.resolve.descriptorUtil.module internal interface LlvmImports { - fun add(origin: CompiledKlibModuleOrigin, onlyBitcode: Boolean = false) + data class LibraryFile(val library: KotlinLibrary, val fqName: String, val filePath: String) + + fun add(origin: CompiledKlibFileOrigin, onlyBitcode: Boolean = false) fun bitcodeIsUsed(library: KonanLibrary): Boolean fun nativeDependenciesAreUsed(library: KonanLibrary): Boolean + fun usedBitcode(): List } internal val DeclarationDescriptor.llvmSymbolOrigin: CompiledKlibModuleOrigin diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IntrinsicGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IntrinsicGenerator.kt index 3cddc3bfcc2..190afeef8bd 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IntrinsicGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IntrinsicGenerator.kt @@ -16,7 +16,6 @@ import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol import org.jetbrains.kotlin.ir.types.getClass -import org.jetbrains.kotlin.ir.util.dump import org.jetbrains.kotlin.ir.util.findAnnotation internal enum class IntrinsicType { @@ -440,21 +439,18 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv val functionReturnType = LlvmRetType(llvm.int8PtrType) val functionParameterTypes = listOf(LlvmParamType(llvm.int8PtrType), LlvmParamType(llvm.int8PtrType)) - val libobjc = context.standardLlvmSymbolsOrigin - val normalMessenger = codegen.llvm.externalFunction(LlvmFunctionProto( + val normalMessenger = codegen.llvm.externalNativeRuntimeFunction( "objc_msgSend$messengerNameSuffix", functionReturnType, functionParameterTypes, - isVararg = true, - origin = libobjc - )) - val superMessenger = codegen.llvm.externalFunction(LlvmFunctionProto( + isVararg = true + ) + val superMessenger = codegen.llvm.externalNativeRuntimeFunction( "objc_msgSendSuper$messengerNameSuffix", functionReturnType, functionParameterTypes, - isVararg = true, - origin = libobjc - )) + isVararg = true + ) val superClass = args.single() val messenger = LLVMBuildSelect(builder, diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index 04ee26e2323..92966794d19 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.backend.konan.lower.DECLARATION_ORIGIN_STATIC_STANDA import org.jetbrains.kotlin.backend.konan.lower.DECLARATION_ORIGIN_STATIC_THREAD_LOCAL_INITIALIZER import org.jetbrains.kotlin.builtins.UnsignedType import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.library.metadata.CompiledKlibFileOrigin import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET @@ -1627,21 +1628,18 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState, } else { // e.g. ObjCObject, ObjCObjectBase etc. if (dstClass.isObjCMetaClass()) { - val isClassProto = LlvmFunctionProto( + val isClass = llvm.externalNativeRuntimeFunction( "object_isClass", LlvmRetType(llvm.int8Type), - listOf(LlvmParamType(llvm.int8PtrType)), - origin = context.standardLlvmSymbolsOrigin + listOf(LlvmParamType(llvm.int8PtrType)) ) - val isClass = llvm.externalFunction(isClassProto) call(isClass, listOf(objCObject)).let { functionGenerationContext.icmpNe(it, llvm.int8(0)) } } else if (dstClass.isObjCProtocolClass()) { // Note: it is not clear whether this class should be looked up this way. // clang does the same, however swiftc uses dynamic lookup. - val protocolClass = - functionGenerationContext.getObjCClass("Protocol", context.standardLlvmSymbolsOrigin) + val protocolClass = functionGenerationContext.getObjCClass("Protocol", CompiledKlibFileOrigin.StdlibRuntime) call( llvm.Kotlin_Interop_IsObjectKindOfClass, listOf(objCObject, protocolClass) @@ -2496,7 +2494,7 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState, val protocolGetterProto = LlvmFunctionProto( protocolGetterName, LlvmRetType(llvm.int8PtrType), - origin = irClass.llvmSymbolOrigin, + origin = generationState.computeOrigin(irClass), independent = true // Protocol is header-only declaration. ) val protocolGetter = llvm.externalFunction(protocolGetterProto) @@ -2679,7 +2677,7 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState, if (generationState.llvmModuleSpecification.importsKotlinDeclarationsFromOtherSharedLibraries()) { // When some dynamic caches are used, we consider that stdlib is in the dynamic cache as well. // Runtime is linked into stdlib module only, so import runtime global from it. - val global = codegen.importGlobal(name, value.llvmType, context.standardLlvmSymbolsOrigin) + val global = codegen.importNativeRuntimeGlobal(name, value.llvmType) val initializer = generateFunctionNoRuntime(codegen, functionType(llvm.voidType, false), "") { store(value.llvm, global) ret(null) @@ -2689,7 +2687,7 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState, llvm.otherStaticInitializers += initializer } else { - generationState.llvmImports.add(context.standardLlvmSymbolsOrigin) + generationState.llvmImports.add(CompiledKlibFileOrigin.StdlibRuntime) // Define a strong runtime global. It'll overrule a weak global defined in a statically linked runtime. val global = llvm.staticData.placeGlobal(name, value, true) @@ -2759,11 +2757,9 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState, //-------------------------------------------------------------------------// fun appendStaticInitializers() { // Note: the list of libraries is topologically sorted (in order for initializers to be called correctly). - val libraries = (llvm.allBitcodeDependencies + listOf(null)/* Null for "current" non-library module */) + val dependencies = (llvm.allBitcodeDependencies + listOf(null)/* Null for "current" non-library module */) - val libraryToInitializers = libraries.associateWith { - mutableListOf() - } + val libraryToInitializers = dependencies.associate { it?.library to mutableListOf() } llvm.irStaticInitializers.forEach { val library = it.konanLibrary @@ -2783,7 +2779,8 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState, kVoidFuncType ).also { LLVMSetLinkage(it, LLVMLinkage.LLVMExternalLinkage) } - val ctorFunctions = libraries.flatMap { library -> + val ctorFunctions = dependencies.flatMap { dependency -> + val library = dependency?.library val initializers = libraryToInitializers.getValue(library) val ctorName = when { @@ -2811,12 +2808,16 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState, val cache = context.config.cachedLibraries.getLibraryCache(library) ?: error("Library $library is expected to be cached") - when (cache.granularity) { - CachedLibraries.Granularity.MODULE -> listOf(addCtorFunction(ctorName)) - CachedLibraries.Granularity.FILE -> { - context.irLinker.klibToModuleDeserializerMap[library]!!.sortedFileIds.map { - addCtorFunction(fileCtorName(library.uniqueName, it)) + when (cache) { + is CachedLibraries.Cache.Monolithic -> listOf(addCtorFunction(ctorName)) + is CachedLibraries.Cache.PerFile -> { + val files = when (dependency) { + is Llvm.CachedBitcodeDependency.WholeModule -> + context.irLinker.klibToModuleDeserializerMap[library]!!.sortedFileIds + is Llvm.CachedBitcodeDependency.CertainFiles -> + dependency.files } + files.map { addCtorFunction(fileCtorName(library.uniqueName, it)) } } } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinObjCClassInfoGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinObjCClassInfoGenerator.kt index 716ee1f90c1..cb4cea18d38 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinObjCClassInfoGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinObjCClassInfoGenerator.kt @@ -28,11 +28,11 @@ internal class KotlinObjCClassInfoGenerator(override val generationState: Native val classMethods = companionObject?.generateMethodDescs().orEmpty() val superclassName = irClass.getSuperClassNotAny()!!.let { - llvm.imports.add(it.llvmSymbolOrigin) + llvm.imports.add(generationState.computeOrigin(it)) it.descriptor.getExternalObjCClassBinaryName() } val protocolNames = irClass.getSuperInterfaces().map { - llvm.imports.add(it.llvmSymbolOrigin) + llvm.imports.add(generationState.computeOrigin(it)) it.name.asString().removeSuffix("Protocol") } @@ -161,7 +161,7 @@ internal fun CodeGenerator.kotlinObjCClassInfo(irClass: IrClass): LLVMValueRef { importGlobal( irClass.kotlinObjCClassInfoSymbolName, runtime.kotlinObjCClassInfo, - origin = irClass.llvmSymbolOrigin + generationState.computeOrigin(irClass) ) } else { llvmDeclarations.forClass(irClass).objCDeclarations!!.classInfoGlobal.llvmGlobal diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinStaticData.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinStaticData.kt index ce5aa761b86..d70fed12445 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinStaticData.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinStaticData.kt @@ -104,9 +104,8 @@ internal class KotlinStaticData(override val generationState: NativeGenerationSt UniqueKind.EMPTY_ARRAY -> context.ir.symbols.array.owner } return if (isExternal(descriptor)) { - constPointer(importGlobal( - kind.llvmName, runtime.objHeaderType, origin = descriptor.llvmSymbolOrigin - )) + constPointer( + importGlobal(kind.llvmName, runtime.objHeaderType, generationState.computeOrigin(descriptor))) } else { generationState.llvmDeclarations.forUnique(kind).pointer } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmFunctionPrototype.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmFunctionPrototype.kt index 3342410ca12..13f7b478b46 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmFunctionPrototype.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmFunctionPrototype.kt @@ -12,13 +12,14 @@ import llvm.* import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.RuntimeNames import org.jetbrains.kotlin.backend.konan.ir.llvmSymbolOrigin +import org.jetbrains.kotlin.library.metadata.CompiledKlibFileOrigin +import org.jetbrains.kotlin.library.metadata.CompiledKlibModuleOrigin import org.jetbrains.kotlin.ir.declarations.IrConstructor import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.types.isNothing import org.jetbrains.kotlin.ir.util.hasAnnotation import org.jetbrains.kotlin.ir.util.isSuspend import org.jetbrains.kotlin.ir.util.isThrowable -import org.jetbrains.kotlin.library.metadata.CompiledKlibModuleOrigin /** * Add attributes to LLVM function declaration and its invocation. @@ -156,14 +157,14 @@ internal class LlvmFunctionProto( returnType: LlvmRetType, parameterTypes: List = emptyList(), functionAttributes: List = emptyList(), - val origin: CompiledKlibModuleOrigin, + val origin: CompiledKlibFileOrigin, isVararg: Boolean = false, val independent: Boolean = false, ) : LlvmFunctionSignature(returnType, parameterTypes, isVararg, functionAttributes) { constructor( name: String, signature: LlvmFunctionSignature, - origin: CompiledKlibModuleOrigin, + origin: CompiledKlibFileOrigin, independent: Boolean = false, ) : this(name, signature.returnType, signature.parameterTypes, signature.functionAttributes, origin, signature.isVararg, independent) @@ -172,7 +173,7 @@ internal class LlvmFunctionProto( returnType = contextUtils.getLlvmFunctionReturnType(irFunction), parameterTypes = contextUtils.getLlvmFunctionParameterTypes(irFunction), functionAttributes = inferFunctionAttributes(contextUtils, irFunction), - origin = irFunction.llvmSymbolOrigin, + origin = contextUtils.generationState.computeOrigin(irFunction), independent = irFunction.hasAnnotation(RuntimeNames.independent) ) } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt index c97cf146d87..264c7277132 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt @@ -7,7 +7,7 @@ package org.jetbrains.kotlin.backend.konan.llvm import kotlinx.cinterop.* import llvm.* -import org.jetbrains.kotlin.library.metadata.CompiledKlibModuleOrigin +import org.jetbrains.kotlin.library.metadata.CompiledKlibFileOrigin internal val LLVMValueRef.type: LLVMTypeRef get() = LLVMTypeOf(this)!! @@ -170,19 +170,25 @@ internal fun ContextUtils.addGlobal(name: String, type: LLVMTypeRef, isExported: return LLVMAddGlobal(llvm.module, type, name)!! } -internal fun ContextUtils.importGlobal(name: String, type: LLVMTypeRef, origin: CompiledKlibModuleOrigin): LLVMValueRef { - llvm.imports.add(origin) - +private fun ContextUtils.importGlobal(name: String, type: LLVMTypeRef): LLVMValueRef { val found = LLVMGetNamedGlobal(llvm.module, name) - return if (found != null) { - assert (getGlobalType(found) == type) - assert (LLVMGetInitializer(found) == null) { "$name is already declared in the current module" } - found - } else { + return if (found == null) addGlobal(name, type, isExported = false) + else { + require(getGlobalType(found) == type) + require(LLVMGetInitializer(found) == null) { "$name is already declared in the current module" } + found } } +internal fun ContextUtils.importGlobal(name: String, type: LLVMTypeRef, origin: CompiledKlibFileOrigin) = + importGlobal(name, type).also { llvm.imports.add(origin) } + +internal fun ContextUtils.importObjCGlobal(name: String, type: LLVMTypeRef) = importGlobal(name, type) + +internal fun ContextUtils.importNativeRuntimeGlobal(name: String, type: LLVMTypeRef) = + importGlobal(name, type, CompiledKlibFileOrigin.StdlibRuntime) + internal abstract class AddressAccess { abstract fun getAddress(generationContext: FunctionGenerationContext?): LLVMValueRef } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt index ee7c1da5788..8f151a4cf59 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt @@ -16,7 +16,6 @@ import org.jetbrains.kotlin.builtins.PrimitiveType import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* -import org.jetbrains.kotlin.name.FqName internal class RTTIGenerator(override val generationState: NativeGenerationState) : ContextUtils { @@ -147,10 +146,8 @@ internal class RTTIGenerator(override val generationState: NativeGenerationState staticData.kotlinStringLiteral(string) } - private val EXPORT_TYPE_INFO_FQ_NAME = FqName.fromSegments(listOf("kotlin", "native", "internal", "ExportTypeInfo")) - private fun exportTypeInfoIfRequired(irClass: IrClass, typeInfoGlobal: LLVMValueRef?) { - val annotation = irClass.annotations.findAnnotation(EXPORT_TYPE_INFO_FQ_NAME) + val annotation = irClass.annotations.findAnnotation(RuntimeNames.exportTypeInfoAnnotation) if (annotation != null) { val name = annotation.getAnnotationStringValue()!! // TODO: use LLVMAddAlias. diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objc/ObjCCodeGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objc/ObjCCodeGenerator.kt index a6e02403acf..8070f5d5a95 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objc/ObjCCodeGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objc/ObjCCodeGenerator.kt @@ -28,48 +28,40 @@ internal open class ObjCCodeGenerator(val codegen: CodeGenerator) { } private val objcMsgSend = constPointer( - llvm.externalFunction(LlvmFunctionProto( + llvm.externalNativeRuntimeFunction( "objc_msgSend", LlvmRetType(llvm.int8PtrType), listOf(LlvmParamType(llvm.int8PtrType), LlvmParamType(llvm.int8PtrType)), - isVararg = true, - origin = context.stdlibModule.llvmSymbolOrigin - )).llvmValue + isVararg = true + ).llvmValue ) - val objcRelease = run { - val proto = LlvmFunctionProto( - "llvm.objc.release", - LlvmRetType(llvm.voidType), - listOf(LlvmParamType(llvm.int8PtrType)), - listOf(LlvmFunctionAttribute.NoUnwind), - origin = context.stdlibModule.llvmSymbolOrigin - ) - llvm.externalFunction(proto) - } + val objcRelease = llvm.externalNativeRuntimeFunction( + "llvm.objc.release", + LlvmRetType(llvm.voidType), + listOf(LlvmParamType(llvm.int8PtrType)), + listOf(LlvmFunctionAttribute.NoUnwind) + ) - val objcAlloc = llvm.externalFunction(LlvmFunctionProto( + val objcAlloc = llvm.externalNativeRuntimeFunction( "objc_alloc", LlvmRetType(llvm.int8PtrType), - listOf(LlvmParamType(llvm.int8PtrType)), - origin = context.stdlibModule.llvmSymbolOrigin - )) + listOf(LlvmParamType(llvm.int8PtrType)) + ) - val objcAutoreleaseReturnValue = llvm.externalFunction(LlvmFunctionProto( + val objcAutoreleaseReturnValue = llvm.externalNativeRuntimeFunction( "llvm.objc.autoreleaseReturnValue", LlvmRetType(llvm.int8PtrType), listOf(LlvmParamType(llvm.int8PtrType)), - listOf(LlvmFunctionAttribute.NoUnwind), - origin = context.stdlibModule.llvmSymbolOrigin - )) + listOf(LlvmFunctionAttribute.NoUnwind) + ) - val objcRetainAutoreleasedReturnValue = llvm.externalFunction(LlvmFunctionProto( + val objcRetainAutoreleasedReturnValue = llvm.externalNativeRuntimeFunction( "llvm.objc.retainAutoreleasedReturnValue", LlvmRetType(llvm.int8PtrType), listOf(LlvmParamType(llvm.int8PtrType)), - listOf(LlvmFunctionAttribute.NoUnwind), - origin = context.stdlibModule.llvmSymbolOrigin - )) + listOf(LlvmFunctionAttribute.NoUnwind) + ) val objcRetainAutoreleasedReturnValueMarker: LLVMValueRef? by lazy { // See emitAutoreleasedReturnValueMarker in Clang. diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objc/ObjCDataGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objc/ObjCDataGenerator.kt index 59bdac65657..96fd623bcbe 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objc/ObjCDataGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objc/ObjCDataGenerator.kt @@ -7,7 +7,6 @@ package org.jetbrains.kotlin.backend.konan.llvm.objc import llvm.* import org.jetbrains.kotlin.backend.konan.llvm.* -import org.jetbrains.kotlin.library.metadata.CurrentKlibModuleOrigin /** * This class provides methods to generate Objective-C RTTI and other data. @@ -75,18 +74,14 @@ internal class ObjCDataGenerator(val codegen: CodeGenerator) { val globalName = prefix + name // TODO: refactor usages and use [Global] class. - val llvmGlobal = LLVMGetNamedGlobal(llvm.module, globalName) ?: - codegen.importGlobal(globalName, classObjectType, CurrentKlibModuleOrigin) + val llvmGlobal = LLVMGetNamedGlobal(llvm.module, globalName) + ?: codegen.importObjCGlobal(globalName, classObjectType) return constPointer(llvmGlobal) } private val emptyCache = constPointer( - codegen.importGlobal( - "_objc_empty_cache", - codegen.runtime.objCCache, - CurrentKlibModuleOrigin - ) + codegen.importObjCGlobal("_objc_empty_cache", codegen.runtime.objCCache) ) fun emitEmptyClass(name: String, superName: String) { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/BlockPointerSupport.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/BlockPointerSupport.kt index 6966d2c248f..7604a7db4e7 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/BlockPointerSupport.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/BlockPointerSupport.kt @@ -13,7 +13,6 @@ import org.jetbrains.kotlin.backend.konan.llvm.* import org.jetbrains.kotlin.backend.konan.objcexport.BlockPointerBridge import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.util.simpleFunctions -import org.jetbrains.kotlin.library.metadata.CurrentKlibModuleOrigin import org.jetbrains.kotlin.util.OperatorNameConventions internal fun ObjCExportCodeGeneratorBase.generateBlockToKotlinFunctionConverter( @@ -315,11 +314,7 @@ internal class BlockGenerator(private val codegen: CodeGenerator) { ret(llvm.kNullInt8Ptr) } - val isa = codegen.importGlobal( - "_NSConcreteStackBlock", - llvm.int8PtrType, - CurrentKlibModuleOrigin - ) + val isa = codegen.importObjCGlobal("_NSConcreteStackBlock", llvm.int8PtrType) val flags = llvm.int32((1 shl 25) or (1 shl 30) or (1 shl 31)) val reserved = llvm.int32(0) @@ -350,12 +345,8 @@ internal class BlockGenerator(private val codegen: CodeGenerator) { } private val ObjCExportCodeGeneratorBase.retainBlock: LlvmCallable - get() { - val functionProto = LlvmFunctionProto( - "objc_retainBlock", - LlvmRetType(llvm.int8PtrType), - listOf(LlvmParamType(llvm.int8PtrType)), - origin = CurrentKlibModuleOrigin - ) - return llvm.externalFunction(functionProto) - } + get() = llvm.externalNativeRuntimeFunction( + "objc_retainBlock", + LlvmRetType(llvm.int8PtrType), + listOf(LlvmParamType(llvm.int8PtrType)) + ) \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt index 576279e6fdd..ad00d3d3704 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt @@ -16,7 +16,6 @@ import org.jetbrains.kotlin.backend.konan.descriptors.allOverriddenFunctions import org.jetbrains.kotlin.backend.konan.descriptors.isAbstract import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols import org.jetbrains.kotlin.backend.konan.ir.isUnit -import org.jetbrains.kotlin.backend.konan.ir.llvmSymbolOrigin import org.jetbrains.kotlin.backend.konan.ir.superClasses import org.jetbrains.kotlin.backend.konan.llvm.* import org.jetbrains.kotlin.backend.konan.llvm.objc.ObjCCodeGenerator @@ -26,6 +25,7 @@ import org.jetbrains.kotlin.backend.konan.objcexport.* import org.jetbrains.kotlin.backend.konan.serialization.resolveFakeOverrideMaybeAbstract import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.library.metadata.CompiledKlibFileOrigin import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.IrClassReference @@ -40,8 +40,6 @@ import org.jetbrains.kotlin.ir.types.isUnit import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.konan.target.AppleConfigurables import org.jetbrains.kotlin.konan.target.LinkerOutputKind -import org.jetbrains.kotlin.library.metadata.CompiledKlibModuleOrigin -import org.jetbrains.kotlin.library.metadata.CurrentKlibModuleOrigin import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi2ir.descriptors.IrBuiltInsOverDescriptors import org.jetbrains.kotlin.utils.DFS @@ -223,15 +221,6 @@ internal open class ObjCExportCodeGeneratorBase(codegen: CodeGenerator) : ObjCCo val rttiGenerator = RTTIGenerator(generationState) - private val objcTerminate: LlvmCallable by lazy { - llvm.externalFunction(LlvmFunctionProto( - "objc_terminate", - LlvmRetType(llvm.voidType), - functionAttributes = listOf(LlvmFunctionAttribute.NoUnwind), - origin = CurrentKlibModuleOrigin - )) - } - fun dispose() { rttiGenerator.dispose() } @@ -478,7 +467,7 @@ internal class ObjCExportCodeGenerator( replaceExternalWeakOrCommonGlobal( "Kotlin_ObjCInterop_uniquePrefix", codegen.staticData.cStringLiteral(namer.topLevelNamePrefix), - context.standardLlvmSymbolsOrigin + CompiledKlibFileOrigin.StdlibRuntime ) emitSelectorsHolder() @@ -524,7 +513,7 @@ internal class ObjCExportCodeGenerator( val sortedAdaptersPointer = staticData.placeGlobalConstArray("", type, sortedAdapters) // Note: this globals replace runtime globals with weak linkage: - val origin = context.standardLlvmSymbolsOrigin + val origin = CompiledKlibFileOrigin.StdlibRuntime replaceExternalWeakOrCommonGlobal(prefix, sortedAdaptersPointer, origin) replaceExternalWeakOrCommonGlobal("${prefix}Num", llvm.constInt32(sortedAdapters.size), origin) } @@ -535,9 +524,9 @@ internal class ObjCExportCodeGenerator( if (generationState.llvmModuleSpecification.importsKotlinDeclarationsFromOtherSharedLibraries()) { replaceExternalWeakOrCommonGlobal( - "Kotlin_ObjCExport_initTypeAdapters", - llvm.constInt1(true), - context.standardLlvmSymbolsOrigin + "Kotlin_ObjCExport_initTypeAdapters", + llvm.constInt1(true), + CompiledKlibFileOrigin.StdlibRuntime ) } } @@ -704,7 +693,7 @@ internal class ObjCExportCodeGenerator( private fun ObjCExportCodeGenerator.replaceExternalWeakOrCommonGlobal( name: String, value: ConstValue, - origin: CompiledKlibModuleOrigin + origin: CompiledKlibFileOrigin ) { // TODO: A similar mechanism is used in `IrToBitcode.overrideRuntimeGlobal`. Consider merging them. if (generationState.llvmModuleSpecification.importsKotlinDeclarationsFromOtherSharedLibraries()) { @@ -742,7 +731,7 @@ private fun ObjCExportCodeGenerator.setObjCExportTypeInfo( replaceExternalWeakOrCommonGlobal( irClass.writableTypeInfoSymbolName, writableTypeInfoValue, - irClass.llvmSymbolOrigin + generationState.computeOrigin(irClass) ) } else { setOwnWritableTypeInfo(irClass, writableTypeInfoValue) @@ -924,11 +913,8 @@ private fun ObjCExportCodeGenerator.emitSpecialClassesConvertions() { private fun ObjCExportCodeGenerator.emitCollectionConverters() { - fun importConverter(name: String): ConstPointer = constPointer(llvm.externalFunction(LlvmFunctionProto( - name, - kotlinToObjCFunctionType, - origin = CurrentKlibModuleOrigin - )).llvmValue) + fun importConverter(name: String): ConstPointer = + constPointer(llvm.externalNativeRuntimeFunction(name, kotlinToObjCFunctionType).llvmValue) setObjCExportTypeInfo( symbols.list.owner, diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CacheInfoBuilder.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CacheInfoBuilder.kt index 121007a3a54..093bd9b6fa3 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CacheInfoBuilder.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CacheInfoBuilder.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.backend.konan.lower import org.jetbrains.kotlin.backend.konan.DECLARATION_ORIGIN_FUNCTION_CLASS import org.jetbrains.kotlin.backend.konan.NativeGenerationState +import org.jetbrains.kotlin.backend.konan.KonanFqNames import org.jetbrains.kotlin.backend.konan.serialization.KonanIrLinker import org.jetbrains.kotlin.backend.konan.serialization.KonanManglerIr import org.jetbrains.kotlin.ir.IrElement @@ -21,32 +22,49 @@ internal class CacheInfoBuilder( private val moduleDeserializer: KonanIrLinker.KonanPartialModuleDeserializer, private val irModule: IrModuleFragment ) { - fun build() = irModule.acceptChildrenVoid(object : IrElementVisitorVoid { - override fun visitElement(element: IrElement) { - element.acceptChildrenVoid(this) - } + fun build() = irModule.files.forEach { irFile -> + var hasEagerlyInitializedProperties = false - override fun visitClass(declaration: IrClass) { - declaration.acceptChildrenVoid(this) - - if (!declaration.isInterface && !declaration.isLocal - && declaration.isExported && declaration.origin != DECLARATION_ORIGIN_FUNCTION_CLASS - ) { - val declaredFields = generationState.context.getLayoutBuilder(declaration).getDeclaredFields() - generationState.classFields.add(moduleDeserializer.buildClassFields(declaration, declaredFields)) + irFile.acceptChildrenVoid(object : IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) } - } - override fun visitFunction(declaration: IrFunction) { - // Don't need to visit the children here: classes would be all local and wouldn't be handled anyway, - // as for functions - both their callees will be handled and inline bodies will be built for the top function. + override fun visitClass(declaration: IrClass) { + declaration.acceptChildrenVoid(this) - if (!declaration.isFakeOverride && declaration.isInline && declaration.isExported) { - generationState.inlineFunctionBodies.add(moduleDeserializer.buildInlineFunctionReference(declaration)) - trackCallees(declaration) + if (!declaration.isInterface && !declaration.isLocal + && declaration.isExported && declaration.origin != DECLARATION_ORIGIN_FUNCTION_CLASS + ) { + val declaredFields = generationState.context.getLayoutBuilder(declaration).getDeclaredFields() + generationState.classFields.add(moduleDeserializer.buildClassFields(declaration, declaredFields)) + } } - } - }) + + override fun visitFunction(declaration: IrFunction) { + // Don't need to visit the children here: classes would be all local and wouldn't be handled anyway, + // as for functions - both their callees will be handled and inline bodies will be built for the top function. + + if (!declaration.isFakeOverride && declaration.isInline && declaration.isExported) { + generationState.inlineFunctionBodies.add(moduleDeserializer.buildInlineFunctionReference(declaration)) + trackCallees(declaration) + } + } + + override fun visitProperty(declaration: IrProperty) { + declaration.acceptChildrenVoid(this) + + if (declaration.parent == irFile + && declaration.hasAnnotation(KonanFqNames.eagerInitialization) + ) { + hasEagerlyInitializedProperties = true + } + } + }) + + if (hasEagerlyInitializedProperties) + generationState.eagerInitializedFiles.add(moduleDeserializer.buildEagerInitializedFile(irFile)) + } private val IrDeclaration.isExported get() = with(KonanManglerIr) { isExported(compatibleMode = moduleDeserializer.compatibilityMode.oldSignatures) } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CachesAbiLowering.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CachesAbiLowering.kt index 1332f0876c0..17a53f29dcb 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CachesAbiLowering.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CachesAbiLowering.kt @@ -51,6 +51,7 @@ internal class CachesAbiSupport(mapping: NativeMapping, private val irFactory: I returnType = irClass.parentAsClass.defaultType }.apply { parent = irClass.getPackageFragment() + attributeOwnerId = irClass // To be able to get the file. addValueParameter { name = Name.identifier("innerClass") @@ -73,6 +74,7 @@ internal class CachesAbiSupport(mapping: NativeMapping, private val irFactory: I returnType = actualField.type }.apply { parent = irProperty.getPackageFragment() + attributeOwnerId = irProperty // To be able to get the file. (owner as? IrClass)?.let { addValueParameter { @@ -150,6 +152,7 @@ internal class ImportCachesAbiTransformer(val generationState: NativeGenerationS private val cachesAbiSupport = generationState.context.cachesAbiSupport private val innerClassesSupport = generationState.context.innerClassesSupport private val llvmImports = generationState.llvmImports + private val irLinker = generationState.context.irLinker override fun lower(irFile: IrFile) { irFile.transformChildrenVoid(this) @@ -168,7 +171,7 @@ internal class ImportCachesAbiTransformer(val generationState: NativeGenerationS irClass?.isInner == true && innerClassesSupport.getOuterThisField(irClass) == field -> { val accessor = cachesAbiSupport.getOuterThisAccessor(irClass) - llvmImports.add(irClass.llvmSymbolOrigin) + llvmImports.add(generationState.computeOrigin(irClass)) return irCall(expression.startOffset, expression.endOffset, accessor, emptyList()).apply { putValueArgument(0, expression.receiver) } @@ -176,7 +179,7 @@ internal class ImportCachesAbiTransformer(val generationState: NativeGenerationS property?.isLateinit == true -> { val accessor = cachesAbiSupport.getLateinitPropertyAccessor(property) - llvmImports.add(property.llvmSymbolOrigin) + llvmImports.add(generationState.computeOrigin(property)) return irCall(expression.startOffset, expression.endOffset, accessor, emptyList()).apply { if (irClass != null) putValueArgument(0, expression.receiver) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InteropLowering.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InteropLowering.kt index 9a200ac15a2..346618d267f 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InteropLowering.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InteropLowering.kt @@ -543,7 +543,7 @@ private class InteropLoweringPart1(val generationState: NativeGenerationState) : ): IrExpression = generateWithStubs(call) { if (method.parent !is IrClass) { // Category-provided. - generationState.llvmImports.add(method.llvmSymbolOrigin) + generationState.llvmImports.add(generationState.computeOrigin(method)) } this.generateObjCCall( @@ -617,7 +617,7 @@ private class InteropLoweringPart1(val generationState: NativeGenerationState) : // handled in CodeGeneratorVisitor.callVirtual. val useKotlinDispatch = isInteropStubsFile && (builder.scope.scopeOwnerSymbol.owner as? IrAnnotationContainer) - ?.hasAnnotation(FqName("kotlin.native.internal.ExportForCppRuntime")) == true + ?.hasAnnotation(RuntimeNames.exportForCppRuntime) == true if (!useKotlinDispatch) { val arguments = callee.valueParameters.map { expression.getValueArgument(it.index) } @@ -1011,7 +1011,7 @@ private class InteropTransformer( private fun generateCCall(expression: IrCall): IrExpression { val function = expression.symbol.owner - generationState.llvmImports.add(function.llvmSymbolOrigin) + generationState.llvmImports.add(generationState.computeOrigin(function)) val exceptionMode = ForeignExceptionMode.byValue( function.konanLibrary?.manifestProperties?.getProperty(ForeignExceptionMode.manifestKey) ) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrlinker.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrlinker.kt index 43d9ccf3202..104311043e1 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrlinker.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrlinker.kt @@ -23,14 +23,17 @@ import org.jetbrains.kotlin.backend.common.serialization.* import org.jetbrains.kotlin.backend.common.serialization.encodings.BinaryNameAndType import org.jetbrains.kotlin.backend.common.serialization.encodings.BinarySymbolData import org.jetbrains.kotlin.backend.common.serialization.encodings.FunctionFlags +import org.jetbrains.kotlin.backend.common.serialization.isForwardDeclarationModule import org.jetbrains.kotlin.backend.common.serialization.linkerissues.UserVisibleIrModulesSupport import org.jetbrains.kotlin.backend.konan.* +import org.jetbrains.kotlin.backend.konan.descriptors.* import org.jetbrains.kotlin.backend.konan.descriptors.ClassLayoutBuilder import org.jetbrains.kotlin.backend.konan.descriptors.findPackage -import org.jetbrains.kotlin.backend.konan.descriptors.isInteropLibrary import org.jetbrains.kotlin.backend.konan.descriptors.toFieldInfo import org.jetbrains.kotlin.backend.konan.ir.interop.IrProviderForCEnumAndCStructStubs import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.library.metadata.DeserializedKlibModuleOrigin +import org.jetbrains.kotlin.library.metadata.klibModuleOrigin import org.jetbrains.kotlin.descriptors.konan.isNativeStdlib import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.builders.TranslationPluginContext @@ -47,8 +50,6 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformer import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.library.KotlinAbiVersion import org.jetbrains.kotlin.library.KotlinLibrary -import org.jetbrains.kotlin.library.metadata.DeserializedKlibModuleOrigin -import org.jetbrains.kotlin.library.metadata.klibModuleOrigin import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name @@ -196,6 +197,27 @@ internal object ClassFieldsSerializer { } } +class SerializedEagerInitializedFile(val file: Int) + +internal object EagerInitializedPropertySerializer { + fun serialize(properties: List): ByteArray { + val size = properties.sumOf { Int.SIZE_BYTES } + val stream = ByteArrayStream(ByteArray(size)) + properties.forEach { + stream.writeInt(it.file) + } + return stream.buf + } + + fun deserializeTo(data: ByteArray, result: MutableList) { + val stream = ByteArrayStream(data) + while (stream.hasData()) { + val file = stream.readInt() + result.add(SerializedEagerInitializedFile(file)) + } + } +} + internal fun ProtoClass.findClass(irClass: IrClass, fileReader: IrLibraryFile, symbolDeserializer: IrSymbolDeserializer): ProtoClass { val signature = irClass.symbol.signature ?: error("No signature for ${irClass.render()}") var result: ProtoClass? = null @@ -400,9 +422,7 @@ internal class KonanIrLinker( is IrExternalPackageFragment -> { val moduleDescriptor = packageFragment.packageFragmentDescriptor.containingDeclaration val moduleDeserializer = moduleDeserializers[moduleDescriptor] ?: error("No module deserializer for $moduleDescriptor") - val descriptor = declaration.descriptor - val idSig = moduleDeserializer.descriptorSignatures[descriptor] ?: error("No signature for $descriptor") - idSig.topLevelSignature().fileSignature()?.fileName ?: error("No file for $idSig") + moduleDeserializer.getFileNameOf(declaration) } else -> error("Unknown package fragment kind ${packageFragment::class.java}") @@ -443,6 +463,33 @@ internal class KonanIrLinker( ) { override val moduleFragment: IrModuleFragment = KonanIrModuleFragmentImpl(moduleDescriptor, builtIns) + val files by lazy { fileDeserializationStates.map { it.file } } + + private val fileToFileDeserializationState by lazy { fileDeserializationStates.associateBy { it.file } } + + private val idSignatureToFile by lazy { + buildMap { + fileDeserializationStates.forEach { fileDeserializationState -> + fileDeserializationState.fileDeserializer.reversedSignatureIndex.keys.forEach { idSig -> + put(idSig, fileDeserializationState.file) + } + } + } + } + + fun getFileNameOf(declaration: IrDeclaration): String { + fun IrDeclaration.getSignature() = symbol.signature ?: descriptorSignatures[descriptor] + + val idSig = declaration.getSignature() + ?: (declaration.parent as? IrDeclaration)?.getSignature() + ?: ((declaration as? IrAttributeContainer)?.attributeOwnerId as? IrDeclaration)?.getSignature() + ?: error("Can't find signature of ${declaration.render()}") + val topLevelIdSig = idSig.topLevelSignature() + return topLevelIdSig.fileSignature()?.fileName + ?: idSignatureToFile[topLevelIdSig]?.path + ?: error("No file for $idSig") + } + fun buildInlineFunctionReference(irFunction: IrFunction): SerializedInlineFunctionReference { val signature = irFunction.symbol.signature ?: error("No signature for ${irFunction.render()}") @@ -598,6 +645,12 @@ internal class KonanIrLinker( }) } + fun buildEagerInitializedFile(irFile: IrFile): SerializedEagerInitializedFile { + val fileDeserializationState = fileToFileDeserializationState[irFile] + ?: error("No file deserializer for ${irFile.render()}") + return SerializedEagerInitializedFile(fileDeserializationState.fileIndex) + } + private val descriptorByIdSignatureFinder = DescriptorByIdSignatureFinderImpl( moduleDescriptor, KonanManglerDesc, DescriptorByIdSignatureFinderImpl.LookupMode.MODULE_ONLY @@ -807,6 +860,13 @@ internal class KonanIrLinker( } } + val eagerInitializedFiles by lazy { + val cache = cachedLibraries.getLibraryCache(klib)!! // ?: error("No cache for ${klib.libraryName}") // KT-54668 + cache.serializedEagerInitializedFiles + .map { fileDeserializationStates[it.file].file } + .distinct() + } + val sortedFileIds by lazy { fileDeserializationStates .sortedBy { it.file.fileEntry.name } diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Runtime.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Runtime.kt new file mode 100644 index 00000000000..9370a619417 --- /dev/null +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Runtime.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ +package kotlin.native.internal + +// +// NOTE: THIS FILE IS AUTO-GENERATED, DO NOT EDIT +// +// This is a special fictitious file, mirroring native runtime. +// During cache building for stdlib, the compiler will link native runtime to the bitcode this file +// will be compiled to. The bitcode dependencies of this file will be generated by the compiler, +// containing all the files with either ExportTypeInfo or ExportForCppRuntime annotations. +//