diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt index 4e85a3ecb68..fc1925c1f3d 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt @@ -50,7 +50,7 @@ internal fun produceCStubs(context: Context) { context.messageCollector, context.inVerbosePhase ).forEach { - parseAndLinkBitcodeFile(llvmModule, it.absolutePath) + parseAndLinkBitcodeFile(context, llvmModule, it.absolutePath) } } @@ -76,7 +76,7 @@ private fun linkAllDependencies(context: Context, generatedBitcodeFiles: List) { + diagnostics.forEach { + when (it.severity) { + LlvmDiagnostic.Severity.ERROR -> throw Error(it.message) + LlvmDiagnostic.Severity.WARNING -> if (context.inVerbosePhase || !policy.suppressWarning(it)) { + context.messageCollector.report(CompilerMessageSeverity.WARNING, it.message) + } else { + // else block is required by the compiler. + } + LlvmDiagnostic.Severity.REMARK, + LlvmDiagnostic.Severity.NOTE -> { + context.log { "${it.severity}: ${it.message}" } + } + }.also {} // Make exhaustive. + } + } +} diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/diagnostics.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/diagnostics.kt new file mode 100644 index 00000000000..cb693552538 --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/diagnostics.kt @@ -0,0 +1,95 @@ +/* + * Copyright 2010-2021 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.llvm + +import kotlinx.cinterop.* +import llvm.* +import org.jetbrains.kotlin.backend.konan.llvm.LlvmDiagnostic.Severity + +// Note: we rely on LLVM reporting diagnostics to callback. +// This happens during execution of LLVM C++ code, so the callback shouldn't throw Kotlin exceptions. +// That's why the supposed operation is: collect diagnostics while running LLVM, then handle after returning to Kotlin. +// +// By default LLVM prints messages to stderr and terminates the process on errors, +// which isn't ok for Kotlin compiler, e.g. when embedding it into Gradle daemon process. + +internal class LlvmDiagnostic(val severity: Severity, val message: String) { + enum class Severity { + ERROR, + WARNING, + REMARK, + NOTE + } +} + +internal class LlvmDiagnosticCollector { + private val diagnostics = mutableListOf() + + fun add(diagnostic: LlvmDiagnostic) { + diagnostics += diagnostic + } + + fun flush(handler: LlvmDiagnosticHandler) { + handler.handle(diagnostics) + diagnostics.clear() + } +} + +internal interface LlvmDiagnosticHandler { + fun handle(diagnostics: List) +} + +internal inline fun withLlvmDiagnosticHandler(handler: LlvmDiagnosticHandler, block: () -> R): R { + val collector = LlvmDiagnosticCollector() + return try { + withLlvmDiagnosticCollector(collector, block) + } finally { + collector.flush(handler) + } +} + +internal inline fun withLlvmDiagnosticCollector(collector: LlvmDiagnosticCollector, block: () -> R): R { + val handler: LLVMDiagnosticHandler = staticCFunction { diagnostic, context -> + context!!.asStableRef().get().add(createLlvmDiagnostic(diagnostic)) + } + val context = StableRef.create(collector) + return try { + withLlvmDiagnosticHandler(handler, context.asCPointer(), block) + } finally { + context.dispose() + } +} + +internal inline fun withLlvmDiagnosticHandler( + handler: LLVMDiagnosticHandler, + context: COpaquePointer, + block: () -> R +): R { + val llvmContext = llvmContext + val currentHandler = LLVMContextGetDiagnosticHandler(llvmContext) + val currentContext = LLVMContextGetDiagnosticContext(llvmContext) + + return try { + LLVMContextSetDiagnosticHandler(llvmContext, handler, context) + block() + } finally { + LLVMContextSetDiagnosticHandler(llvmContext, currentHandler, currentContext) + } +} + +private fun createLlvmDiagnostic(diagnostic: LLVMDiagnosticInfoRef?) = if (diagnostic == null) { + LlvmDiagnostic(Severity.ERROR, "Unknown LLVM error") +} else { + LlvmDiagnostic( + severity = when (LLVMGetDiagInfoSeverity(diagnostic)) { + LLVMDiagnosticSeverity.LLVMDSError -> Severity.ERROR + LLVMDiagnosticSeverity.LLVMDSWarning -> Severity.WARNING + LLVMDiagnosticSeverity.LLVMDSRemark -> Severity.REMARK + LLVMDiagnosticSeverity.LLVMDSNote -> Severity.NOTE + }, + message = LLVMGetDiagInfoDescription(diagnostic)?.toKString() ?: "Unknown LLVM error" + ) +} diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/linkModules.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/linkModules.kt new file mode 100644 index 00000000000..878c1d71d92 --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/linkModules.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2010-2021 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.llvm + +import llvm.* +import org.jetbrains.kotlin.backend.konan.Context + +internal fun llvmLinkModules2(context: Context, dest: LLVMModuleRef, src: LLVMModuleRef): LLVMBool = + withLlvmDiagnosticHandler(DefaultLlvmDiagnosticHandler(context)) { + LLVMLinkModules2(dest, src) + } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objc/linkObjC.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objc/linkObjC.kt index 6240faaa294..0887b1f0330 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objc/linkObjC.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objc/linkObjC.kt @@ -25,7 +25,7 @@ internal fun linkObjC(context: Context) { patchBuilder.buildAndApply(parsedModule) - val failed = LLVMLinkModules2(context.llvmModule!!, parsedModule) + val failed = llvmLinkModules2(context, context.llvmModule!!, parsedModule) if (failed != 0) { throw Error("failed to link $bitcodeFile") }