[K/N] Introduce multiple compiler drivers

We want to move from current compilation pipeline
that is defined in TopLevelPhases.kt to a "dynamic" one.
The main drawback of the current one is that it is built
around single Context, single IR module, single compiler output, etc.
It is now as flexible as Kotlin/Native compiler requires.
For example, it is pretty hard to support multiple Apple frameworks
or a proper separate compilation.
To mitigate this problem, we are going to move to a new "dynamic"
compiler pipeline that relies on an explicit data passing between
different compiler phases. For example, it allows to use single phase
output as an input for multiple independent subsequent phases. Or
vice versa, e.g. use outputs of multiple "produce object file" phases by
a single linker phase.

This commit introduces `CompilerDriver` hierarchy which is required
during transition period.
This commit is contained in:
Sergey Bogolepov
2022-10-05 12:28:22 +03:00
committed by Space Team
parent 89d3060e00
commit 6ef4277d89
4 changed files with 153 additions and 72 deletions
@@ -6,24 +6,17 @@
package org.jetbrains.kotlin.backend.konan
import com.intellij.openapi.project.Project
import kotlinx.cinterop.usingJvmCInteropCallbacks
import org.jetbrains.kotlin.analyzer.AnalysisResult
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.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
import org.jetbrains.kotlin.backend.konan.driver.CompilerDriver
import org.jetbrains.kotlin.backend.konan.driver.DynamicCompilerDriver
import org.jetbrains.kotlin.backend.konan.driver.StaticCompilerDriver
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.konan.library.impl.createKonanLibrary
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.util.usingNativeMemoryAllocator
import org.jetbrains.kotlin.library.uniqueName
import org.jetbrains.kotlin.protobuf.ExtensionRegistryLite
import org.jetbrains.kotlin.utils.addToStdlib.cast
class KonanDriver(val project: Project, val environment: KotlinCoreEnvironment, val configuration: CompilerConfiguration) {
fun run() {
@@ -43,12 +36,9 @@ class KonanDriver(val project: Project, val environment: KotlinCoreEnvironment,
configuration.put(KonanConfigKeys.FILES_TO_CACHE, fileNames)
}
KonanConfig(project, configuration).runTopLevelPhases()
}
private fun KonanConfig.runTopLevelPhases() {
ensureModuleName(this)
runTopLevelPhases(this, environment)
val konanConfig = KonanConfig(project, configuration)
ensureModuleName(konanConfig)
pickCompilerDriver().run(konanConfig, environment)
}
private fun ensureModuleName(config: KonanConfig) {
@@ -62,62 +52,13 @@ class KonanDriver(val project: Project, val environment: KotlinCoreEnvironment,
}
}
}
}
private fun runTopLevelPhases(konanConfig: KonanConfig, environment: KotlinCoreEnvironment) {
val config = konanConfig.configuration
val targets = konanConfig.targetManager
if (config.get(KonanConfigKeys.LIST_TARGETS) ?: false) {
targets.list()
}
val context = Context(konanConfig)
context.environment = environment
context.phaseConfig.konanPhasesConfig(konanConfig) // TODO: Wrong place to call it
if (konanConfig.infoArgsOnly) return
if (!context.frontendPhase()) return
usingNativeMemoryAllocator {
usingJvmCInteropCallbacks {
try {
toplevelPhase.cast<CompilerPhase<Context, Unit, Unit>>().invokeToplevel(context.phaseConfig, context, Unit)
} finally {
context.disposeGenerationState()
}
private fun pickCompilerDriver(): CompilerDriver {
// Dynamic driver is WIP, so it might not support all possible configurations.
return if (DynamicCompilerDriver.supportsConfig()) {
DynamicCompilerDriver()
} else {
StaticCompilerDriver()
}
}
}
// returns true if should generate code.
internal fun Context.frontendPhase(): Boolean {
lateinit var analysisResult: AnalysisResult
do {
val analyzerWithCompilerReport = AnalyzerWithCompilerReport(
messageCollector,
environment.configuration.languageVersionSettings,
environment.configuration.getBoolean(CLIConfigurationKeys.RENDER_DIAGNOSTIC_INTERNAL_NAME)
)
// Build AST and binding info.
analyzerWithCompilerReport.analyzeAndReport(environment.getSourceFiles()) {
TopDownAnalyzerFacadeForKonan.analyzeFiles(environment.getSourceFiles(), this)
}
if (analyzerWithCompilerReport.hasErrors()) {
throw KonanCompilationException()
}
analysisResult = analyzerWithCompilerReport.analysisResult
if (analysisResult is AnalysisResult.RetryWithAdditionalRoots) {
environment.addKotlinSourceRoots(analysisResult.additionalKotlinRoots)
}
} while(analysisResult is AnalysisResult.RetryWithAdditionalRoots)
moduleDescriptor = analysisResult.moduleDescriptor
bindingContext = analysisResult.bindingContext
return analysisResult.shouldGenerateCode
}
}
@@ -0,0 +1,19 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.konan.driver
import org.jetbrains.kotlin.backend.konan.KonanConfig
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
/**
* Compiler driver orchestrates and connects different parts of compiler into a complete pipeline.
*/
internal sealed class CompilerDriver {
/**
* Entry point for compilation pipeline.
*/
abstract fun run(config: KonanConfig, environment: KotlinCoreEnvironment)
}
@@ -0,0 +1,34 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.konan.driver
import org.jetbrains.kotlin.backend.konan.KonanConfig
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
/**
* Dynamic driver does not "know" upfront which phases will be executed.
*/
internal class DynamicCompilerDriver : CompilerDriver() {
companion object {
// Will become non-trivial in the future.
fun supportsConfig(): Boolean =
false
}
override fun run(config: KonanConfig, environment: KotlinCoreEnvironment) = when (config.produce) {
CompilerOutputKind.PROGRAM -> error("Dynamic compiler driver does not support `program` output yet.")
CompilerOutputKind.DYNAMIC -> error("Dynamic compiler driver does not support `dynamic` output yet.")
CompilerOutputKind.STATIC -> error("Dynamic compiler driver does not support `static` output yet.")
CompilerOutputKind.FRAMEWORK -> error("Dynamic compiler driver does not support `framework` output yet.")
CompilerOutputKind.LIBRARY -> error("Dynamic compiler driver does not support `library` output yet.")
CompilerOutputKind.BITCODE -> error("Dynamic compiler driver does not support `bitcode` output yet.")
CompilerOutputKind.DYNAMIC_CACHE -> error("Dynamic compiler driver does not support `dynamic_cache` output yet.")
CompilerOutputKind.STATIC_CACHE -> error("Dynamic compiler driver does not support `static_cache` output yet.")
CompilerOutputKind.PRELIMINARY_CACHE -> TODO()
}
}
@@ -0,0 +1,87 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.konan.driver
import kotlinx.cinterop.usingJvmCInteropCallbacks
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.backend.common.phaser.CompilerPhase
import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.konan.util.usingNativeMemoryAllocator
import org.jetbrains.kotlin.utils.addToStdlib.cast
/**
* Static compiler uses statically-defined compilation pipeline and a single Context during whole compilation.
* Superseded by [DynamicCompilerDriver] and will be removed once dynamic driver become complete.
*/
internal class StaticCompilerDriver : CompilerDriver() {
override fun run(config: KonanConfig, environment: KotlinCoreEnvironment) {
runTopLevelPhases(config, environment)
}
private fun runTopLevelPhases(konanConfig: KonanConfig, environment: KotlinCoreEnvironment) {
val config = konanConfig.configuration
val targets = konanConfig.targetManager
if (config.get(KonanConfigKeys.LIST_TARGETS) ?: false) {
targets.list()
}
val context = Context(konanConfig)
context.environment = environment
context.phaseConfig.konanPhasesConfig(konanConfig) // TODO: Wrong place to call it
if (konanConfig.infoArgsOnly) return
if (!context.frontendPhase()) return
usingNativeMemoryAllocator {
usingJvmCInteropCallbacks {
try {
toplevelPhase.cast<CompilerPhase<Context, Unit, Unit>>().invokeToplevel(context.phaseConfig, context, Unit)
} finally {
context.disposeGenerationState()
}
}
}
}
// returns true if should generate code.
private fun Context.frontendPhase(): Boolean {
lateinit var analysisResult: AnalysisResult
do {
val analyzerWithCompilerReport = AnalyzerWithCompilerReport(
messageCollector,
environment.configuration.languageVersionSettings,
environment.configuration.getBoolean(CLIConfigurationKeys.RENDER_DIAGNOSTIC_INTERNAL_NAME)
)
// Build AST and binding info.
analyzerWithCompilerReport.analyzeAndReport(environment.getSourceFiles()) {
TopDownAnalyzerFacadeForKonan.analyzeFiles(environment.getSourceFiles(), this)
}
if (analyzerWithCompilerReport.hasErrors()) {
throw KonanCompilationException()
}
analysisResult = analyzerWithCompilerReport.analysisResult
if (analysisResult is AnalysisResult.RetryWithAdditionalRoots) {
environment.addKotlinSourceRoots(analysisResult.additionalKotlinRoots)
}
} while(analysisResult is AnalysisResult.RetryWithAdditionalRoots)
moduleDescriptor = analysisResult.moduleDescriptor
bindingContext = analysisResult.bindingContext
return analysisResult.shouldGenerateCode
}
}