From 6ef4277d896cacb6332587f1acb5fcb96b735bca Mon Sep 17 00:00:00 2001 From: Sergey Bogolepov Date: Wed, 5 Oct 2022 12:28:22 +0300 Subject: [PATCH] [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. --- .../kotlin/backend/konan/KonanDriver.kt | 85 +++--------------- .../backend/konan/driver/CompilerDriver.kt | 19 ++++ .../konan/driver/DynamicCompilerDriver.kt | 34 ++++++++ .../konan/driver/StaticCompilerDriver.kt | 87 +++++++++++++++++++ 4 files changed, 153 insertions(+), 72 deletions(-) create mode 100644 kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/CompilerDriver.kt create mode 100644 kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/DynamicCompilerDriver.kt create mode 100644 kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/StaticCompilerDriver.kt diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanDriver.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanDriver.kt index a554320cd82..dc581664874 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanDriver.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanDriver.kt @@ -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>().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 -} +} \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/CompilerDriver.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/CompilerDriver.kt new file mode 100644 index 00000000000..72d8f3009bb --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/CompilerDriver.kt @@ -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) +} diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/DynamicCompilerDriver.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/DynamicCompilerDriver.kt new file mode 100644 index 00000000000..1313536f25a --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/DynamicCompilerDriver.kt @@ -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() + } +} \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/StaticCompilerDriver.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/StaticCompilerDriver.kt new file mode 100644 index 00000000000..6408d906f4a --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/StaticCompilerDriver.kt @@ -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>().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 + } + +} \ No newline at end of file