[K/N][IR][codegen][caches] Speed-up of per-file caches

This commit is contained in:
Igor Chevdar
2022-07-01 08:30:24 +03:00
committed by Space
parent aba8d8a859
commit 77f24a22dd
22 changed files with 339 additions and 148 deletions
@@ -219,11 +219,14 @@ class K2NativeCompilerArguments : CommonCompilerArguments() {
description = "Path to file to cache", description = "Path to file to cache",
delimiter = "" delimiter = ""
) )
var fileToCache: String? = null var filesToCache: Array<String>? = null
@Argument(value = "-Xmake-per-file-cache", description = "Force compiler to produce per-file cache") @Argument(value = "-Xmake-per-file-cache", description = "Force compiler to produce per-file cache")
var makePerFileCache: Boolean = false 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") @Argument(value = "-Xexport-kdoc", description = "Export KDoc in framework header")
var exportKDoc: Boolean = false var exportKDoc: Boolean = false
@@ -285,11 +285,20 @@ class K2Native : CLICompiler<K2NativeCompilerArguments>() {
libraryToAddToCache?.let { put(LIBRARY_TO_ADD_TO_CACHE, it) } libraryToAddToCache?.let { put(LIBRARY_TO_ADD_TO_CACHE, it) }
put(CACHE_DIRECTORIES, cacheDirectories) put(CACHE_DIRECTORIES, cacheDirectories)
put(CACHED_LIBRARIES, parseCachedLibraries(arguments, configuration)) put(CACHED_LIBRARIES, parseCachedLibraries(arguments, configuration))
val fileToCache = arguments.fileToCache val filesToCache = arguments.filesToCache
if (outputKind == CompilerOutputKind.PRELIMINARY_CACHE && fileToCache == null) if (outputKind == CompilerOutputKind.PRELIMINARY_CACHE && filesToCache.isNullOrEmpty())
configuration.report(ERROR, "preliminary_cache only supported for per-file caches") 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) 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 { parseShortModuleName(arguments, configuration, outputKind)?.let {
put(SHORT_MODULE_NAME, it) put(SHORT_MODULE_NAME, it)
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.backend.konan package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.konan.descriptors.findPackage 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.FunctionClassDescriptor
import org.jetbrains.kotlin.builtins.functions.FunctionClassKind import org.jetbrains.kotlin.builtins.functions.FunctionClassKind
import org.jetbrains.kotlin.descriptors.* 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") internal object DECLARATION_ORIGIN_FUNCTION_CLASS : IrDeclarationOriginImpl("DECLARATION_ORIGIN_FUNCTION_CLASS")
val IrPackageFragment.isFunctionInterfaceFile get() = packageFragmentDescriptor is FunctionInterfacePackageFragment
abstract class KonanIrAbstractDescriptorBasedFunctionFactory : IrProvider, IrAbstractDescriptorBasedFunctionFactory() abstract class KonanIrAbstractDescriptorBasedFunctionFactory : IrProvider, IrAbstractDescriptorBasedFunctionFactory()
internal class LazyIrFunctionFactory( internal class LazyIrFunctionFactory(
@@ -34,17 +34,25 @@ sealed class CacheDeserializationStrategy {
override fun contains(fqName: FqName, fileName: String) = true 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 override fun contains(filePath: String) = filePath == this.filePath
lateinit var fqName: String
override fun contains(fqName: FqName, fileName: String) = override fun contains(fqName: FqName, fileName: String) =
fqName.asString() == this.fqName && File(filePath).name == fileName fqName.asString() == this.fqName && File(filePath).name == fileName
} }
class MultipleFiles(filePaths: List<String>, fqNames: List<String>) : 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( class CacheSupport(
private val configuration: CompilerConfiguration, private val configuration: CompilerConfiguration,
@@ -64,7 +72,7 @@ class CacheSupport(
?: configuration.reportCompilationError("cache directory $it is not found or not a directory") ?: 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 val libraryToCache = libraryToCache ?: return null
// Put the resulting library in the first cache directory. // Put the resulting library in the first cache directory.
val cacheDirectory = implicitCacheDirectories.firstOrNull() ?: return null val cacheDirectory = implicitCacheDirectories.firstOrNull() ?: return null
@@ -75,19 +83,13 @@ class CacheSupport(
else else
CachedLibraries.getPerFileCachedLibraryName(libraryToCache.klib) CachedLibraries.getPerFileCachedLibraryName(libraryToCache.klib)
) )
if (singleFileStrategy == null) val singleFilePath = singleFileStrategy?.filePath
return baseLibraryCacheDirectory.absolutePath ?: return baseLibraryCacheDirectory.absolutePath
val fileToCache = singleFileStrategy.filePath val fqName = singleFileStrategy.fqName
val fileProtos = Array<ProtoFile>(libraryToCache.klib.fileCount()) { val fileCacheDirectory = baseLibraryCacheDirectory.child(cacheFileId(fqName, singleFilePath))
ProtoFile.parseFrom(libraryToCache.klib.file(it).codedInputStream, ExtensionRegistryLite.newInstance()) if (!explicitlyProducePerFileCache)
} return fileCacheDirectory.absolutePath
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 contentDirName = if (produce == CompilerOutputKind.PRELIMINARY_CACHE) val contentDirName = if (produce == CompilerOutputKind.PRELIMINARY_CACHE)
CachedLibraries.PER_FILE_CACHE_IR_LEVEL_DIR_NAME CachedLibraries.PER_FILE_CACHE_IR_LEVEL_DIR_NAME
else CachedLibraries.PER_FILE_CACHE_BINARY_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) if (libraryCache != null && libraryCache.granularity == CachedLibraries.Granularity.MODULE)
null null
else { else {
val fileToCache = configuration.get(KonanConfigKeys.FILE_TO_CACHE) val filesToCache = configuration.get(KonanConfigKeys.FILES_TO_CACHE)
PartialCacheInfo(libraryToAddToCache, if (fileToCache == null) CacheDeserializationStrategy.WholeModule else CacheDeserializationStrategy.SingleFile(fileToCache))
val strategy = if (filesToCache.isNullOrEmpty())
CacheDeserializationStrategy.WholeModule
else {
val fileProtos = Array<ProtoFile>(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)
} }
} }
@@ -90,22 +90,25 @@ class CachedLibraries(
private val cacheDirsContents = mutableMapOf<String, Set<String>>() private val cacheDirsContents = mutableMapOf<String, Set<String>>()
private fun selectCache(library: KotlinLibrary, cacheDir: File): Cache? { 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. // See Linker.renameOutput why is it ok to have an empty cache directory.
val cacheDirContents = cacheDirsContents.getOrPut(cacheDir.absolutePath) { val cacheDirContents = cacheDirsContents.getOrPut(cacheDir.absolutePath) {
cacheBinaryPartDir.listFilesOrEmpty.map { it.absolutePath }.toSet() cacheDir.listFilesOrEmpty.map { it.absolutePath }.toSet()
} }
if (cacheDirContents.isEmpty()) return null 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 baseName = getCachedLibraryName(library)
val dynamicFile = cacheBinaryPartDir.child(getArtifactName(baseName, CompilerOutputKind.DYNAMIC_CACHE)) val dynamicFile = cacheBinaryPartDir.child(getArtifactName(baseName, CompilerOutputKind.DYNAMIC_CACHE))
val staticFile = cacheBinaryPartDir.child(getArtifactName(baseName, CompilerOutputKind.STATIC_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." + error("Both dynamic and static caches files cannot be in the same directory." +
" Library: ${library.libraryName}, path to cache: ${cacheDir.absolutePath}") " Library: ${library.libraryName}, path to cache: ${cacheDir.absolutePath}")
return when { return when {
dynamicFile.absolutePath in cacheDirContents -> Cache(Kind.DYNAMIC, Granularity.MODULE, dynamicFile.absolutePath) dynamicFile.absolutePath in cacheBinaryPartDirContents -> Cache(Kind.DYNAMIC, Granularity.MODULE, dynamicFile.absolutePath)
staticFile.absolutePath in cacheDirContents -> Cache(Kind.STATIC, Granularity.MODULE, staticFile.absolutePath) staticFile.absolutePath in cacheBinaryPartDirContents -> Cache(Kind.STATIC, Granularity.MODULE, staticFile.absolutePath)
else -> Cache(Kind.STATIC, Granularity.FILE, cacheDir.absolutePath) else -> Cache(Kind.STATIC, Granularity.FILE, cacheDir.absolutePath)
} }
} }
@@ -70,6 +70,9 @@ val CompilerOutputKind.isCache: Boolean
get() = this == CompilerOutputKind.STATIC_CACHE || this == CompilerOutputKind.DYNAMIC_CACHE get() = this == CompilerOutputKind.STATIC_CACHE || this == CompilerOutputKind.DYNAMIC_CACHE
|| this == CompilerOutputKind.PRELIMINARY_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) { internal fun llvmIrDumpCallback(state: ActionState, module: IrModuleFragment, context: Context) {
module.let{} module.let{}
if (state.beforeOrAfter == BeforeOrAfter.AFTER && state.phase.name in context.configuration.getList(KonanConfigKeys.SAVE_LLVM_IR)) { if (state.beforeOrAfter == BeforeOrAfter.AFTER && state.phase.name in context.configuration.getList(KonanConfigKeys.SAVE_LLVM_IR)) {
@@ -143,7 +143,7 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
lazyValues[member] = newValue lazyValues[member] = newValue
} }
private val localClassNames = mutableMapOf<IrAttributeContainer, String>() val localClassNames = mutableMapOf<IrAttributeContainer, String>()
fun getLocalClassName(container: IrAttributeContainer): String? = localClassNames[container.attributeOwnerId] fun getLocalClassName(container: IrAttributeContainer): String? = localClassNames[container.attributeOwnerId]
@@ -227,22 +227,32 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
var llvmModule: LLVMModuleRef? = null var llvmModule: LLVMModuleRef? = null
set(module) { set(module) {
if (field != null) {
throw Error("Another LLVMModule in the context.")
}
field = module!! field = module!!
llvm = Llvm(this, module) llvm = Llvm(this, module)
debugInfo = DebugInfo(this) 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 lateinit var llvm: Llvm
val llvmImports: LlvmImports = Llvm.ImportsImpl(this)
lateinit var llvmDeclarations: LlvmDeclarations lateinit var llvmDeclarations: LlvmDeclarations
lateinit var bitcodeFileName: String lateinit var bitcodeFileName: String
lateinit var library: KonanLibraryLayout lateinit var library: KonanLibraryLayout
private var llvmDisposed = false var llvmDisposed = false
fun disposeLlvm() { fun disposeLlvm() {
if (llvmDisposed) return if (llvmDisposed) return
@@ -250,15 +260,10 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
LLVMDisposeDIBuilder(debugInfo.builder) LLVMDisposeDIBuilder(debugInfo.builder)
if (llvmModule != null) if (llvmModule != null)
LLVMDisposeModule(llvmModule) LLVMDisposeModule(llvmModule)
if (::llvm.isInitialized) {
LLVMDisposeTargetData(llvm.runtime.targetData)
LLVMDisposeModule(llvm.runtime.llvmModule)
}
tryDisposeLLVMContext()
llvmDisposed = true llvmDisposed = true
} }
val cStubsManager = CStubsManager(config.target) var cStubsManager = CStubsManager(config.target)
val coverage = CoverageManager(this) val coverage = CoverageManager(this)
@@ -354,7 +359,7 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
val llvmModuleSpecification: LlvmModuleSpecification by lazy { val llvmModuleSpecification: LlvmModuleSpecification by lazy {
when { when {
config.produce.isCache -> config.produce.isCache ->
CacheLlvmModuleSpecification(config.cachedLibraries, config.libraryToCache!!.klib) CacheLlvmModuleSpecification(this, config.cachedLibraries, config.libraryToCache!!)
else -> DefaultLlvmModuleSpecification(config.cachedLibraries) else -> DefaultLlvmModuleSpecification(config.cachedLibraries)
} }
} }
@@ -370,6 +375,8 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
val calledFromExportedInlineFunctions = mutableSetOf<IrFunction>() val calledFromExportedInlineFunctions = mutableSetOf<IrFunction>()
val constructedFromExportedInlineFunctions = mutableSetOf<IrClass>() val constructedFromExportedInlineFunctions = mutableSetOf<IrClass>()
val enumEntriesMaps = mutableMapOf<IrClass, Map<Name, LoweredEnumEntryDescription>>()
val targetAbiInfo: TargetAbiInfo by lazy { val targetAbiInfo: TargetAbiInfo by lazy {
when { when {
config.target == KonanTarget.MINGW_X64 -> { config.target == KonanTarget.MINGW_X64 -> {
@@ -410,13 +410,28 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
internal val libraryToCache: PartialCacheInfo? internal val libraryToCache: PartialCacheInfo?
get() = cacheSupport.libraryToCache get() = cacheSupport.libraryToCache
internal val producePerFileCache = libraryToCache?.strategy is CacheDeserializationStrategy.SingleFile internal val producePerFileCache
get() = libraryToCache?.strategy is CacheDeserializationStrategy.SingleFile
val outputFiles = internal val produceBatchedPerFileCache
OutputFiles(configuration.get(KonanConfigKeys.OUTPUT) ?: cacheSupport.tryGetImplicitOutput(), get() = configuration.get(KonanConfigKeys.MAKE_PER_FILE_CACHE) == true
target, produce, producePerFileCache) && 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 val outputFile get() = outputFiles.mainFileName
@@ -44,10 +44,12 @@ class KonanConfigKeys {
= CompilerConfigurationKey.create<List<String>>("paths to directories containing caches") = CompilerConfigurationKey.create<List<String>>("paths to directories containing caches")
val CACHED_LIBRARIES: CompilerConfigurationKey<Map<String, String>> val CACHED_LIBRARIES: CompilerConfigurationKey<Map<String, String>>
= CompilerConfigurationKey.create<Map<String, String>>("mapping from library paths to cache paths") = CompilerConfigurationKey.create<Map<String, String>>("mapping from library paths to cache paths")
val FILE_TO_CACHE: CompilerConfigurationKey<String?> val FILES_TO_CACHE: CompilerConfigurationKey<List<String>>
= CompilerConfigurationKey.create<String?>("which file should be compiled to cache") = CompilerConfigurationKey.create<List<String>>("which files should be compiled to cache")
val MAKE_PER_FILE_CACHE: CompilerConfigurationKey<Boolean> val MAKE_PER_FILE_CACHE: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create<Boolean>("make per-file cache") = CompilerConfigurationKey.create<Boolean>("make per-file cache")
val BATCHED_PER_FILE_CACHE_BUILD: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create<Boolean>("build per-file caches in batches")
val FRAMEWORK_IMPORT_HEADERS: CompilerConfigurationKey<List<String>> val FRAMEWORK_IMPORT_HEADERS: CompilerConfigurationKey<List<String>>
= CompilerConfigurationKey.create<List<String>>("headers imported to framework header") = CompilerConfigurationKey.create<List<String>>("headers imported to framework header")
val FRIEND_MODULES: CompilerConfigurationKey<List<String>> val FRIEND_MODULES: CompilerConfigurationKey<List<String>>
@@ -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.phaser.invokeToplevel
import org.jetbrains.kotlin.backend.common.serialization.codedInputStream import org.jetbrains.kotlin.backend.common.serialization.codedInputStream
import org.jetbrains.kotlin.backend.common.serialization.proto.IrFile 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.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
@@ -29,7 +30,7 @@ class KonanDriver(val project: Project, val environment: KotlinCoreEnvironment,
fun run() { fun run() {
val fileNames = configuration.get(KonanConfigKeys.LIBRARY_TO_ADD_TO_CACHE)?.let { libPath -> val fileNames = configuration.get(KonanConfigKeys.LIBRARY_TO_ADD_TO_CACHE)?.let { libPath ->
if (configuration.get(KonanConfigKeys.MAKE_PER_FILE_CACHE) != true) if (configuration.get(KonanConfigKeys.MAKE_PER_FILE_CACHE) != true)
null configuration.get(KonanConfigKeys.FILES_TO_CACHE)
else { else {
val lib = createKonanLibrary(File(libPath), "default", null, true) val lib = createKonanLibrary(File(libPath), "default", null, true)
(0 until lib.fileCount()).map { fileIndex -> (0 until lib.fileCount()).map { fileIndex ->
@@ -42,8 +43,14 @@ class KonanDriver(val project: Project, val environment: KotlinCoreEnvironment,
if (fileNames == null) { if (fileNames == null) {
KonanConfig(project, configuration).runTopLevelPhases() KonanConfig(project, configuration).runTopLevelPhases()
} else { } else {
fileNames.forEach { buildFileCache(it, CompilerOutputKind.PRELIMINARY_CACHE) } configuration.put(KonanConfigKeys.MAKE_PER_FILE_CACHE, true)
fileNames.forEach { buildFileCache(it, configuration.get(KonanConfigKeys.PRODUCE)!!) } 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 phaseConfig = configuration.get(CLIConfigurationKeys.PHASE_CONFIG)!!
val subConfiguration = configuration.copy() val subConfiguration = configuration.copy()
subConfiguration.put(KonanConfigKeys.PRODUCE, cacheKind) subConfiguration.put(KonanConfigKeys.PRODUCE, cacheKind)
subConfiguration.put(KonanConfigKeys.FILE_TO_CACHE, fileName) subConfiguration.put(KonanConfigKeys.FILES_TO_CACHE, listOf(fileName))
subConfiguration.put(KonanConfigKeys.MAKE_PER_FILE_CACHE, false)
subConfiguration.put(CLIConfigurationKeys.PHASE_CONFIG, phaseConfig.toBuilder().build()) subConfiguration.put(CLIConfigurationKeys.PHASE_CONFIG, phaseConfig.toBuilder().build())
KonanConfig(project, subConfiguration).runTopLevelPhases() KonanConfig(project, subConfiguration).runTopLevelPhases()
} }
@@ -102,6 +108,8 @@ private fun runTopLevelPhases(konanConfig: KonanConfig, environment: KotlinCoreE
toplevelPhase.cast<CompilerPhase<Context, Unit, Unit>>().invokeToplevel(context.phaseConfig, context, Unit) toplevelPhase.cast<CompilerPhase<Context, Unit, Unit>>().invokeToplevel(context.phaseConfig, context, Unit)
} finally { } finally {
context.disposeLlvm() context.disposeLlvm()
context.disposeRuntime()
tryDisposeLLVMContext()
} }
} }
} }
@@ -82,8 +82,8 @@ internal fun makeKonanModuleOpPhase(
actions = modulePhaseActions actions = modulePhaseActions
) )
internal val specialBackendChecksPhase = konanUnitPhase( internal val specialBackendChecksPhase = makeKonanModuleLoweringPhase(
op = { irModule!!.files.forEach { SpecialBackendChecksTraversal(this).lower(it) } }, ::SpecialBackendChecksTraversal,
name = "SpecialBackendChecks", name = "SpecialBackendChecks",
description = "Special backend checks" description = "Special backend checks"
) )
@@ -151,6 +151,18 @@ internal val inventNamesForLocalClasses = makeKonanFileLoweringPhase(
internal val extractLocalClassesFromInlineBodies = makeKonanFileOpPhase( internal val extractLocalClassesFromInlineBodies = makeKonanFileOpPhase(
{ context, irFile -> { 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) LocalClassesInInlineLambdasLowering(context).lower(irFile)
LocalClassesInInlineFunctionsLowering(context).lower(irFile) LocalClassesInInlineFunctionsLowering(context).lower(irFile)
LocalClassesExtractionFromInlineFunctionsLowering(context).lower(irFile) LocalClassesExtractionFromInlineFunctionsLowering(context).lower(irFile)
@@ -168,18 +180,6 @@ internal val wrapInlineDeclarationsWithReifiedTypeParametersLowering = makeKonan
internal val inlinePhase = makeKonanFileOpPhase( internal val inlinePhase = makeKonanFileOpPhase(
{ context, irFile -> { 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) FunctionInlining(context, NativeInlineFunctionResolver(context)).lower(irFile)
}, },
name = "Inline", name = "Inline",
@@ -286,12 +286,10 @@ internal val rangeContainsLoweringPhase = makeKonanFileLoweringPhase(
description = "Optimizes calls to contains() for ClosedRanges" description = "Optimizes calls to contains() for ClosedRanges"
) )
internal val functionsWithoutBoundCheck = makeKonanModuleOpPhase( internal val functionsWithoutBoundCheck = konanUnitPhase(
name = "FunctionsWithoutBoundCheckGenerator", name = "FunctionsWithoutBoundCheckGenerator",
description = "Functions without bounds check generation", description = "Functions without bounds check generation",
op = { context, _ -> op = { FunctionsWithoutBoundCheckGenerator(this).generate() }
FunctionsWithoutBoundCheckGenerator(context).generate()
}
) )
internal val forLoopsPhase = makeKonanFileOpPhase( internal val forLoopsPhase = makeKonanFileOpPhase(
@@ -51,7 +51,7 @@ internal class CacheStorage(val context: Context) {
context.config.outputFiles.prepareTempDirectories() context.config.outputFiles.prepareTempDirectories()
if (!isPreliminaryCache) if (!isPreliminaryCache)
saveCacheBitcodeDependencies() saveCacheBitcodeDependencies()
if (isPreliminaryCache || context.configuration.get(KonanConfigKeys.FILE_TO_CACHE) == null) { if (isPreliminaryCache || !context.config.producePerFileCache || context.config.produceBatchedPerFileCache) {
saveInlineFunctionBodies() saveInlineFunctionBodies()
saveClassFields() saveClassFields()
} }
@@ -6,8 +6,10 @@
package org.jetbrains.kotlin.backend.konan package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.konan.ir.konanLibrary import org.jetbrains.kotlin.backend.konan.ir.konanLibrary
import org.jetbrains.kotlin.builtins.FunctionInterfacePackageFragment
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.fileOrNull
import org.jetbrains.kotlin.ir.util.getPackageFragment import org.jetbrains.kotlin.ir.util.getPackageFragment
import org.jetbrains.kotlin.library.KotlinLibrary import org.jetbrains.kotlin.library.KotlinLibrary
@@ -41,10 +43,19 @@ internal class DefaultLlvmModuleSpecification(cachedLibraries: CachedLibraries)
} }
internal class CacheLlvmModuleSpecification( internal class CacheLlvmModuleSpecification(
private val context: Context,
cachedLibraries: CachedLibraries, cachedLibraries: CachedLibraries,
private val libraryToCache: KotlinLibrary private val libraryToCache: PartialCacheInfo
) : LlvmModuleSpecificationBase(cachedLibraries) { ) : LlvmModuleSpecificationBase(cachedLibraries) {
override val isFinal = false 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 }
}
} }
@@ -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]*/) outputName.substring(0, outputName.lastIndexOf(File.separatorChar) /* skip [PER_FILE_CACHE_BINARY_LEVEL_DIR_NAME]*/)
else null else null
val perFileCacheFileName = pathToPerFileCache?.let { File(it).name } val perFileCacheFileName = File(pathToPerFileCache ?: outputName).absoluteFile.name
val cacheFileName = File((pathToPerFileCache ?: outputName).fullOutputName()).absoluteFile.name val cacheFileName = File((pathToPerFileCache ?: outputName).fullOutputName()).absoluteFile.name
@@ -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.lower.SamSuperTypesChecker
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport
import org.jetbrains.kotlin.backend.konan.serialization.* import org.jetbrains.kotlin.backend.konan.serialization.*
import org.jetbrains.kotlin.builtins.FunctionInterfacePackageFragment
import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.languageVersionSettings import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
@@ -67,7 +68,7 @@ internal fun fileValidationCallback(state: ActionState, irFile: IrFile, context:
internal fun konanUnitPhase( internal fun konanUnitPhase(
name: String, name: String,
description: String, description: String,
prerequisite: Set<AnyNamedPhase> = emptySet(), prerequisite: Set<NamedCompilerPhase<Context, *>> = emptySet(),
op: Context.() -> Unit op: Context.() -> Unit
) = namedOpUnitPhase(name, description, prerequisite, op) ) = 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<Context, Unit, Unit> {
override fun invoke(phaseConfig: PhaseConfig, phaserState: PhaserState<Unit>, 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<Context, IrModuleFragment>( internal val dumpTestsPhase = makeCustomPhase<Context, IrModuleFragment>(
name = "dumpTestsPhase", name = "dumpTestsPhase",
description = "Dump the list of all available tests", description = "Dump the list of all available tests",
@@ -408,12 +452,10 @@ private val bitcodePostprocessingPhase = NamedCompilerPhase(
rewriteExternalCallsCheckerGlobals rewriteExternalCallsCheckerGlobals
) )
private val backendCodegen = namedUnitPhase( private val backendCodegen = NamedCompilerPhase(
name = "Backend codegen", name = "Backend codegen",
description = "Backend code generation", description = "Backend code generation",
lower = takeFromContext<Context, Unit, IrModuleFragment> { it.irModule!! } then lower = entryPointPhase then
entryPointPhase then
functionsWithoutBoundCheck then
allLoweringsPhase then // Lower current module first. allLoweringsPhase then // Lower current module first.
dependenciesLowerPhase then // Then lower all libraries in topological order. dependenciesLowerPhase then // Then lower all libraries in topological order.
// With that we guarantee that inline functions are unlowered while being inlined. // With that we guarantee that inline functions are unlowered while being inlined.
@@ -422,36 +464,53 @@ private val backendCodegen = namedUnitPhase(
verifyBitcodePhase then verifyBitcodePhase then
printBitcodePhase then printBitcodePhase then
linkBitcodeDependenciesPhase then linkBitcodeDependenciesPhase then
bitcodePostprocessingPhase then bitcodePostprocessingPhase
unitSink() )
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. // Have to hide Context as type parameter in order to expose toplevelPhase outside of this module.
val toplevelPhase: CompilerPhase<*, Unit, Unit> = namedUnitPhase( val toplevelPhase: CompilerPhase<*, Unit, Unit> = namedUnitPhase(
name = "Compiler", name = "Compiler",
description = "The whole compilation process", description = "The whole compilation process",
lower = createSymbolTablePhase then lower = middleEnd then
objCExportPhase then singleCompilation then
buildCExportsPhase then umbrellaCompilation
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
) )
internal fun PhaseConfig.disableIf(phase: AnyNamedPhase, condition: Boolean) { internal fun PhaseConfig.disableIf(phase: AnyNamedPhase, condition: Boolean) {
@@ -472,6 +531,9 @@ internal fun PhaseConfig.konanPhasesConfig(config: KonanConfig) {
disable(localEscapeAnalysisPhase) disable(localEscapeAnalysisPhase)
disableIf(singleCompilation, config.produceBatchedPerFileCache)
disableUnless(umbrellaCompilation, config.produceBatchedPerFileCache)
// Don't serialize anything to a final executable. // Don't serialize anything to a final executable.
disableUnless(serializerPhase, config.produce == CompilerOutputKind.LIBRARY) disableUnless(serializerPhase, config.produce == CompilerOutputKind.LIBRARY)
disableUnless(entryPointPhase, config.produce == CompilerOutputKind.PROGRAM) disableUnless(entryPointPhase, config.produce == CompilerOutputKind.PROGRAM)
@@ -480,7 +542,7 @@ internal fun PhaseConfig.konanPhasesConfig(config: KonanConfig) {
disableUnless(finalizeCachePhase, config.produce.isCache) disableUnless(finalizeCachePhase, config.produce.isCache)
disableUnless(exportInternalAbiPhase, config.produce.isCache) disableUnless(exportInternalAbiPhase, config.produce.isCache)
disableUnless(buildCExportsPhase, config.produce.isNativeLibrary) 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(checkExternalCallsPhase, getBoolean(KonanConfigKeys.CHECK_EXTERNAL_CALLS))
disableUnless(rewriteExternalCallsCheckerGlobals, getBoolean(KonanConfigKeys.CHECK_EXTERNAL_CALLS)) disableUnless(rewriteExternalCallsCheckerGlobals, getBoolean(KonanConfigKeys.CHECK_EXTERNAL_CALLS))
disableUnless(stringConcatenationTypeNarrowingPhase, config.optimizationsEnabled) disableUnless(stringConcatenationTypeNarrowingPhase, config.optimizationsEnabled)
@@ -405,9 +405,18 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) {
* All fields of the class instance. * All fields of the class instance.
* The order respects the class hierarchy, i.e. a class [fields] contains superclass [fields] as a prefix. * The order respects the class hierarchy, i.e. a class [fields] contains superclass [fields] as a prefix.
*/ */
val fields: List<FieldInfo> by lazy { val fields: List<FieldInfo>
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<FieldInfo> by lazy {
val superClass = irClass.getSuperClassNotAny() 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 declaredFields = getDeclaredFields()
val sortedDeclaredFields = if (irClass.hasAnnotation(KonanFqNames.noReorderFields)) val sortedDeclaredFields = if (irClass.hasAnnotation(KonanFqNames.noReorderFields))
@@ -21,10 +21,7 @@ import org.jetbrains.kotlin.backend.konan.optimizations.*
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.defaultType import org.jetbrains.kotlin.ir.util.*
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.visitors.* import org.jetbrains.kotlin.ir.visitors.*
import org.jetbrains.kotlin.konan.target.Architecture import org.jetbrains.kotlin.konan.target.Architecture
import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.OperatorNameConventions
@@ -39,7 +36,12 @@ internal val contextLLVMSetupPhase = makeKonanModuleOpPhase(
// (see Llvm class in ContextUtils) // (see Llvm class in ContextUtils)
// Which in turn is determined by the clang flags // Which in turn is determined by the clang flags
// used to compile runtime.bc. // used to compile runtime.bc.
// TODO
llvmContext = LLVMContextCreate()!! llvmContext = LLVMContextCreate()!!
context.llvmDisposed = false
context.runtime = Runtime(context.config.distribution.compilerInterface(context.config.target))
val llvmModule = LLVMModuleCreateWithNameInContext("out", llvmContext)!! val llvmModule = LLVMModuleCreateWithNameInContext("out", llvmContext)!!
context.llvmModule = llvmModule context.llvmModule = llvmModule
context.debugInfo.builder = LLVMCreateDIBuilder(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<Context, Unit, Unit> {
override fun invoke(phaseConfig: PhaseConfig, phaserState: PhaserState<Unit>, context: Context, input: Unit) {
context.llvmImports = Llvm.ImportsImpl(context)
}
}
)
internal val disposeLLVMPhase = namedUnitPhase( internal val disposeLLVMPhase = namedUnitPhase(
name = "DisposeLLVM", name = "DisposeLLVM",
description = "Dispose LLVM", description = "Dispose LLVM",
lower = object : CompilerPhase<Context, Unit, Unit> { lower = object : CompilerPhase<Context, Unit, Unit> {
override fun invoke(phaseConfig: PhaseConfig, phaserState: PhaserState<Unit>, context: Context, input: Unit) { override fun invoke(phaseConfig: PhaseConfig, phaserState: PhaserState<Unit>, context: Context, input: Unit) {
context.disposeLlvm() context.disposeLlvm()
context.disposeRuntime()
tryDisposeLLVMContext()
} }
} }
) )
@@ -444,8 +444,7 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) : Runti
private val target = context.config.target private val target = context.config.target
val runtimeFile = context.config.distribution.compilerInterface(target) override val runtime get() = context.runtime
override val runtime = Runtime(runtimeFile) // TODO: dispose
val targetTriple = runtime.target val targetTriple = runtime.target
@@ -2858,7 +2858,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
library == null -> context.config.moduleId.moduleConstructorName library == null -> context.config.moduleId.moduleConstructorName
library == context.config.libraryToCache?.klib library == context.config.libraryToCache?.klib
&& context.config.producePerFileCache -> && context.config.producePerFileCache ->
fileCtorName(library.uniqueName, context.config.outputFiles.perFileCacheFileName!!) fileCtorName(library.uniqueName, context.config.outputFiles.perFileCacheFileName)
else -> library.moduleConstructorName else -> library.moduleConstructorName
} }
@@ -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.ClassLayoutBuilder
import org.jetbrains.kotlin.backend.konan.descriptors.isTypedIntrinsic import org.jetbrains.kotlin.backend.konan.descriptors.isTypedIntrinsic
import org.jetbrains.kotlin.backend.konan.ir.* 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.IrElement
import org.jetbrains.kotlin.ir.declarations.* 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.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
@@ -22,8 +22,19 @@ import org.jetbrains.kotlin.name.Name
internal class InlineFunctionsSupport(mapping: NativeMapping) { internal class InlineFunctionsSupport(mapping: NativeMapping) {
private val notLoweredInlineFunctions = mapping.notLoweredInlineFunctions 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 } context.mapping.loweredInlineFunctions[function]?.let { return it.irFunction }
val packageFragment = function.getPackageFragment() val packageFragment = function.getPackageFragment()
val notLoweredFunction = if (packageFragment !is IrExternalPackageFragment) { val (possiblyLoweredFunction, shouldLower) = if (packageFragment !is IrExternalPackageFragment) {
context.inlineFunctionsSupport.getNonLoweredInlineFunction(function).also { context.inlineFunctionsSupport.getNonLoweredInlineFunction(function, copy = context.config.produceBatchedPerFileCache).also {
context.mapping.loweredInlineFunctions[function] = context.mapping.loweredInlineFunctions[function] =
InlineFunctionOriginInfo(it, packageFragment as IrFile, function.startOffset, function.endOffset) InlineFunctionOriginInfo(it, packageFragment as IrFile, function.startOffset, function.endOffset)
} } to true
} else { } else {
// The function is from Lazy IR, get its body from the IR linker. // The function is from Lazy IR, get its body from the IR linker.
val moduleDescriptor = packageFragment.packageFragmentDescriptor.containingDeclaration val moduleDescriptor = packageFragment.packageFragmentDescriptor.containingDeclaration
@@ -48,35 +59,38 @@ internal class NativeInlineFunctionResolver(override val context: Context) : Def
require(context.config.cachedLibraries.isLibraryCached(moduleDeserializer.klib)) { require(context.config.cachedLibraries.isLibraryCached(moduleDeserializer.klib)) {
"No IR and no cache for ${function.render()}" "No IR and no cache for ${function.render()}"
} }
context.mapping.loweredInlineFunctions[function] = moduleDeserializer.deserializeInlineFunction(function) val (shouldLower, deserializedInlineFunction) = moduleDeserializer.deserializeInlineFunction(function)
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) ArrayConstructorLowering(context).lower(body, possiblyLoweredFunction)
NullableFieldsDeclarationLowering(context).lowerWithLocalDeclarations(notLoweredFunction)
LateinitUsageLowering(context).lower(body, notLoweredFunction)
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. // Do not extract local classes off of inline functions from cached libraries.
LocalClassesInInlineFunctionsLowering(context).lower(body, notLoweredFunction) LocalClassesInInlineFunctionsLowering(context).lower(body, possiblyLoweredFunction)
LocalClassesExtractionFromInlineFunctionsLowering(context).lower(body, notLoweredFunction) 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) { private fun DeclarationTransformer.lowerWithLocalDeclarations(function: IrFunction) {
@@ -307,6 +307,8 @@ object KonanFakeOverrideClassFilter : FakeOverrideClassFilter {
} }
} }
internal data class DeserializedInlineFunction(val firstAccess: Boolean, val function: InlineFunctionOriginInfo)
internal class KonanIrLinker( internal class KonanIrLinker(
private val currentModule: ModuleDescriptor, private val currentModule: ModuleDescriptor,
override val translationPluginContext: TranslationPluginContext?, override val translationPluginContext: TranslationPluginContext?,
@@ -387,14 +389,18 @@ internal class KonanIrLinker(
else -> null else -> null
} }
fun getExternalDeclarationFileName(declaration: IrDeclaration) = with(declaration) { fun getExternalDeclarationFileName(declaration: IrDeclaration) = when (val packageFragment = declaration.getPackageFragment()) {
val externalPackageFragment = getPackageFragment() as? IrExternalPackageFragment is IrFile -> packageFragment.path
?: error("Expected an external package fragment for ${render()}")
val moduleDescriptor = externalPackageFragment.packageFragmentDescriptor.containingDeclaration is IrExternalPackageFragment -> {
val moduleDeserializer = moduleDeserializers[moduleDescriptor] val moduleDescriptor = packageFragment.packageFragmentDescriptor.containingDeclaration
?: error("No module deserializer for $moduleDescriptor") val moduleDeserializer = moduleDeserializers[moduleDescriptor] ?: error("No module deserializer for $moduleDescriptor")
val idSig = moduleDeserializer.descriptorSignatures[descriptor] ?: error("No signature for $descriptor") val descriptor = declaration.descriptor
idSig.topLevelSignature().fileSignature()?.fileName ?: error("No file for $idSig") 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 private val IrClass.firstNonClassParent: IrDeclarationParent
@@ -637,7 +643,16 @@ internal class KonanIrLinker(
} }
} }
fun deserializeInlineFunction(function: IrFunction): InlineFunctionOriginInfo { private val deserializedInlineFunctions = mutableMapOf<IrFunction, InlineFunctionOriginInfo>()
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 val packageFragment = function.getPackageFragment() as? IrExternalPackageFragment
?: error("Expected an external package fragment for ${function.render()}") ?: error("Expected an external package fragment for ${function.render()}")
if (function.parents.any { (it as? IrFunction)?.isInline == true }) { if (function.parents.any { (it as? IrFunction)?.isInline == true }) {