[K/N] Finish refactoring TempFiles usages

1. Introduce CExportFiles class
This class aggregates all files that are created specifically for
C export. Adding more such classes for other output kinds should
help getting rid of OutputFiles class.

2. Remove NativeGenerationState.tempFiles
Making NativeGenerationState (and compiler phases in general)
unaware of files and their lifetime is necessary for making phases
simpler and easier to compose.
This commit is contained in:
Sergey Bogolepov
2023-02-02 17:47:29 +02:00
committed by Space Team
parent 4fbddf1786
commit f7d3312ad5
3 changed files with 37 additions and 24 deletions
@@ -47,7 +47,6 @@ internal class NativeGenerationState(
val dependenciesTracker: DependenciesTracker, val dependenciesTracker: DependenciesTracker,
val llvmModuleSpecification: LlvmModuleSpecification, val llvmModuleSpecification: LlvmModuleSpecification,
val outputFiles: OutputFiles, val outputFiles: OutputFiles,
val tempFiles: TempFiles,
val llvmModuleName: String, val llvmModuleName: String,
) : BasicPhaseContext(config), BackendContextHolder<Context>, LlvmIrHolder { ) : BasicPhaseContext(config), BackendContextHolder<Context>, LlvmIrHolder {
val outputFile = outputFiles.mainFileName val outputFile = outputFiles.mainFileName
@@ -8,9 +8,9 @@ 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.driver.utilities.CExportFiles
import org.jetbrains.kotlin.backend.konan.driver.utilities.createTempFiles import org.jetbrains.kotlin.backend.konan.driver.utilities.createTempFiles
import org.jetbrains.kotlin.backend.konan.ir.konanLibrary 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.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclaration import org.jetbrains.kotlin.ir.declarations.IrDeclaration
@@ -65,14 +65,23 @@ internal fun <C : PhaseContext> PhaseEngine<C>.runBackend(backendContext: Contex
val outputFiles = OutputFiles(outputPath, config.target, config.produce) val outputFiles = OutputFiles(outputPath, config.target, config.produce)
val generationState = NativeGenerationState(context.config, backendContext, val generationState = NativeGenerationState(context.config, backendContext,
fragment.cacheDeserializationStrategy, fragment.dependenciesTracker, fragment.llvmModuleSpecification, outputFiles, fragment.cacheDeserializationStrategy, fragment.dependenciesTracker, fragment.llvmModuleSpecification, outputFiles,
tempFiles,
llvmModuleName = "out" // TODO: Currently, all llvm modules are named as "out" which might lead to collisions. llvmModuleName = "out" // TODO: Currently, all llvm modules are named as "out" which might lead to collisions.
) )
backendEngine.useContext(generationState) { generationStateEngine -> backendEngine.useContext(generationState) { generationStateEngine ->
val bitcodeFile = tempFiles.create(generationState.llvmModuleName, ".bc").javaFile()
val cExportFiles = if (config.produce.isNativeLibrary) {
CExportFiles(
cppAdapter = tempFiles.create("api", ".cpp").javaFile(),
bitcodeAdapter = tempFiles.create("api", ".bc").javaFile(),
header = outputFiles.cAdapterHeader.javaFile(),
def = if (config.target.family == Family.MINGW) outputFiles.cAdapterDef.javaFile() else null,
)
} else null
// 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.irModule) generationStateEngine.compileModule(fragment.irModule, bitcodeFile, cExportFiles)
// Split here // Split here
compileAndLink(it, it.outputFiles.mainFileName, it.outputFiles, it.temporaryFiles, isCoverageEnabled = false) val moduleCompilationOutput = ModuleCompilationOutput(bitcodeFile, generationState.dependenciesTracker.collectResult())
compileAndLink(moduleCompilationOutput, outputFiles.mainFileName, outputFiles, tempFiles, isCoverageEnabled = false)
} }
} finally { } finally {
tempFiles.dispose() tempFiles.dispose()
@@ -152,10 +161,6 @@ private fun PhaseEngine<out Context>.splitIntoFragments(
internal data class ModuleCompilationOutput( internal data class ModuleCompilationOutput(
val bitcodeFile: File, val bitcodeFile: File,
val dependenciesTrackingResult: DependenciesTrackingResult, val dependenciesTrackingResult: DependenciesTrackingResult,
// Passing tempFiles and output files through this file looks silly and incorrect.
// TODO: Refactor these classes and remove them from here.
val temporaryFiles: TempFiles,
val outputFiles: OutputFiles,
) )
/** /**
@@ -165,23 +170,19 @@ internal data class ModuleCompilationOutput(
* 4. Optimizes it. * 4. Optimizes it.
* 5. Serializes it to a bitcode file. * 5. Serializes it to a bitcode file.
*/ */
internal fun PhaseEngine<NativeGenerationState>.compileModule(module: IrModuleFragment): ModuleCompilationOutput { internal fun PhaseEngine<NativeGenerationState>.compileModule(module: IrModuleFragment, bitcodeFile: File, cExportFiles: CExportFiles?) {
if (context.config.produce.isCache) { if (context.config.produce.isCache) {
runPhase(BuildAdditionalCacheInfoPhase, module) runPhase(BuildAdditionalCacheInfoPhase, module)
} }
if (context.config.produce == CompilerOutputKind.PROGRAM) { if (context.config.produce == CompilerOutputKind.PROGRAM) {
runPhase(EntryPointPhase, module) runPhase(EntryPointPhase, module)
} }
runBackendCodegen(module) runBackendCodegen(module, cExportFiles)
runBitcodePostProcessing() runBitcodePostProcessing()
if (context.config.produce.isCache) { if (context.config.produce.isCache) {
runPhase(SaveAdditionalCacheInfoPhase) runPhase(SaveAdditionalCacheInfoPhase)
} }
// TODO: Currently, all llvm modules are named as "out" which might lead to collisions.
val bitcodeFile = context.tempFiles.create(context.llvm.module.getName(), ".bc").javaFile()
runPhase(WriteBitcodeFilePhase, WriteBitcodeFileInput(context.llvm.module, bitcodeFile)) runPhase(WriteBitcodeFilePhase, WriteBitcodeFileInput(context.llvm.module, bitcodeFile))
val dependenciesTrackingResult = context.dependenciesTracker.collectResult()
return ModuleCompilationOutput(bitcodeFile, dependenciesTrackingResult, context.tempFiles, context.outputFiles)
} }
internal fun <C : PhaseContext> PhaseEngine<C>.compileAndLink( internal fun <C : PhaseContext> PhaseEngine<C>.compileAndLink(
@@ -227,7 +228,7 @@ internal fun <C : PhaseContext> PhaseEngine<C>.compileAndLink(
} }
internal fun PhaseEngine<NativeGenerationState>.runBackendCodegen(module: IrModuleFragment) { internal fun PhaseEngine<NativeGenerationState>.runBackendCodegen(module: IrModuleFragment, cExportFiles: CExportFiles?) {
runAllLowerings(module) runAllLowerings(module)
val dependenciesToCompile = findDependenciesToCompile() val dependenciesToCompile = findDependenciesToCompile()
// TODO: KonanLibraryResolver.TopologicalLibraryOrder actually returns libraries in the reverse topological order. // TODO: KonanLibraryResolver.TopologicalLibraryOrder actually returns libraries in the reverse topological order.
@@ -238,17 +239,16 @@ internal fun PhaseEngine<NativeGenerationState>.runBackendCodegen(module: IrModu
mergeDependencies(module, dependenciesToCompile) mergeDependencies(module, dependenciesToCompile)
runCodegen(module) runCodegen(module)
val generatedBitcodeFiles = if (context.config.produce.isNativeLibrary) { val generatedBitcodeFiles = if (context.config.produce.isNativeLibrary) {
val cppAdapterFile = context.tempFiles.create("api", ".cpp").javaFile() require(cExportFiles != null)
val bitcodeAdapterFile = context.tempFiles.create("api", ".bc").javaFile()
val input = CExportGenerateApiInput( val input = CExportGenerateApiInput(
context.context.cAdapterExportedElements!!, context.context.cAdapterExportedElements!!,
headerFile = context.outputFiles.cAdapterHeader.javaFile(), headerFile = cExportFiles.header,
defFile = if (context.config.target.family == Family.MINGW) context.outputFiles.cAdapterDef.javaFile() else null, defFile = cExportFiles.def,
cppAdapterFile = cppAdapterFile cppAdapterFile = cExportFiles.cppAdapter
) )
runPhase(CExportGenerateApiPhase, input) runPhase(CExportGenerateApiPhase, input)
runPhase(CExportCompileAdapterPhase, CExportCompileAdapterInput(cppAdapterFile, bitcodeAdapterFile)) runPhase(CExportCompileAdapterPhase, CExportCompileAdapterInput(cExportFiles.cppAdapter, cExportFiles.bitcodeAdapter))
listOf(bitcodeAdapterFile) listOf(cExportFiles.bitcodeAdapter)
} else { } else {
emptyList() emptyList()
} }
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.backend.konan.CacheSupport
import org.jetbrains.kotlin.backend.konan.KonanConfig import org.jetbrains.kotlin.backend.konan.KonanConfig
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
import org.jetbrains.kotlin.konan.TempFiles import org.jetbrains.kotlin.konan.TempFiles
import java.io.File
internal fun createTempFiles(config: KonanConfig, cacheDeserializationStrategy: CacheDeserializationStrategy?): TempFiles { internal fun createTempFiles(config: KonanConfig, cacheDeserializationStrategy: CacheDeserializationStrategy?): TempFiles {
val pathToTempDir = config.configuration.get(KonanConfigKeys.TEMPORARY_FILES_DIR)?.let { val pathToTempDir = config.configuration.get(KonanConfigKeys.TEMPORARY_FILES_DIR)?.let {
@@ -19,4 +20,17 @@ internal fun createTempFiles(config: KonanConfig, cacheDeserializationStrategy:
else org.jetbrains.kotlin.konan.file.File(it, CacheSupport.cacheFileId(singleFileStrategy.fqName, singleFileStrategy.filePath)).path else org.jetbrains.kotlin.konan.file.File(it, CacheSupport.cacheFileId(singleFileStrategy.fqName, singleFileStrategy.filePath)).path
} }
return TempFiles(pathToTempDir) return TempFiles(pathToTempDir)
} }
/**
* Files that are generated only during compilation of C dynamic/static library.
*
* TODO: At some point this class and it usages can be generalized to all possible compiler outputs,
* so instead of OutputFiles we will create a series of classes for each specific compiler output kit.
*/
internal class CExportFiles(
val cppAdapter: File,
val bitcodeAdapter: File,
val header: File,
val def: File?,
)