[K/N] Refactor module splitting for backend

We want to extract tempFiles from NativeGenerationState.
To do so, we need to move creation of NativeGenerationState from
`splitIntoFragments` function and untie CacheLlvmModuleSpecification
from NativeGenerationState
This commit is contained in:
Sergey Bogolepov
2023-02-02 16:32:48 +02:00
committed by Space Team
parent eba1b4c388
commit b7b85177a7
4 changed files with 70 additions and 37 deletions
@@ -44,9 +44,11 @@ internal val NativeGenerationState.shouldDefineCachedBoxes: Boolean
get() = producedLlvmModuleContainsStdlib &&
cacheDeserializationStrategy?.contains(KonanFqNames.internalPackageName, "Boxing.kt") != false
internal val CacheDeserializationStrategy?.containsRuntime: Boolean
get() = this?.contains(KonanFqNames.internalPackageName, "Runtime.kt") != false
internal val NativeGenerationState.shouldLinkRuntimeNativeLibraries: Boolean
get() = producedLlvmModuleContainsStdlib &&
cacheDeserializationStrategy?.contains(KonanFqNames.internalPackageName, "Runtime.kt") != false
get() = producedLlvmModuleContainsStdlib && cacheDeserializationStrategy.containsRuntime
val CompilerOutputKind.isCache: Boolean
get() = this == CompilerOutputKind.STATIC_CACHE || this == CompilerOutputKind.DYNAMIC_CACHE
@@ -43,19 +43,19 @@ internal class DefaultLlvmModuleSpecification(cachedLibraries: CachedLibraries)
}
internal class CacheLlvmModuleSpecification(
private val generationState: NativeGenerationState,
cachedLibraries: CachedLibraries,
private val libraryToCache: PartialCacheInfo
private val libraryToCache: PartialCacheInfo,
private val containsStdlib: Boolean,
) : LlvmModuleSpecificationBase(cachedLibraries) {
override val isFinal = false
override fun containsLibrary(library: KotlinLibrary): Boolean = library == libraryToCache.klib
override fun containsDeclaration(declaration: IrDeclaration): Boolean {
if (generationState.shouldDefineFunctionClasses && declaration.getPackageFragment().isFunctionInterfaceFile)
if (containsStdlib && libraryToCache.strategy.containsKFunctionImpl && declaration.getPackageFragment().isFunctionInterfaceFile)
return true
if (!super.containsDeclaration(declaration)) return false
return (generationState.cacheDeserializationStrategy as? CacheDeserializationStrategy.SingleFile)
return (libraryToCache.strategy as? CacheDeserializationStrategy.SingleFile)
?.filePath.let { it == null || it == declaration.fileOrNull?.path }
}
}
@@ -9,6 +9,7 @@ import llvm.*
import org.jetbrains.kotlin.backend.konan.driver.BasicPhaseContext
import org.jetbrains.kotlin.backend.konan.driver.utilities.BackendContextHolder
import org.jetbrains.kotlin.backend.konan.driver.utilities.LlvmIrHolder
import org.jetbrains.kotlin.backend.konan.ir.konanLibrary
import org.jetbrains.kotlin.backend.konan.llvm.*
import org.jetbrains.kotlin.backend.konan.llvm.coverage.CoverageManager
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport
@@ -79,8 +80,11 @@ internal class NativeGenerationState(
val llvmModuleSpecification by lazy {
if (config.produce.isCache)
CacheLlvmModuleSpecification(this, config.cachedLibraries,
PartialCacheInfo(config.libraryToCache!!.klib, cacheDeserializationStrategy!!))
CacheLlvmModuleSpecification(
config.cachedLibraries,
PartialCacheInfo(config.libraryToCache!!.klib, cacheDeserializationStrategy!!),
containsStdlib = config.libraryToCache!!.klib == context.stdlibModule.konanLibrary
)
else DefaultLlvmModuleSpecification(config.cachedLibraries)
}
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.backend.konan.driver.phases
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.driver.PhaseContext
import org.jetbrains.kotlin.backend.konan.driver.PhaseEngine
import org.jetbrains.kotlin.backend.konan.ir.konanLibrary
import org.jetbrains.kotlin.backend.konan.llvm.getName
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.ir.declarations.IrClass
@@ -55,10 +56,13 @@ internal fun <C : PhaseContext> PhaseEngine<C>.runBackend(backendContext: Contex
useContext(backendContext) { backendEngine ->
backendEngine.runPhase(functionsWithoutBoundCheck)
val fragments = backendEngine.splitIntoFragments(irModule)
fragments.forEach { (generationState, fragment) ->
fragments.forEach { fragment ->
val generationState = NativeGenerationState(context.config, backendContext,
fragment.cacheDeserializationStrategy
)
backendEngine.useContext(generationState) { generationStateEngine ->
// TODO: Make this work if we first compile all the fragments and only after that run the link phases.
val it = generationStateEngine.compileModule(fragment)
val it = generationStateEngine.compileModule(fragment.irModule)
// Split here
compileAndLink(it, it.outputFiles.mainFileName, it.outputFiles, it.temporaryFiles, isCoverageEnabled = false)
}
@@ -74,41 +78,64 @@ private fun isReferencedByNativeRuntime(declarations: List<IrDeclaration>): Bool
it is IrClass && isReferencedByNativeRuntime(it.declarations)
}
private data class BackendJobFragment(
val irModule: IrModuleFragment,
val cacheDeserializationStrategy: CacheDeserializationStrategy?,
val dependenciesTracker: DependenciesTracker,
val llvmModuleSpecification: LlvmModuleSpecification,
)
private fun PhaseEngine<out Context>.splitIntoFragments(
input: IrModuleFragment,
): Sequence<Pair<NativeGenerationState, IrModuleFragment>> = if (context.config.producePerFileCache) {
val module = input
val files = module.files.toList()
val stdlibIsBeingCached = module.descriptor == context.stdlibModule
val functionInterfaceFiles = files.takeIf { stdlibIsBeingCached }
?.filter { it.isFunctionInterfaceFile }.orEmpty()
val filesReferencedByNativeRuntime = files.takeIf { stdlibIsBeingCached }
?.filter { isReferencedByNativeRuntime(it.declarations) }.orEmpty()
): Sequence<BackendJobFragment> {
val config = context.config
return if (context.config.producePerFileCache) {
val files = input.files.toList()
val containsStdlib = config.libraryToCache!!.klib == context.stdlibModule.konanLibrary
files.asSequence().filter { !it.isFunctionInterfaceFile }.map { file ->
val generationState = NativeGenerationState(
context.config,
context,
CacheDeserializationStrategy.SingleFile(file.path, file.fqName.asString())
)
val fragment = IrModuleFragmentImpl(input.descriptor, input.irBuiltins, listOf(file))
if (generationState.shouldDefineFunctionClasses)
fragment.files += functionInterfaceFiles
files.asSequence().filter { !it.isFunctionInterfaceFile }.map { file ->
val cacheDeserializationStrategy = CacheDeserializationStrategy.SingleFile(file.path, file.fqName.asString())
val llvmModuleSpecification = CacheLlvmModuleSpecification(
config.cachedLibraries,
PartialCacheInfo(config.libraryToCache!!.klib, cacheDeserializationStrategy),
containsStdlib = containsStdlib
)
val dependenciesTracker = DependenciesTrackerImpl(llvmModuleSpecification, context.config, context)
val fragment = IrModuleFragmentImpl(input.descriptor, input.irBuiltins, listOf(file))
if (containsStdlib && cacheDeserializationStrategy.containsKFunctionImpl)
fragment.files += files.filter { it.isFunctionInterfaceFile }
if (generationState.shouldLinkRuntimeNativeLibraries) {
filesReferencedByNativeRuntime.forEach {
generationState.dependenciesTracker.add(it)
if (containsStdlib && cacheDeserializationStrategy.containsRuntime) {
files.filter { isReferencedByNativeRuntime(it.declarations) }
.forEach { dependenciesTracker.add(it) }
}
}
fragment.files.filterIsInstance<IrFileImpl>().forEach {
it.module = fragment
fragment.files.filterIsInstance<IrFileImpl>().forEach {
it.module = fragment
}
BackendJobFragment(
fragment,
cacheDeserializationStrategy,
dependenciesTracker,
llvmModuleSpecification,
)
}
generationState to fragment
} else {
val llvmModuleSpecification = if (config.produce.isCache) {
val containsStdlib = config.libraryToCache!!.klib == context.stdlibModule.konanLibrary
CacheLlvmModuleSpecification(config.cachedLibraries, context.config.libraryToCache!!, containsStdlib = containsStdlib)
} else {
DefaultLlvmModuleSpecification(config.cachedLibraries)
}
sequenceOf(
BackendJobFragment(
input,
context.config.libraryToCache?.strategy,
DependenciesTrackerImpl(llvmModuleSpecification, context.config, context),
llvmModuleSpecification
)
)
}
} else {
val nativeGenerationState = NativeGenerationState(context.config, context, context.config.libraryToCache?.strategy)
sequenceOf(nativeGenerationState to input)
}
internal data class ModuleCompilationOutput(