From 73a52b608106e95be3a36b78a02baef01e2a7d2b Mon Sep 17 00:00:00 2001 From: Sergey Bogolepov Date: Tue, 29 Nov 2022 16:23:03 +0200 Subject: [PATCH] [K/N] Support CompilerOutputKind.LIBRARY in the dynamic driver --- .../konan/driver/DynamicCompilerDriver.kt | 51 ++++++++++--- .../backend/konan/driver/phases/Serializer.kt | 71 ++++++++++++++++++ .../backend/konan/driver/phases/WriteKlib.kt | 73 +++++++++++++++++++ 3 files changed, 185 insertions(+), 10 deletions(-) create mode 100644 kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Serializer.kt create mode 100644 kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/WriteKlib.kt 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 index 1313536f25a..855660caf28 100644 --- 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 @@ -5,9 +5,12 @@ package org.jetbrains.kotlin.backend.konan.driver +import kotlinx.cinterop.usingJvmCInteropCallbacks import org.jetbrains.kotlin.backend.konan.KonanConfig +import org.jetbrains.kotlin.backend.konan.driver.phases.* import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.konan.target.CompilerOutputKind +import org.jetbrains.kotlin.konan.util.usingNativeMemoryAllocator /** * Dynamic driver does not "know" upfront which phases will be executed. @@ -20,15 +23,43 @@ internal class DynamicCompilerDriver : CompilerDriver() { 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() + override fun run(config: KonanConfig, environment: KotlinCoreEnvironment) { + usingNativeMemoryAllocator { + usingJvmCInteropCallbacks { + PhaseEngine.startTopLevel(config) { engine -> + 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 -> produceKlib(engine, config, environment) + 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() + } + } + } + } + } + + private fun produceKlib(engine: PhaseEngine, config: KonanConfig, environment: KotlinCoreEnvironment) { + val frontendOutput = engine.useContext(FrontendContextImpl(config)) { it.runFrontend(environment) } + if (frontendOutput is FrontendPhaseOutput.ShouldNotGenerateCode) { + return + } + require(frontendOutput is FrontendPhaseOutput.Full) + val psiToIrOutput = if (config.metadataKlib) { + null + } else { + val psiToIrContext = PsiToIrContextImpl(config, frontendOutput.moduleDescriptor, frontendOutput.bindingContext) + val psiToIrOutput = engine.useContext(psiToIrContext) { psiToIrEngine -> + val output = psiToIrEngine.runPsiToIr(frontendOutput, isProducingLibrary = true) + output + } + psiToIrOutput + } + val serializerOutput = engine.runSerializer(frontendOutput.moduleDescriptor, psiToIrOutput) + engine.writeKlib(serializerOutput) } } \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Serializer.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Serializer.kt new file mode 100644 index 00000000000..f420899e8e4 --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Serializer.kt @@ -0,0 +1,71 @@ +/* + * 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.phases + +import org.jetbrains.kotlin.backend.common.serialization.CompatibilityMode +import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataMonolithicSerializer +import org.jetbrains.kotlin.backend.konan.driver.PhaseContext +import org.jetbrains.kotlin.backend.konan.driver.PhaseEngine +import org.jetbrains.kotlin.backend.konan.serialization.KonanIrModuleSerializer +import org.jetbrains.kotlin.config.CommonConfigurationKeys +import org.jetbrains.kotlin.config.languageVersionSettings +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.ir.util.IrMessageLogger +import org.jetbrains.kotlin.konan.library.KonanLibrary +import org.jetbrains.kotlin.library.SerializedIrModule +import org.jetbrains.kotlin.library.SerializedMetadata + +internal data class SerializerInput( + val moduleDescriptor: ModuleDescriptor, + val psiToIrOutput: PsiToIrOutput?, +) + +data class SerializerOutput( + val serializedMetadata: SerializedMetadata?, + val serializedIr: SerializedIrModule?, + val dataFlowGraph: ByteArray?, + val neededLibraries: List +) + +internal val SerializerPhase = createSimpleNamedCompilerPhase( + "Serializer", "IR serialzer", + outputIfNotEnabled = { _, _, _, _ -> SerializerOutput(null, null, null, emptyList()) } +) { context: PhaseContext, input: SerializerInput -> + val config = context.config + val expectActualLinker = config.configuration.get(CommonConfigurationKeys.EXPECT_ACTUAL_LINKER) ?: false + val messageLogger = config.configuration.get(IrMessageLogger.IR_MESSAGE_LOGGER) ?: IrMessageLogger.None + val relativePathBase = config.configuration.get(CommonConfigurationKeys.KLIB_RELATIVE_PATH_BASES) ?: emptyList() + val normalizeAbsolutePaths = config.configuration.get(CommonConfigurationKeys.KLIB_NORMALIZE_ABSOLUTE_PATH) ?: false + + val serializedIr = input.psiToIrOutput?.let { + val ir = it.irModule + KonanIrModuleSerializer( + messageLogger, ir.irBuiltins, it.expectDescriptorToSymbol, + skipExpects = !expectActualLinker, + compatibilityMode = CompatibilityMode.CURRENT, + normalizeAbsolutePaths = normalizeAbsolutePaths, + sourceBaseDirs = relativePathBase, + ).serializedIrModule(ir) + } + + val serializer = KlibMetadataMonolithicSerializer( + config.configuration.languageVersionSettings, + config.configuration.get(CommonConfigurationKeys.METADATA_VERSION)!!, + config.project, + exportKDoc = context.shouldExportKDoc(), + !expectActualLinker, includeOnlyModuleContent = true) + val serializedMetadata = serializer.serializeModule(input.moduleDescriptor) + val neededLibraries = config.librariesWithDependencies(input.moduleDescriptor) + SerializerOutput(serializedMetadata, serializedIr, null, neededLibraries) +} + +internal fun PhaseEngine.runSerializer( + moduleDescriptor: ModuleDescriptor, + psiToIrResult: PsiToIrOutput?, +): SerializerOutput { + val input = SerializerInput(moduleDescriptor, psiToIrResult) + return this.runPhase(SerializerPhase, input) +} \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/WriteKlib.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/WriteKlib.kt new file mode 100644 index 00000000000..415999c91c4 --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/WriteKlib.kt @@ -0,0 +1,73 @@ +/* + * 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.phases + +import org.jetbrains.kotlin.backend.common.serialization.KlibIrVersion +import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataVersion +import org.jetbrains.kotlin.backend.konan.KonanConfigKeys +import org.jetbrains.kotlin.backend.konan.OutputFiles +import org.jetbrains.kotlin.backend.konan.driver.PhaseContext +import org.jetbrains.kotlin.backend.konan.driver.PhaseEngine +import org.jetbrains.kotlin.konan.CURRENT +import org.jetbrains.kotlin.konan.CompilerVersion +import org.jetbrains.kotlin.konan.library.impl.buildLibrary +import org.jetbrains.kotlin.library.KotlinAbiVersion +import org.jetbrains.kotlin.library.KotlinLibraryVersioning + +internal val WriteKlibPhase = createSimpleNamedCompilerPhase( + "WriteKlib", "Write klib output", +) { context, input -> + val config = context.config + val configuration = config.configuration + val outputFiles = OutputFiles(config.outputPath, config.target, config.produce) + val nopack = configuration.getBoolean(KonanConfigKeys.NOPACK) + val output = outputFiles.klibOutputFileName(!nopack) + val libraryName = config.moduleId + val shortLibraryName = config.shortModuleName + val abiVersion = KotlinAbiVersion.CURRENT + val compilerVersion = CompilerVersion.CURRENT.toString() + val libraryVersion = configuration.get(KonanConfigKeys.LIBRARY_VERSION) + val metadataVersion = KlibMetadataVersion.INSTANCE.toString() + val irVersion = KlibIrVersion.INSTANCE.toString() + val versions = KotlinLibraryVersioning( + abiVersion = abiVersion, + libraryVersion = libraryVersion, + compilerVersion = compilerVersion, + metadataVersion = metadataVersion, + irVersion = irVersion + ) + val target = config.target + val manifestProperties = config.manifestProperties + + if (!nopack) { + val suffix = outputFiles.produce.suffix(target) + if (!output.endsWith(suffix)) { + error("please specify correct output: packed: ${!nopack}, $output$suffix") + } + } + + buildLibrary( + config.nativeLibraries, + config.includeBinaries, + input.neededLibraries, + input.serializedMetadata!!, + input.serializedIr, + versions, + target, + output, + libraryName, + nopack, + shortLibraryName, + manifestProperties, + input.dataFlowGraph + ) +} + +internal fun PhaseEngine.writeKlib( + serializationOutput: SerializerOutput, +) { + this.runPhase(WriteKlibPhase, serializationOutput) +} \ No newline at end of file