From 6cb4d0c241f099367e47920c41a00c0cae72277f Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Mon, 8 Oct 2018 14:49:18 +0300 Subject: [PATCH] Implement -Xembed-bitcode and -Xembed-bitcode-marker These options currently make the produced framework bitcode-friendly in release and debug environments correspondigly without embedding real bitcode. --- FAQ.md | 19 ++++++++ .../org/jetbrains/kotlin/cli/bc/K2Native.kt | 47 +++++++++++++++++-- .../cli/bc/K2NativeCompilerArguments.kt | 8 ++++ .../kotlin/backend/konan/BitcodeEmbedding.kt | 41 ++++++++++++++++ .../backend/konan/KonanConfigurationKeys.kt | 2 + .../kotlin/backend/konan/LinkStage.kt | 1 + .../kotlin/backend/konan/llvm/IrToBitcode.kt | 2 + backend.native/tests/build.gradle | 5 ++ .../org/jetbrains/kotlin/FrameworkTest.kt | 6 ++- 9 files changed, 126 insertions(+), 5 deletions(-) create mode 100644 backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BitcodeEmbedding.kt diff --git a/FAQ.md b/FAQ.md index 9cf4ba92045..97bc50a8d38 100644 --- a/FAQ.md +++ b/FAQ.md @@ -50,6 +50,25 @@ framework("MyCustomFramework") { +### Q: How do I enable bitcode for my Kotlin framework? + +A: Use either `-Xembed-bitcode` or `-Xembed-bitcode-marker` compiler option +or matching Gradle DSL statement, i.e. + +
+ +```groovy +framework("MyCustomFramework") { + extraOpts '-Xembed-bitcode' // for release binaries + // or '-Xembed-bitcode-marker' for debug binaries +} +``` + +These options have nearly the same effect as clang's `-fembed-bitcode`/`-fembed-bitcode-marker` +and swiftc's `-embed-bitcode`/`-embed-bitcode-marker`. + +
+ ### Q: Why do I see `InvalidMutabilityException`? A: It likely happens, because you are trying to mutate a frozen object. An object can transfer to the diff --git a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt index b749ffc9e5e..7c0ab1152a4 100644 --- a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt +++ b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt @@ -14,17 +14,14 @@ import org.jetbrains.kotlin.cli.common.CLITool import org.jetbrains.kotlin.cli.common.CommonCompilerPerformanceManager import org.jetbrains.kotlin.cli.common.ExitCode import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot -import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoots import org.jetbrains.kotlin.cli.common.config.kotlinSourceRoots -import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.ERROR -import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.WARNING +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.* import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.plugins.PluginCliParser import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.Services -import org.jetbrains.kotlin.konan.KonanAbiVersion import org.jetbrains.kotlin.konan.KonanVersion import org.jetbrains.kotlin.konan.file.File import org.jetbrains.kotlin.konan.target.CompilerOutputKind @@ -190,6 +187,8 @@ class K2Native : CLICompiler() { }) if (arguments.friendModules != null) put(FRIEND_MODULES, arguments.friendModules!!.split(File.pathSeparator).filterNot(String::isEmpty)) + + put(BITCODE_EMBEDDING_MODE, selectBitcodeEmbeddingMode(this, arguments, outputKind)) } } } @@ -214,5 +213,45 @@ class K2Native : CLICompiler() { } } } + +private fun selectBitcodeEmbeddingMode( + configuration: CompilerConfiguration, + arguments: K2NativeCompilerArguments, + outputKind: CompilerOutputKind +): BitcodeEmbedding.Mode { + + if (outputKind != CompilerOutputKind.FRAMEWORK) { + return BitcodeEmbedding.Mode.NONE.also { + val flag = when { + arguments.embedBitcodeMarker -> EMBED_BITCODE_MARKER_FLAG + arguments.embedBitcode -> EMBED_BITCODE_FLAG + else -> return@also + } + + configuration.report( + STRONG_WARNING, + "'$flag' is only supported when producing frameworks, " + + "but the compiler is producing ${outputKind.name.toLowerCase()}" + ) + } + } + + return when { + arguments.embedBitcodeMarker -> { + if (arguments.embedBitcode) { + configuration.report( + STRONG_WARNING, + "'$EMBED_BITCODE_FLAG' is ignored because '$EMBED_BITCODE_MARKER_FLAG' is specified" + ) + } + BitcodeEmbedding.Mode.MARKER + } + arguments.embedBitcode -> { + BitcodeEmbedding.Mode.FULL + } + else -> BitcodeEmbedding.Mode.NONE + } +} + fun main(args: Array) = K2Native.main(args) diff --git a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt index 1bac1f8fa35..bc7daf8ec01 100644 --- a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt +++ b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt @@ -94,6 +94,12 @@ class K2NativeCompilerArguments : CommonCompilerArguments() { @Argument(value = "-Xdisable", deprecatedName = "--disable", valueDescription = "", description = "Disable backend phase") var disablePhases: Array? = null + @Argument(value = EMBED_BITCODE_FLAG, description = "Embed LLVM IR bitcode as data") + var embedBitcode: Boolean = false + + @Argument(value = EMBED_BITCODE_MARKER_FLAG, description = "Embed placeholder LLVM IR data as a marker") + var embedBitcodeMarker: Boolean = false + @Argument(value = "-Xenable", deprecatedName = "--enable", valueDescription = "", description = "Enable backend phase") var enablePhases: Array? = null @@ -153,3 +159,5 @@ class K2NativeCompilerArguments : CommonCompilerArguments() { } } +const val EMBED_BITCODE_FLAG = "-Xembed-bitcode" +const val EMBED_BITCODE_MARKER_FLAG = "-Xembed-bitcode-marker" diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BitcodeEmbedding.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BitcodeEmbedding.kt new file mode 100644 index 00000000000..87e37addc89 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BitcodeEmbedding.kt @@ -0,0 +1,41 @@ +package org.jetbrains.kotlin.backend.konan + +import org.jetbrains.kotlin.backend.konan.llvm.Int8 +import org.jetbrains.kotlin.backend.konan.llvm.Llvm +import org.jetbrains.kotlin.konan.target.CompilerOutputKind + +object BitcodeEmbedding { + + enum class Mode { + NONE, MARKER, FULL + } + + internal fun getLinkerOptions(config: KonanConfig): List = when (config.bitcodeEmbeddingMode) { + Mode.NONE -> emptyList() + Mode.MARKER -> listOf("-bitcode_bundle", "-bitcode_process_mode", "marker") + Mode.FULL -> listOf("-bitcode_bundle") + } + + private val KonanConfig.bitcodeEmbeddingMode get() = configuration.get(KonanConfigKeys.BITCODE_EMBEDDING_MODE)!!.also { + require(it == Mode.NONE || this.produce == CompilerOutputKind.FRAMEWORK) { + "${it.name.toLowerCase()} bitcode embedding mode is not supported when producing ${this.produce.name.toLowerCase()}" + } + } + + internal fun processModule(llvm: Llvm) = when (llvm.context.config.bitcodeEmbeddingMode) { + Mode.NONE -> {} + Mode.MARKER -> { + addEmptyMarker(llvm, "konan_llvm_bitcode", "__LLVM,__bitcode") + addEmptyMarker(llvm, "konan_llvm_cmdline", "__LLVM,__cmdline") + } + Mode.FULL -> { + addEmptyMarker(llvm, "konan_llvm_asm", "__LLVM,__asm") + } + } + + private fun addEmptyMarker(llvm: Llvm, name: String, section: String) { + val global = llvm.staticData.placeGlobal(name, Int8(0), isExported = false) + global.setSection(section) + llvm.usedGlobals += global.llvmGlobal + } +} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt index 62ad8452c39..ab898c93b63 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt @@ -20,6 +20,8 @@ class KonanConfigKeys { = CompilerConfigurationKey.create("add debug information") val DISABLED_PHASES: CompilerConfigurationKey> = CompilerConfigurationKey.create("disable backend phases") + val BITCODE_EMBEDDING_MODE: CompilerConfigurationKey + = CompilerConfigurationKey.create("bitcode embedding mode") val ENABLE_ASSERTIONS: CompilerConfigurationKey = CompilerConfigurationKey.create("enable runtime assertions in generated code") val ENABLED_PHASES: CompilerConfigurationKey> diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt index 60dc6b95575..e90c53c79f0 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt @@ -183,6 +183,7 @@ internal class LinkStage(val context: Context, val phaser: PhaseManager) { libraries = linker.targetLibffi + linker.linkStaticLibraries(includedBinaries), linkerArgs = entryPointSelector + asLinkerArgs(config.getNotNull(KonanConfigKeys.LINKER_ARGS)) + + BitcodeEmbedding.getLinkerOptions(context.config) + libraryProvidedLinkerFlags + frameworkLinkerArgs, optimize = optimize, debug = debug, kind = linkerOutput, outputDsymBundle = context.config.outputFile + ".dSYM").forEach { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index 76d7beedd2b..ba89211a33a 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -383,6 +383,8 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map