From 77f24a22ddd2002584c7e168a4b9ab68ec6b6485 Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Fri, 1 Jul 2022 08:30:24 +0300 Subject: [PATCH] [K/N][IR][codegen][caches] Speed-up of per-file caches --- .../arguments/K2NativeCompilerArguments.kt | 5 +- .../org/jetbrains/kotlin/cli/bc/K2Native.kt | 15 ++- ...BuiltInFictitiousFunctionIrClassFactory.kt | 3 + .../kotlin/backend/konan/CacheSupport.kt | 58 ++++++--- .../kotlin/backend/konan/CachedLibraries.kt | 13 +- .../kotlin/backend/konan/CompilerOutput.kt | 3 + .../jetbrains/kotlin/backend/konan/Context.kt | 33 +++-- .../kotlin/backend/konan/KonanConfig.kt | 25 +++- .../backend/konan/KonanConfigurationKeys.kt | 6 +- .../kotlin/backend/konan/KonanDriver.kt | 18 ++- .../backend/konan/KonanLoweringPhases.kt | 34 +++-- .../jetbrains/kotlin/backend/konan/Linker.kt | 2 +- .../konan/LlvmModuleSpecificationImpl.kt | 15 ++- .../kotlin/backend/konan/OutputFiles.kt | 2 +- .../kotlin/backend/konan/ToplevelPhases.kt | 122 +++++++++++++----- .../konan/descriptors/ClassLayoutBuilder.kt | 13 +- .../backend/konan/llvm/BitcodePhases.kt | 22 +++- .../kotlin/backend/konan/llvm/ContextUtils.kt | 3 +- .../kotlin/backend/konan/llvm/IrToBitcode.kt | 2 +- .../backend/konan/llvm/LlvmDeclarations.kt | 4 - .../lower/NativeInlineFunctionResolver.kt | 56 +++++--- .../konan/serialization/KonanIrlinker.kt | 33 +++-- 22 files changed, 339 insertions(+), 148 deletions(-) diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2NativeCompilerArguments.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2NativeCompilerArguments.kt index 6c618277dcc..33a79ed23b9 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2NativeCompilerArguments.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2NativeCompilerArguments.kt @@ -219,11 +219,14 @@ class K2NativeCompilerArguments : CommonCompilerArguments() { description = "Path to file to cache", delimiter = "" ) - var fileToCache: String? = null + var filesToCache: Array? = null @Argument(value = "-Xmake-per-file-cache", description = "Force compiler to produce per-file cache") var makePerFileCache: Boolean = false + @Argument(value = "-Xbatched-per-file-cache-build", valueDescription = "{disable|enable}", description = "Build per-file cache in one batch") + var batchedPerFileCacheBuild: String? = null + @Argument(value = "-Xexport-kdoc", description = "Export KDoc in framework header") var exportKDoc: Boolean = false diff --git a/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt b/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt index 3c65408a947..3a19ebe2b7f 100644 --- a/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt +++ b/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt @@ -285,11 +285,20 @@ class K2Native : CLICompiler() { libraryToAddToCache?.let { put(LIBRARY_TO_ADD_TO_CACHE, it) } put(CACHE_DIRECTORIES, cacheDirectories) put(CACHED_LIBRARIES, parseCachedLibraries(arguments, configuration)) - val fileToCache = arguments.fileToCache - if (outputKind == CompilerOutputKind.PRELIMINARY_CACHE && fileToCache == null) + val filesToCache = arguments.filesToCache + if (outputKind == CompilerOutputKind.PRELIMINARY_CACHE && filesToCache.isNullOrEmpty()) configuration.report(ERROR, "preliminary_cache only supported for per-file caches") - fileToCache?.let { put(FILE_TO_CACHE, it) } + filesToCache?.let { put(FILES_TO_CACHE, it.toList()) } put(MAKE_PER_FILE_CACHE, arguments.makePerFileCache) + putIfNotNull(BATCHED_PER_FILE_CACHE_BUILD, when (arguments.batchedPerFileCacheBuild) { + null -> null + "enable" -> true + "disable" -> false + else -> { + configuration.report(ERROR, "Expected 'enable' or 'disable' for batchedPerFileCacheBuild") + false + } + }) parseShortModuleName(arguments, configuration, outputKind)?.let { put(SHORT_MODULE_NAME, it) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BuiltInFictitiousFunctionIrClassFactory.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BuiltInFictitiousFunctionIrClassFactory.kt index 3bf7ab92a08..4c7b4475deb 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BuiltInFictitiousFunctionIrClassFactory.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BuiltInFictitiousFunctionIrClassFactory.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.backend.konan import org.jetbrains.kotlin.backend.konan.descriptors.findPackage +import org.jetbrains.kotlin.builtins.FunctionInterfacePackageFragment import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor import org.jetbrains.kotlin.builtins.functions.FunctionClassKind import org.jetbrains.kotlin.descriptors.* @@ -33,6 +34,8 @@ import org.jetbrains.kotlin.util.OperatorNameConventions internal object DECLARATION_ORIGIN_FUNCTION_CLASS : IrDeclarationOriginImpl("DECLARATION_ORIGIN_FUNCTION_CLASS") +val IrPackageFragment.isFunctionInterfaceFile get() = packageFragmentDescriptor is FunctionInterfacePackageFragment + abstract class KonanIrAbstractDescriptorBasedFunctionFactory : IrProvider, IrAbstractDescriptorBasedFunctionFactory() internal class LazyIrFunctionFactory( 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 17adb0b0634..febecedb88c 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 @@ -34,17 +34,25 @@ sealed class CacheDeserializationStrategy { override fun contains(fqName: FqName, fileName: String) = true } - class SingleFile(val filePath: String) : CacheDeserializationStrategy() { + class SingleFile(val filePath: String, val fqName: String) : CacheDeserializationStrategy() { override fun contains(filePath: String) = filePath == this.filePath - lateinit var fqName: String - override fun contains(fqName: FqName, fileName: String) = fqName.asString() == this.fqName && File(filePath).name == fileName } + + class MultipleFiles(filePaths: List, fqNames: List) : CacheDeserializationStrategy() { + private val filePaths = filePaths.toSet() + + private val fqNamesWithNames = fqNames.mapIndexed { i: Int, fqName: String -> Pair(fqName, File(filePaths[i]).name) }.toSet() + + override fun contains(filePath: String) = filePath in filePaths + + override fun contains(fqName: FqName, fileName: String) = Pair(fqName.asString(), fileName) in fqNamesWithNames + } } -class PartialCacheInfo(val klib: KotlinLibrary, val strategy: CacheDeserializationStrategy) +class PartialCacheInfo(val klib: KotlinLibrary, var strategy: CacheDeserializationStrategy) class CacheSupport( private val configuration: CompilerConfiguration, @@ -64,7 +72,7 @@ class CacheSupport( ?: configuration.reportCompilationError("cache directory $it is not found or not a directory") } - internal fun tryGetImplicitOutput(): String? { + internal fun tryGetImplicitOutput(explicitlyProducePerFileCache: Boolean): String? { val libraryToCache = libraryToCache ?: return null // Put the resulting library in the first cache directory. val cacheDirectory = implicitCacheDirectories.firstOrNull() ?: return null @@ -75,19 +83,13 @@ class CacheSupport( else CachedLibraries.getPerFileCachedLibraryName(libraryToCache.klib) ) - if (singleFileStrategy == null) - return baseLibraryCacheDirectory.absolutePath + val singleFilePath = singleFileStrategy?.filePath + ?: return baseLibraryCacheDirectory.absolutePath - val fileToCache = singleFileStrategy.filePath - val fileProtos = Array(libraryToCache.klib.fileCount()) { - ProtoFile.parseFrom(libraryToCache.klib.file(it).codedInputStream, ExtensionRegistryLite.newInstance()) - } - val fileIndex = fileProtos.indexOfFirst { it.fileEntry.name == fileToCache } - require(fileIndex >= 0) { "No file found in klib with path $fileToCache" } - val fileReader = IrLibraryFileFromBytes(IrKlibBytesSource(libraryToCache.klib, fileIndex)) - val fqName = fileReader.deserializeFqName(fileProtos[fileIndex].fqNameList) - singleFileStrategy.fqName = fqName - val fileCacheDirectory = baseLibraryCacheDirectory.child(cacheFileId(fqName, fileToCache)) + val fqName = singleFileStrategy.fqName + val fileCacheDirectory = baseLibraryCacheDirectory.child(cacheFileId(fqName, singleFilePath)) + if (!explicitlyProducePerFileCache) + return fileCacheDirectory.absolutePath val contentDirName = if (produce == CompilerOutputKind.PRELIMINARY_CACHE) CachedLibraries.PER_FILE_CACHE_IR_LEVEL_DIR_NAME else CachedLibraries.PER_FILE_CACHE_BINARY_LEVEL_DIR_NAME @@ -132,8 +134,26 @@ class CacheSupport( if (libraryCache != null && libraryCache.granularity == CachedLibraries.Granularity.MODULE) null else { - val fileToCache = configuration.get(KonanConfigKeys.FILE_TO_CACHE) - PartialCacheInfo(libraryToAddToCache, if (fileToCache == null) CacheDeserializationStrategy.WholeModule else CacheDeserializationStrategy.SingleFile(fileToCache)) + val filesToCache = configuration.get(KonanConfigKeys.FILES_TO_CACHE) + + val strategy = if (filesToCache.isNullOrEmpty()) + CacheDeserializationStrategy.WholeModule + else { + val fileProtos = Array(libraryToAddToCache.fileCount()) { + ProtoFile.parseFrom(libraryToAddToCache.file(it).codedInputStream, ExtensionRegistryLite.newInstance()) + } + val fileNameToIndex = fileProtos.withIndex().associate { it.value.fileEntry.name to it.index } + val fqNames = filesToCache.map { fileName -> + val index = fileNameToIndex[fileName] ?: error("No file with path $fileName is found in klib ${libraryToAddToCache.libraryName}") + val fileReader = IrLibraryFileFromBytes(IrKlibBytesSource(libraryToAddToCache, index)) + fileReader.deserializeFqName(fileProtos[index].fqNameList) + } + if (configuration.get(KonanConfigKeys.BATCHED_PER_FILE_CACHE_BUILD) == false) + CacheDeserializationStrategy.SingleFile(filesToCache.single(), fqNames.single()) + else + CacheDeserializationStrategy.MultipleFiles(filesToCache, fqNames) + } + PartialCacheInfo(libraryToAddToCache, strategy) } } 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 6fa42ea1624..d1bf14f98c2 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 @@ -90,22 +90,25 @@ class CachedLibraries( private val cacheDirsContents = mutableMapOf>() private fun selectCache(library: KotlinLibrary, cacheDir: File): Cache? { - val cacheBinaryPartDir = cacheDir.child(PER_FILE_CACHE_BINARY_LEVEL_DIR_NAME) // See Linker.renameOutput why is it ok to have an empty cache directory. val cacheDirContents = cacheDirsContents.getOrPut(cacheDir.absolutePath) { - cacheBinaryPartDir.listFilesOrEmpty.map { it.absolutePath }.toSet() + cacheDir.listFilesOrEmpty.map { it.absolutePath }.toSet() } if (cacheDirContents.isEmpty()) return null + val cacheBinaryPartDir = cacheDir.child(PER_FILE_CACHE_BINARY_LEVEL_DIR_NAME) + val cacheBinaryPartDirContents = cacheDirsContents.getOrPut(cacheBinaryPartDir.absolutePath) { + 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)) - if (dynamicFile.absolutePath in cacheDirContents && staticFile.absolutePath in cacheDirContents) + 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 cacheDirContents -> Cache(Kind.DYNAMIC, Granularity.MODULE, dynamicFile.absolutePath) - staticFile.absolutePath in cacheDirContents -> Cache(Kind.STATIC, Granularity.MODULE, staticFile.absolutePath) + 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) } } 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 ba246d8416e..d6178d279ae 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 @@ -70,6 +70,9 @@ val CompilerOutputKind.isCache: Boolean get() = this == CompilerOutputKind.STATIC_CACHE || this == CompilerOutputKind.DYNAMIC_CACHE || this == CompilerOutputKind.PRELIMINARY_CACHE +val KonanConfig.involvesCodegen: Boolean + get() = produce != CompilerOutputKind.LIBRARY && produce != CompilerOutputKind.PRELIMINARY_CACHE && !omitFrameworkBinary + internal fun llvmIrDumpCallback(state: ActionState, module: IrModuleFragment, context: Context) { module.let{} if (state.beforeOrAfter == BeforeOrAfter.AFTER && state.phase.name in context.configuration.getList(KonanConfigKeys.SAVE_LLVM_IR)) { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index f3e19824280..d4a27c27c31 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -143,7 +143,7 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) { lazyValues[member] = newValue } - private val localClassNames = mutableMapOf() + val localClassNames = mutableMapOf() fun getLocalClassName(container: IrAttributeContainer): String? = localClassNames[container.attributeOwnerId] @@ -227,22 +227,32 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) { var llvmModule: LLVMModuleRef? = null set(module) { - if (field != null) { - throw Error("Another LLVMModule in the context.") - } field = module!! llvm = Llvm(this, module) debugInfo = DebugInfo(this) } + lateinit var runtime: Runtime + + private var runtimeDisposed = false + + fun disposeRuntime() { + if (runtimeDisposed) return + if (::runtime.isInitialized) { + LLVMDisposeTargetData(runtime.targetData) + LLVMDisposeModule(runtime.llvmModule) + } + runtimeDisposed = true + } + + lateinit var llvmImports: LlvmImports lateinit var llvm: Llvm - val llvmImports: LlvmImports = Llvm.ImportsImpl(this) lateinit var llvmDeclarations: LlvmDeclarations lateinit var bitcodeFileName: String lateinit var library: KonanLibraryLayout - private var llvmDisposed = false + var llvmDisposed = false fun disposeLlvm() { if (llvmDisposed) return @@ -250,15 +260,10 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) { LLVMDisposeDIBuilder(debugInfo.builder) if (llvmModule != null) LLVMDisposeModule(llvmModule) - if (::llvm.isInitialized) { - LLVMDisposeTargetData(llvm.runtime.targetData) - LLVMDisposeModule(llvm.runtime.llvmModule) - } - tryDisposeLLVMContext() llvmDisposed = true } - val cStubsManager = CStubsManager(config.target) + var cStubsManager = CStubsManager(config.target) val coverage = CoverageManager(this) @@ -354,7 +359,7 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) { val llvmModuleSpecification: LlvmModuleSpecification by lazy { when { config.produce.isCache -> - CacheLlvmModuleSpecification(config.cachedLibraries, config.libraryToCache!!.klib) + CacheLlvmModuleSpecification(this, config.cachedLibraries, config.libraryToCache!!) else -> DefaultLlvmModuleSpecification(config.cachedLibraries) } } @@ -370,6 +375,8 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) { val calledFromExportedInlineFunctions = mutableSetOf() val constructedFromExportedInlineFunctions = mutableSetOf() + val enumEntriesMaps = mutableMapOf>() + val targetAbiInfo: TargetAbiInfo by lazy { when { config.target == KonanTarget.MINGW_X64 -> { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt index 45a02f064e0..03691b26a93 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt @@ -410,13 +410,28 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration internal val libraryToCache: PartialCacheInfo? get() = cacheSupport.libraryToCache - internal val producePerFileCache = libraryToCache?.strategy is CacheDeserializationStrategy.SingleFile + internal val producePerFileCache + get() = libraryToCache?.strategy is CacheDeserializationStrategy.SingleFile - val outputFiles = - OutputFiles(configuration.get(KonanConfigKeys.OUTPUT) ?: cacheSupport.tryGetImplicitOutput(), - target, produce, producePerFileCache) + internal val produceBatchedPerFileCache + get() = configuration.get(KonanConfigKeys.MAKE_PER_FILE_CACHE) == true + && configuration.get(KonanConfigKeys.BATCHED_PER_FILE_CACHE_BUILD) != false - val tempFiles = TempFiles(outputFiles.outputName, configuration.get(KonanConfigKeys.TEMPORARY_FILES_DIR)) + lateinit var outputFiles: OutputFiles + + lateinit var tempFiles: TempFiles + + init { + recreateOutputFiles(producePerFileCache) + } + + fun recreateOutputFiles(explicitlyProducePerFileCache: Boolean) { + val outputPath = configuration.get(KonanConfigKeys.OUTPUT) ?: cacheSupport.tryGetImplicitOutput(explicitlyProducePerFileCache) + outputFiles = OutputFiles(outputPath, target, produce, explicitlyProducePerFileCache) + tempFiles = TempFiles(outputFiles.outputName, configuration.get(KonanConfigKeys.TEMPORARY_FILES_DIR)) + + println("ZZZ: ${outputFiles.outputName}") + } val outputFile get() = outputFiles.mainFileName diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt index 4daa1989120..7117bdaacf9 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt @@ -44,10 +44,12 @@ class KonanConfigKeys { = CompilerConfigurationKey.create>("paths to directories containing caches") val CACHED_LIBRARIES: CompilerConfigurationKey> = CompilerConfigurationKey.create>("mapping from library paths to cache paths") - val FILE_TO_CACHE: CompilerConfigurationKey - = CompilerConfigurationKey.create("which file should be compiled to cache") + val FILES_TO_CACHE: CompilerConfigurationKey> + = CompilerConfigurationKey.create>("which files should be compiled to cache") val MAKE_PER_FILE_CACHE: CompilerConfigurationKey = CompilerConfigurationKey.create("make per-file cache") + val BATCHED_PER_FILE_CACHE_BUILD: CompilerConfigurationKey + = CompilerConfigurationKey.create("build per-file caches in batches") val FRAMEWORK_IMPORT_HEADERS: CompilerConfigurationKey> = CompilerConfigurationKey.create>("headers imported to framework header") val FRIEND_MODULES: CompilerConfigurationKey> diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanDriver.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanDriver.kt index a18713b9f9a..25e25e9b208 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanDriver.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanDriver.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.backend.common.phaser.CompilerPhase import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel import org.jetbrains.kotlin.backend.common.serialization.codedInputStream import org.jetbrains.kotlin.backend.common.serialization.proto.IrFile +import org.jetbrains.kotlin.backend.konan.llvm.tryDisposeLLVMContext import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment @@ -29,7 +30,7 @@ class KonanDriver(val project: Project, val environment: KotlinCoreEnvironment, fun run() { val fileNames = configuration.get(KonanConfigKeys.LIBRARY_TO_ADD_TO_CACHE)?.let { libPath -> if (configuration.get(KonanConfigKeys.MAKE_PER_FILE_CACHE) != true) - null + configuration.get(KonanConfigKeys.FILES_TO_CACHE) else { val lib = createKonanLibrary(File(libPath), "default", null, true) (0 until lib.fileCount()).map { fileIndex -> @@ -42,8 +43,14 @@ class KonanDriver(val project: Project, val environment: KotlinCoreEnvironment, if (fileNames == null) { KonanConfig(project, configuration).runTopLevelPhases() } else { - fileNames.forEach { buildFileCache(it, CompilerOutputKind.PRELIMINARY_CACHE) } - fileNames.forEach { buildFileCache(it, configuration.get(KonanConfigKeys.PRODUCE)!!) } + configuration.put(KonanConfigKeys.MAKE_PER_FILE_CACHE, true) + if (configuration.get(KonanConfigKeys.BATCHED_PER_FILE_CACHE_BUILD) == false) { + fileNames.forEach { buildFileCache(it, CompilerOutputKind.PRELIMINARY_CACHE) } + fileNames.forEach { buildFileCache(it, configuration.get(KonanConfigKeys.PRODUCE)!!) } + } else { + configuration.put(KonanConfigKeys.FILES_TO_CACHE, fileNames) + KonanConfig(project, configuration).runTopLevelPhases() + } } } @@ -51,8 +58,7 @@ class KonanDriver(val project: Project, val environment: KotlinCoreEnvironment, val phaseConfig = configuration.get(CLIConfigurationKeys.PHASE_CONFIG)!! val subConfiguration = configuration.copy() subConfiguration.put(KonanConfigKeys.PRODUCE, cacheKind) - subConfiguration.put(KonanConfigKeys.FILE_TO_CACHE, fileName) - subConfiguration.put(KonanConfigKeys.MAKE_PER_FILE_CACHE, false) + subConfiguration.put(KonanConfigKeys.FILES_TO_CACHE, listOf(fileName)) subConfiguration.put(CLIConfigurationKeys.PHASE_CONFIG, phaseConfig.toBuilder().build()) KonanConfig(project, subConfiguration).runTopLevelPhases() } @@ -102,6 +108,8 @@ private fun runTopLevelPhases(konanConfig: KonanConfig, environment: KotlinCoreE toplevelPhase.cast>().invokeToplevel(context.phaseConfig, context, Unit) } finally { context.disposeLlvm() + context.disposeRuntime() + tryDisposeLLVMContext() } } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt index 125b4c8eb3c..918a65b5428 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt @@ -82,8 +82,8 @@ internal fun makeKonanModuleOpPhase( actions = modulePhaseActions ) -internal val specialBackendChecksPhase = konanUnitPhase( - op = { irModule!!.files.forEach { SpecialBackendChecksTraversal(this).lower(it) } }, +internal val specialBackendChecksPhase = makeKonanModuleLoweringPhase( + ::SpecialBackendChecksTraversal, name = "SpecialBackendChecks", description = "Special backend checks" ) @@ -151,6 +151,18 @@ internal val inventNamesForLocalClasses = makeKonanFileLoweringPhase( internal val extractLocalClassesFromInlineBodies = makeKonanFileOpPhase( { context, irFile -> + irFile.acceptChildrenVoid(object : IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitFunction(declaration: IrFunction) { + if (declaration.isInline) + context.inlineFunctionsSupport.saveNonLoweredInlineFunction(declaration) + declaration.acceptChildrenVoid(this) + } + }) + LocalClassesInInlineLambdasLowering(context).lower(irFile) LocalClassesInInlineFunctionsLowering(context).lower(irFile) LocalClassesExtractionFromInlineFunctionsLowering(context).lower(irFile) @@ -168,18 +180,6 @@ internal val wrapInlineDeclarationsWithReifiedTypeParametersLowering = makeKonan internal val inlinePhase = makeKonanFileOpPhase( { context, irFile -> - irFile.acceptChildrenVoid(object : IrElementVisitorVoid { - override fun visitElement(element: IrElement) { - element.acceptChildrenVoid(this) - } - - override fun visitFunction(declaration: IrFunction) { - if (declaration.isInline) - context.inlineFunctionsSupport.getNonLoweredInlineFunction(declaration) - declaration.acceptChildrenVoid(this) - } - }) - FunctionInlining(context, NativeInlineFunctionResolver(context)).lower(irFile) }, name = "Inline", @@ -286,12 +286,10 @@ internal val rangeContainsLoweringPhase = makeKonanFileLoweringPhase( description = "Optimizes calls to contains() for ClosedRanges" ) -internal val functionsWithoutBoundCheck = makeKonanModuleOpPhase( +internal val functionsWithoutBoundCheck = konanUnitPhase( name = "FunctionsWithoutBoundCheckGenerator", description = "Functions without bounds check generation", - op = { context, _ -> - FunctionsWithoutBoundCheckGenerator(context).generate() - } + op = { FunctionsWithoutBoundCheckGenerator(this).generate() } ) internal val forLoopsPhase = makeKonanFileOpPhase( 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 f461470e06f..f44d4c3ebc9 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 @@ -51,7 +51,7 @@ internal class CacheStorage(val context: Context) { context.config.outputFiles.prepareTempDirectories() if (!isPreliminaryCache) saveCacheBitcodeDependencies() - if (isPreliminaryCache || context.configuration.get(KonanConfigKeys.FILE_TO_CACHE) == null) { + if (isPreliminaryCache || !context.config.producePerFileCache || context.config.produceBatchedPerFileCache) { saveInlineFunctionBodies() saveClassFields() } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LlvmModuleSpecificationImpl.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LlvmModuleSpecificationImpl.kt index 6d935f013e0..06b9549b4cd 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LlvmModuleSpecificationImpl.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LlvmModuleSpecificationImpl.kt @@ -6,8 +6,10 @@ package org.jetbrains.kotlin.backend.konan import org.jetbrains.kotlin.backend.konan.ir.konanLibrary +import org.jetbrains.kotlin.builtins.FunctionInterfacePackageFragment import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.util.fileOrNull import org.jetbrains.kotlin.ir.util.getPackageFragment import org.jetbrains.kotlin.library.KotlinLibrary @@ -41,10 +43,19 @@ internal class DefaultLlvmModuleSpecification(cachedLibraries: CachedLibraries) } internal class CacheLlvmModuleSpecification( + private val context: Context, cachedLibraries: CachedLibraries, - private val libraryToCache: KotlinLibrary + private val libraryToCache: PartialCacheInfo ) : LlvmModuleSpecificationBase(cachedLibraries) { override val isFinal = false - override fun containsLibrary(library: KotlinLibrary): Boolean = library == libraryToCache + override fun containsLibrary(library: KotlinLibrary): Boolean = library == libraryToCache.klib + + override fun containsDeclaration(declaration: IrDeclaration): Boolean { + if (context.shouldDefineFunctionClasses && declaration.getPackageFragment().isFunctionInterfaceFile) + return true + if (!super.containsDeclaration(declaration)) return false + return (libraryToCache.strategy as? CacheDeserializationStrategy.SingleFile) + ?.filePath.let { it == null || it == declaration.fileOrNull?.path } + } } 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 5e781ba3de9..a9b0f6c30d3 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 @@ -49,7 +49,7 @@ class OutputFiles(outputPath: String?, target: KonanTarget, val produce: Compile outputName.substring(0, outputName.lastIndexOf(File.separatorChar) /* skip [PER_FILE_CACHE_BINARY_LEVEL_DIR_NAME]*/) else null - val perFileCacheFileName = pathToPerFileCache?.let { File(it).name } + val perFileCacheFileName = File(pathToPerFileCache ?: outputName).absoluteFile.name val cacheFileName = File((pathToPerFileCache ?: outputName).fullOutputName()).absoluteFile.name 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 8db4ad8ec20..15bb5f5e188 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 @@ -18,6 +18,7 @@ import org.jetbrains.kotlin.backend.konan.lower.ExpectToActualDefaultValueCopier import org.jetbrains.kotlin.backend.konan.lower.SamSuperTypesChecker import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport import org.jetbrains.kotlin.backend.konan.serialization.* +import org.jetbrains.kotlin.builtins.FunctionInterfacePackageFragment import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.languageVersionSettings import org.jetbrains.kotlin.ir.declarations.* @@ -67,7 +68,7 @@ internal fun fileValidationCallback(state: ActionState, irFile: IrFile, context: internal fun konanUnitPhase( name: String, description: String, - prerequisite: Set = emptySet(), + prerequisite: Set> = emptySet(), op: Context.() -> Unit ) = namedOpUnitPhase(name, description, prerequisite, op) @@ -328,6 +329,49 @@ internal val dependenciesLowerPhase = NamedCompilerPhase( } }) +internal val umbrellaCompilation = NamedCompilerPhase( + name = "UmbrellaCompilation", + description = "A batched compilation with shared FE and ME phases", + prerequisite = emptySet(), + lower = object : CompilerPhase { + override fun invoke(phaseConfig: PhaseConfig, phaserState: PhaserState, context: Context, input: Unit) { + val module = context.irModules.values.single() + + val files = module.files.toList() + module.files.clear() + val functionInterfaceFiles = files.filter { it.isFunctionInterfaceFile } + + for (file in files) { + if (file.isFunctionInterfaceFile) continue + + // Pretend we're about to create a single file cache. + context.config.cacheSupport.libraryToCache!!.strategy = + CacheDeserializationStrategy.SingleFile(file.path, file.fqName.asString()) + context.config.recreateOutputFiles(explicitlyProducePerFileCache = false) + + module.files += file + if (context.shouldDefineFunctionClasses) + module.files += functionInterfaceFiles + + entireBackend.invoke(phaseConfig, phaserState, context, Unit) + + module.files.clear() + context.irModule!!.files.clear() // [dependenciesLowerPhase] puts all files to [context.irModule] for codegen. + + context.inlineFunctionBodies.clear() + context.classFields.clear() + context.calledFromExportedInlineFunctions.clear() + context.constructedFromExportedInlineFunctions.clear() + context.localClassNames.clear() + context.testCasesToDump.clear() + context.mapping.loweredInlineFunctions.clear() + context.cStubsManager = CStubsManager(context.config.target) + } + + module.files += files + } + }) + internal val dumpTestsPhase = makeCustomPhase( name = "dumpTestsPhase", description = "Dump the list of all available tests", @@ -408,12 +452,10 @@ private val bitcodePostprocessingPhase = NamedCompilerPhase( rewriteExternalCallsCheckerGlobals ) -private val backendCodegen = namedUnitPhase( +private val backendCodegen = NamedCompilerPhase( name = "Backend codegen", description = "Backend code generation", - lower = takeFromContext { it.irModule!! } then - entryPointPhase then - functionsWithoutBoundCheck then + lower = entryPointPhase then allLoweringsPhase then // Lower current module first. dependenciesLowerPhase then // Then lower all libraries in topological order. // With that we guarantee that inline functions are unlowered while being inlined. @@ -422,36 +464,53 @@ private val backendCodegen = namedUnitPhase( verifyBitcodePhase then printBitcodePhase then linkBitcodeDependenciesPhase then - bitcodePostprocessingPhase then - unitSink() + bitcodePostprocessingPhase +) + +private val entireBackend = NamedCompilerPhase( + name = "EntireBackend", + description = "Entire backend", + lower = createLLVMImportsPhase then + buildAdditionalCacheInfoPhase then + takeFromContext { it.irModule!! } then + specialBackendChecksPhase then + backendCodegen then + unitSink() then + saveAdditionalCacheInfoPhase then + produceOutputPhase then + disposeLLVMPhase then + objectFilesPhase then + linkerPhase then + finalizeCachePhase +) + +private val middleEnd = NamedCompilerPhase( + name = "MiddleEnd", + description = "Build and prepare IR for back end", + lower = createSymbolTablePhase then + objCExportPhase then + buildCExportsPhase then + psiToIrPhase then + destroySymbolTablePhase then + copyDefaultValuesToActualPhase then + checkSamSuperTypesPhase then + serializerPhase then + functionsWithoutBoundCheck +) + +private val singleCompilation = NamedCompilerPhase( + name = "SingleCompilation", + description = "Single compilation", + lower = entireBackend ) // Have to hide Context as type parameter in order to expose toplevelPhase outside of this module. val toplevelPhase: CompilerPhase<*, Unit, Unit> = namedUnitPhase( name = "Compiler", description = "The whole compilation process", - lower = createSymbolTablePhase then - objCExportPhase then - buildCExportsPhase then - psiToIrPhase then - buildAdditionalCacheInfoPhase then - destroySymbolTablePhase then - copyDefaultValuesToActualPhase then - checkSamSuperTypesPhase then - serializerPhase then - specialBackendChecksPhase then - namedUnitPhase( - name = "Backend", - description = "All backend", - lower = backendCodegen then - produceOutputPhase then - disposeLLVMPhase then - unitSink() - ) then - saveAdditionalCacheInfoPhase then - objectFilesPhase then - linkerPhase then - finalizeCachePhase + lower = middleEnd then + singleCompilation then + umbrellaCompilation ) internal fun PhaseConfig.disableIf(phase: AnyNamedPhase, condition: Boolean) { @@ -472,6 +531,9 @@ internal fun PhaseConfig.konanPhasesConfig(config: KonanConfig) { disable(localEscapeAnalysisPhase) + disableIf(singleCompilation, config.produceBatchedPerFileCache) + disableUnless(umbrellaCompilation, config.produceBatchedPerFileCache) + // Don't serialize anything to a final executable. disableUnless(serializerPhase, config.produce == CompilerOutputKind.LIBRARY) disableUnless(entryPointPhase, config.produce == CompilerOutputKind.PROGRAM) @@ -480,7 +542,7 @@ internal fun PhaseConfig.konanPhasesConfig(config: KonanConfig) { disableUnless(finalizeCachePhase, config.produce.isCache) disableUnless(exportInternalAbiPhase, config.produce.isCache) disableUnless(buildCExportsPhase, config.produce.isNativeLibrary) - disableIf(backendCodegen, config.produce == CompilerOutputKind.LIBRARY || config.omitFrameworkBinary || config.produce == CompilerOutputKind.PRELIMINARY_CACHE) + disableUnless(backendCodegen, config.involvesCodegen) disableUnless(checkExternalCallsPhase, getBoolean(KonanConfigKeys.CHECK_EXTERNAL_CALLS)) disableUnless(rewriteExternalCallsCheckerGlobals, getBoolean(KonanConfigKeys.CHECK_EXTERNAL_CALLS)) disableUnless(stringConcatenationTypeNarrowingPhase, config.optimizationsEnabled) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt index 5e46c631ec8..fe632a33fd4 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt @@ -405,9 +405,18 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) { * All fields of the class instance. * The order respects the class hierarchy, i.e. a class [fields] contains superclass [fields] as a prefix. */ - val fields: List by lazy { + val fields: List + get() = fieldsInternal.map { fieldInfo -> + val mappedField = fieldInfo.irField?.let { context.mapping.lateInitFieldToNullableField[it] ?: it } + if (mappedField == fieldInfo.irField) + fieldInfo + else + mappedField!!.toFieldInfo().also { it.index = fieldInfo.index } + } + + private val fieldsInternal: List by lazy { val superClass = irClass.getSuperClassNotAny() - val superFields = if (superClass != null) context.getLayoutBuilder(superClass).fields else emptyList() + val superFields = if (superClass != null) context.getLayoutBuilder(superClass).fieldsInternal else emptyList() val declaredFields = getDeclaredFields() val sortedDeclaredFields = if (irClass.hasAnnotation(KonanFqNames.noReorderFields)) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt index 1289ad47114..feae3f4b7e3 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt @@ -21,10 +21,7 @@ import org.jetbrains.kotlin.backend.konan.optimizations.* import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.util.defaultType -import org.jetbrains.kotlin.ir.util.isFunction -import org.jetbrains.kotlin.ir.util.isReal -import org.jetbrains.kotlin.ir.util.parentAsClass +import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.* import org.jetbrains.kotlin.konan.target.Architecture import org.jetbrains.kotlin.util.OperatorNameConventions @@ -39,7 +36,12 @@ internal val contextLLVMSetupPhase = makeKonanModuleOpPhase( // (see Llvm class in ContextUtils) // Which in turn is determined by the clang flags // used to compile runtime.bc. + // TODO llvmContext = LLVMContextCreate()!! + context.llvmDisposed = false + + context.runtime = Runtime(context.config.distribution.compilerInterface(context.config.target)) + val llvmModule = LLVMModuleCreateWithNameInContext("out", llvmContext)!! context.llvmModule = llvmModule context.debugInfo.builder = LLVMCreateDIBuilder(llvmModule) @@ -72,12 +74,24 @@ internal val createLLVMDeclarationsPhase = makeKonanModuleOpPhase( } ) +internal val createLLVMImportsPhase = namedUnitPhase( + name = "CreateLLVMImoorts", + description = "Create LLVM imports", + lower = object : CompilerPhase { + override fun invoke(phaseConfig: PhaseConfig, phaserState: PhaserState, context: Context, input: Unit) { + context.llvmImports = Llvm.ImportsImpl(context) + } + } +) + internal val disposeLLVMPhase = namedUnitPhase( name = "DisposeLLVM", description = "Dispose LLVM", lower = object : CompilerPhase { override fun invoke(phaseConfig: PhaseConfig, phaserState: PhaserState, context: Context, input: Unit) { context.disposeLlvm() + context.disposeRuntime() + tryDisposeLLVMContext() } } ) 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 f5b76cf815b..68534c8e521 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 @@ -444,8 +444,7 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) : Runti private val target = context.config.target - val runtimeFile = context.config.distribution.compilerInterface(target) - override val runtime = Runtime(runtimeFile) // TODO: dispose + override val runtime get() = context.runtime val targetTriple = runtime.target 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 3835168f52a..d3ef60e1bfa 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 @@ -2858,7 +2858,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map context.config.moduleId.moduleConstructorName library == context.config.libraryToCache?.klib && context.config.producePerFileCache -> - fileCtorName(library.uniqueName, context.config.outputFiles.perFileCacheFileName!!) + fileCtorName(library.uniqueName, context.config.outputFiles.perFileCacheFileName) else -> library.moduleConstructorName } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt index e4d28f7ed33..77ec91542a3 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt @@ -12,12 +12,8 @@ import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.descriptors.ClassLayoutBuilder import org.jetbrains.kotlin.backend.konan.descriptors.isTypedIntrinsic import org.jetbrains.kotlin.backend.konan.ir.* -import org.jetbrains.kotlin.backend.konan.llvm.KonanBinaryInterface.functionName import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.expressions.IrBlockBody -import org.jetbrains.kotlin.ir.expressions.IrConst -import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeInlineFunctionResolver.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeInlineFunctionResolver.kt index 70e9c780b7b..3c84aeb9e87 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeInlineFunctionResolver.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeInlineFunctionResolver.kt @@ -22,8 +22,19 @@ import org.jetbrains.kotlin.name.Name internal class InlineFunctionsSupport(mapping: NativeMapping) { private val notLoweredInlineFunctions = mapping.notLoweredInlineFunctions - fun getNonLoweredInlineFunction(function: IrFunction) = notLoweredInlineFunctions.getOrPut(function.symbol) { - function.deepCopyWithVariables().also { it.patchDeclarationParents(function.parent) } + + fun saveNonLoweredInlineFunction(function: IrFunction) { + getNonLoweredInlineFunction(function, copy = false) + } + + fun getNonLoweredInlineFunction(function: IrFunction, copy: Boolean): IrFunction { + val notLoweredInlineFunction = notLoweredInlineFunctions.getOrPut(function.symbol) { + function.deepCopyWithVariables().also { it.patchDeclarationParents(function.parent) } + } + return if (copy) + notLoweredInlineFunction.deepCopyWithVariables().also { it.patchDeclarationParents(function.parent) } + else + notLoweredInlineFunction } } @@ -35,11 +46,11 @@ internal class NativeInlineFunctionResolver(override val context: Context) : Def context.mapping.loweredInlineFunctions[function]?.let { return it.irFunction } val packageFragment = function.getPackageFragment() - val notLoweredFunction = if (packageFragment !is IrExternalPackageFragment) { - context.inlineFunctionsSupport.getNonLoweredInlineFunction(function).also { + val (possiblyLoweredFunction, shouldLower) = if (packageFragment !is IrExternalPackageFragment) { + context.inlineFunctionsSupport.getNonLoweredInlineFunction(function, copy = context.config.produceBatchedPerFileCache).also { context.mapping.loweredInlineFunctions[function] = InlineFunctionOriginInfo(it, packageFragment as IrFile, function.startOffset, function.endOffset) - } + } to true } else { // The function is from Lazy IR, get its body from the IR linker. val moduleDescriptor = packageFragment.packageFragmentDescriptor.containingDeclaration @@ -48,35 +59,38 @@ internal class NativeInlineFunctionResolver(override val context: Context) : Def require(context.config.cachedLibraries.isLibraryCached(moduleDeserializer.klib)) { "No IR and no cache for ${function.render()}" } - context.mapping.loweredInlineFunctions[function] = moduleDeserializer.deserializeInlineFunction(function) - function + val (shouldLower, deserializedInlineFunction) = moduleDeserializer.deserializeInlineFunction(function) + context.mapping.loweredInlineFunctions[function] = deserializedInlineFunction + function to shouldLower } - val body = notLoweredFunction.body ?: return notLoweredFunction + if (!shouldLower) return possiblyLoweredFunction - PreInlineLowering(context).lower(body, notLoweredFunction, context.mapping.loweredInlineFunctions[function]!!.irFile) + val body = possiblyLoweredFunction.body ?: return possiblyLoweredFunction - ArrayConstructorLowering(context).lower(body, notLoweredFunction) + PreInlineLowering(context).lower(body, possiblyLoweredFunction, context.mapping.loweredInlineFunctions[function]!!.irFile) - NullableFieldsForLateinitCreationLowering(context).lowerWithLocalDeclarations(notLoweredFunction) - NullableFieldsDeclarationLowering(context).lowerWithLocalDeclarations(notLoweredFunction) - LateinitUsageLowering(context).lower(body, notLoweredFunction) + ArrayConstructorLowering(context).lower(body, possiblyLoweredFunction) - SharedVariablesLowering(context).lower(body, notLoweredFunction) + NullableFieldsForLateinitCreationLowering(context).lowerWithLocalDeclarations(possiblyLoweredFunction) + NullableFieldsDeclarationLowering(context).lowerWithLocalDeclarations(possiblyLoweredFunction) + LateinitUsageLowering(context).lower(body, possiblyLoweredFunction) - OuterThisLowering(context).lower(notLoweredFunction) + SharedVariablesLowering(context).lower(body, possiblyLoweredFunction) - LocalClassesInInlineLambdasLowering(context).lower(body, notLoweredFunction) + OuterThisLowering(context).lower(possiblyLoweredFunction) - if (packageFragment !is IrExternalPackageFragment) { + LocalClassesInInlineLambdasLowering(context).lower(body, possiblyLoweredFunction) + + if (context.llvmModuleSpecification.containsDeclaration(function)) { // Do not extract local classes off of inline functions from cached libraries. - LocalClassesInInlineFunctionsLowering(context).lower(body, notLoweredFunction) - LocalClassesExtractionFromInlineFunctionsLowering(context).lower(body, notLoweredFunction) + LocalClassesInInlineFunctionsLowering(context).lower(body, possiblyLoweredFunction) + LocalClassesExtractionFromInlineFunctionsLowering(context).lower(body, possiblyLoweredFunction) } - WrapInlineDeclarationsWithReifiedTypeParametersLowering(context).lower(body, notLoweredFunction) + WrapInlineDeclarationsWithReifiedTypeParametersLowering(context).lower(body, possiblyLoweredFunction) - return notLoweredFunction + return possiblyLoweredFunction } private fun DeclarationTransformer.lowerWithLocalDeclarations(function: IrFunction) { 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 663c780c870..fd8f9779394 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 @@ -307,6 +307,8 @@ object KonanFakeOverrideClassFilter : FakeOverrideClassFilter { } } +internal data class DeserializedInlineFunction(val firstAccess: Boolean, val function: InlineFunctionOriginInfo) + internal class KonanIrLinker( private val currentModule: ModuleDescriptor, override val translationPluginContext: TranslationPluginContext?, @@ -387,14 +389,18 @@ internal class KonanIrLinker( else -> null } - fun getExternalDeclarationFileName(declaration: IrDeclaration) = with(declaration) { - val externalPackageFragment = getPackageFragment() as? IrExternalPackageFragment - ?: error("Expected an external package fragment for ${render()}") - val moduleDescriptor = externalPackageFragment.packageFragmentDescriptor.containingDeclaration - val moduleDeserializer = moduleDeserializers[moduleDescriptor] - ?: error("No module deserializer for $moduleDescriptor") - val idSig = moduleDeserializer.descriptorSignatures[descriptor] ?: error("No signature for $descriptor") - idSig.topLevelSignature().fileSignature()?.fileName ?: error("No file for $idSig") + fun getExternalDeclarationFileName(declaration: IrDeclaration) = when (val packageFragment = declaration.getPackageFragment()) { + is IrFile -> packageFragment.path + + 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") + } + + else -> error("Unknown package fragment kind ${packageFragment::class.java}") } private val IrClass.firstNonClassParent: IrDeclarationParent @@ -637,7 +643,16 @@ internal class KonanIrLinker( } } - fun deserializeInlineFunction(function: IrFunction): InlineFunctionOriginInfo { + private val deserializedInlineFunctions = mutableMapOf() + + fun deserializeInlineFunction(function: IrFunction): DeserializedInlineFunction { + deserializedInlineFunctions[function]?.let { return DeserializedInlineFunction(firstAccess = false, it) } + val result = deserializeInlineFunctionInternal(function) + deserializedInlineFunctions[function] = result + return DeserializedInlineFunction(firstAccess = true, result) + } + + private fun deserializeInlineFunctionInternal(function: IrFunction): InlineFunctionOriginInfo { val packageFragment = function.getPackageFragment() as? IrExternalPackageFragment ?: error("Expected an external package fragment for ${function.render()}") if (function.parents.any { (it as? IrFunction)?.isInline == true }) {