[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 && get() = producedLlvmModuleContainsStdlib &&
cacheDeserializationStrategy?.contains(KonanFqNames.internalPackageName, "Boxing.kt") != false 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 internal val NativeGenerationState.shouldLinkRuntimeNativeLibraries: Boolean
get() = producedLlvmModuleContainsStdlib && get() = producedLlvmModuleContainsStdlib && cacheDeserializationStrategy.containsRuntime
cacheDeserializationStrategy?.contains(KonanFqNames.internalPackageName, "Runtime.kt") != false
val CompilerOutputKind.isCache: Boolean val CompilerOutputKind.isCache: Boolean
get() = this == CompilerOutputKind.STATIC_CACHE || this == CompilerOutputKind.DYNAMIC_CACHE get() = this == CompilerOutputKind.STATIC_CACHE || this == CompilerOutputKind.DYNAMIC_CACHE
@@ -43,19 +43,19 @@ internal class DefaultLlvmModuleSpecification(cachedLibraries: CachedLibraries)
} }
internal class CacheLlvmModuleSpecification( internal class CacheLlvmModuleSpecification(
private val generationState: NativeGenerationState,
cachedLibraries: CachedLibraries, cachedLibraries: CachedLibraries,
private val libraryToCache: PartialCacheInfo private val libraryToCache: PartialCacheInfo,
private val containsStdlib: Boolean,
) : LlvmModuleSpecificationBase(cachedLibraries) { ) : LlvmModuleSpecificationBase(cachedLibraries) {
override val isFinal = false override val isFinal = false
override fun containsLibrary(library: KotlinLibrary): Boolean = library == libraryToCache.klib override fun containsLibrary(library: KotlinLibrary): Boolean = library == libraryToCache.klib
override fun containsDeclaration(declaration: IrDeclaration): Boolean { override fun containsDeclaration(declaration: IrDeclaration): Boolean {
if (generationState.shouldDefineFunctionClasses && declaration.getPackageFragment().isFunctionInterfaceFile) if (containsStdlib && libraryToCache.strategy.containsKFunctionImpl && declaration.getPackageFragment().isFunctionInterfaceFile)
return true return true
if (!super.containsDeclaration(declaration)) return false 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 } ?.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.BasicPhaseContext
import org.jetbrains.kotlin.backend.konan.driver.utilities.BackendContextHolder import org.jetbrains.kotlin.backend.konan.driver.utilities.BackendContextHolder
import org.jetbrains.kotlin.backend.konan.driver.utilities.LlvmIrHolder 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.*
import org.jetbrains.kotlin.backend.konan.llvm.coverage.CoverageManager import org.jetbrains.kotlin.backend.konan.llvm.coverage.CoverageManager
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport
@@ -79,8 +80,11 @@ internal class NativeGenerationState(
val llvmModuleSpecification by lazy { val llvmModuleSpecification by lazy {
if (config.produce.isCache) if (config.produce.isCache)
CacheLlvmModuleSpecification(this, config.cachedLibraries, CacheLlvmModuleSpecification(
PartialCacheInfo(config.libraryToCache!!.klib, cacheDeserializationStrategy!!)) config.cachedLibraries,
PartialCacheInfo(config.libraryToCache!!.klib, cacheDeserializationStrategy!!),
containsStdlib = config.libraryToCache!!.klib == context.stdlibModule.konanLibrary
)
else DefaultLlvmModuleSpecification(config.cachedLibraries) 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.*
import org.jetbrains.kotlin.backend.konan.driver.PhaseContext import org.jetbrains.kotlin.backend.konan.driver.PhaseContext
import org.jetbrains.kotlin.backend.konan.driver.PhaseEngine 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.backend.konan.llvm.getName
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrClass
@@ -55,10 +56,13 @@ internal fun <C : PhaseContext> PhaseEngine<C>.runBackend(backendContext: Contex
useContext(backendContext) { backendEngine -> useContext(backendContext) { backendEngine ->
backendEngine.runPhase(functionsWithoutBoundCheck) backendEngine.runPhase(functionsWithoutBoundCheck)
val fragments = backendEngine.splitIntoFragments(irModule) val fragments = backendEngine.splitIntoFragments(irModule)
fragments.forEach { (generationState, fragment) -> fragments.forEach { fragment ->
val generationState = NativeGenerationState(context.config, backendContext,
fragment.cacheDeserializationStrategy
)
backendEngine.useContext(generationState) { generationStateEngine -> backendEngine.useContext(generationState) { generationStateEngine ->
// TODO: Make this work if we first compile all the fragments and only after that run the link phases. // 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 // Split here
compileAndLink(it, it.outputFiles.mainFileName, it.outputFiles, it.temporaryFiles, isCoverageEnabled = false) 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) 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( private fun PhaseEngine<out Context>.splitIntoFragments(
input: IrModuleFragment, input: IrModuleFragment,
): Sequence<Pair<NativeGenerationState, IrModuleFragment>> = if (context.config.producePerFileCache) { ): Sequence<BackendJobFragment> {
val module = input val config = context.config
val files = module.files.toList() return if (context.config.producePerFileCache) {
val stdlibIsBeingCached = module.descriptor == context.stdlibModule val files = input.files.toList()
val functionInterfaceFiles = files.takeIf { stdlibIsBeingCached } val containsStdlib = config.libraryToCache!!.klib == context.stdlibModule.konanLibrary
?.filter { it.isFunctionInterfaceFile }.orEmpty()
val filesReferencedByNativeRuntime = files.takeIf { stdlibIsBeingCached }
?.filter { isReferencedByNativeRuntime(it.declarations) }.orEmpty()
files.asSequence().filter { !it.isFunctionInterfaceFile }.map { file -> files.asSequence().filter { !it.isFunctionInterfaceFile }.map { file ->
val generationState = NativeGenerationState( val cacheDeserializationStrategy = CacheDeserializationStrategy.SingleFile(file.path, file.fqName.asString())
context.config, val llvmModuleSpecification = CacheLlvmModuleSpecification(
context, config.cachedLibraries,
CacheDeserializationStrategy.SingleFile(file.path, file.fqName.asString()) PartialCacheInfo(config.libraryToCache!!.klib, cacheDeserializationStrategy),
) containsStdlib = containsStdlib
val fragment = IrModuleFragmentImpl(input.descriptor, input.irBuiltins, listOf(file)) )
if (generationState.shouldDefineFunctionClasses) val dependenciesTracker = DependenciesTrackerImpl(llvmModuleSpecification, context.config, context)
fragment.files += functionInterfaceFiles val fragment = IrModuleFragmentImpl(input.descriptor, input.irBuiltins, listOf(file))
if (containsStdlib && cacheDeserializationStrategy.containsKFunctionImpl)
fragment.files += files.filter { it.isFunctionInterfaceFile }
if (generationState.shouldLinkRuntimeNativeLibraries) { if (containsStdlib && cacheDeserializationStrategy.containsRuntime) {
filesReferencedByNativeRuntime.forEach { files.filter { isReferencedByNativeRuntime(it.declarations) }
generationState.dependenciesTracker.add(it) .forEach { dependenciesTracker.add(it) }
} }
}
fragment.files.filterIsInstance<IrFileImpl>().forEach { fragment.files.filterIsInstance<IrFileImpl>().forEach {
it.module = fragment 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( internal data class ModuleCompilationOutput(