From 9ed97a27f1ce0701bcddfd66109cfc7896c058fc Mon Sep 17 00:00:00 2001 From: Sergey Bogolepov Date: Tue, 6 Jul 2021 14:53:59 +0700 Subject: [PATCH] [K/N] Introduce LLD compatibility checker for MinGW We are going to switch to LLD linker for MinGW targets. Right now LLD for MinGW doesn't support all features of ld.bfd and thus this change might be breaking for some users. To make transition smoother, we run lld -### to show a warning to user so they can update their compilation options before LLD will be turned on by default. More details: https://youtrack.jetbrains.com/issue/KT-47605 --- .../org/jetbrains/kotlin/cli/bc/K2Native.kt | 9 +++++ .../cli/bc/K2NativeCompilerArguments.kt | 7 ++++ .../backend/konan/KonanConfigurationKeys.kt | 1 + .../jetbrains/kotlin/backend/konan/Linker.kt | 19 ++++++++++ kotlin-native/konan/konan.properties | 10 +++-- .../kotlin/konan/target/Configurables.kt | 5 ++- .../jetbrains/kotlin/konan/target/Linker.kt | 37 ++++++++++++++++--- 7 files changed, 78 insertions(+), 10 deletions(-) diff --git a/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt b/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt index 74d18d45bfe..0d2863c7f69 100644 --- a/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt +++ b/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt @@ -298,6 +298,15 @@ class K2Native : CLICompiler() { configuration.report(ERROR, "-Xgc-aggressive is only supported for -memory-model experimental") } put(GARBAGE_COLLECTOR_AGRESSIVE, arguments.gcAggressive) + put(CHECK_LLD_COMPATIBILITY, when (val it = arguments.checkLldCompatibility) { + "enable" -> true + "disable" -> false + null -> true + else -> { + configuration.report(ERROR, "Unsupported '-Xcheck-compatibility-with-lld' value: $it. Possible values are 'enable'/'disable'") + true + } + }) } } } diff --git a/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt b/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt index 66c94c38d16..947f443d8c7 100644 --- a/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt +++ b/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt @@ -314,6 +314,13 @@ class K2NativeCompilerArguments : CommonCompilerArguments() { @Argument(value="-Xgc-aggressive", description = "Make GC agressive. Works only with -memory-model experimental") var gcAggressive: Boolean = false + @Argument( + value = "-Xcheck-compatibility-with-lld", + valueDescription = "{disable|enable}", + description = "Check that linker flags are compatible with LLD." + ) + var checkLldCompatibility: String? = null + override fun configureAnalysisFlags(collector: MessageCollector, languageVersion: LanguageVersion): MutableMap, Any> = super.configureAnalysisFlags(collector, languageVersion).also { val useExperimental = it[AnalysisFlags.useExperimental] as List<*> diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt index 442a1348497..2a50bb67d43 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt @@ -158,6 +158,7 @@ class KonanConfigKeys { = CompilerConfigurationKey.create("when to destroy runtime") val GARBAGE_COLLECTOR: CompilerConfigurationKey = CompilerConfigurationKey.create("gc") val GARBAGE_COLLECTOR_AGRESSIVE: CompilerConfigurationKey = CompilerConfigurationKey.create("turn on agressive GC mode") + val CHECK_LLD_COMPATIBILITY: CompilerConfigurationKey = CompilerConfigurationKey.create("check compatibility with LLD") } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Linker.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Linker.kt index 9c536afb26a..fe23c3c51c3 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Linker.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Linker.kt @@ -145,6 +145,8 @@ internal class Linker(val context: Context) { BitcodeEmbedding.getLinkerOptions(context.config) + linkerInput.caches.dynamic + libraryProvidedLinkerFlags + additionalLinkerArgs + + checkLldCompatibility() val finalOutputCommands = linker.finalLinkCommands( objectFiles = linkerInput.objectFiles, executable = executable, @@ -176,6 +178,23 @@ internal class Linker(val context: Context) { return executable } + private fun checkLldCompatibility() { + if (linker is MingwLinker && config.getBoolean(KonanConfigKeys.CHECK_LLD_COMPATIBILITY)) { + linker.lldCompatibilityChecker = { command -> + command.logWith(context::log) + val result = command.getResult(withErrors = true) + if (result.exitCode != 0) { + val message = """ + Kotlin/Native will switch from ld to LLD linker in future releases and this warning will become an error. + See https://youtrack.jetbrains.com/issue/KT-47605 for details. + ${result.outputLines.joinToString("\n")} + """.lineSequence().map { it.trim() }.joinToString("\n") + context.reportCompilationWarning(message) + } + } + } + } + private fun shouldPerformPreLink(caches: CachesToLink, linkerOutputKind: LinkerOutputKind): Boolean { // Pre-link is only useful when producing static library. Otherwise its just a waste of time. val isStaticLibrary = linkerOutputKind == LinkerOutputKind.STATIC_LIBRARY && diff --git a/kotlin-native/konan/konan.properties b/kotlin-native/konan/konan.properties index 59972af1880..014a00d01c5 100644 --- a/kotlin-native/konan/konan.properties +++ b/kotlin-native/konan/konan.properties @@ -868,12 +868,14 @@ runtimeDefinitions.android_x64 = __ANDROID__ USE_GCC_UNWIND=1 USE_ELF_SYMBOLS=1 llvmHome.mingw_x64 = $llvm.mingw_x64.dev targetToolchain.mingw_x64 = msys2-mingw-w64-x86_64-clang-llvm-lld-compiler_rt-8.0.1 libffiDir.mingw_x64 = libffi-3.2.1-mingw-w64-x86-64 +lldLocation.mingw_x64 = lld-11.1.0-windows-x64/ld.lld.exe targetToolchain.linux_x64-mingw_x64 = msys2-mingw-w64-x86_64-clang-llvm-lld-compiler_rt-8.0.1 targetToolchain.macos_x64-mingw_x64 = msys2-mingw-w64-x86_64-clang-llvm-lld-compiler_rt-8.0.1 # for Windows we are currently using LLDB 9 dependencies.mingw_x64 = \ - lldb-2-windows + lldb-2-windows \ + lld-11.1.0-windows-x64 targetTriple.mingw_x64 = x86_64-pc-windows-gnu targetSysRoot.mingw_x64 = msys2-mingw-w64-x86_64-clang-llvm-lld-compiler_rt-8.0.1 @@ -897,8 +899,10 @@ runtimeDefinitions.mingw_x64 = USE_GCC_UNWIND=1 USE_PE_COFF_SYMBOLS=1 KONAN_WIND # Windows i686, based on mingw-w64. targetToolchain.mingw_x64-mingw_x86 = msys2-mingw-w64-i686-clang-llvm-lld-compiler_rt-8.0.1 +lldLocation.mingw_x86 = lld-11.1.0-windows-x64/ld.lld.exe dependencies.mingw_x64-mingw_x86 = \ - msys2-mingw-w64-i686-clang-llvm-lld-compiler_rt-8.0.1 + msys2-mingw-w64-i686-clang-llvm-lld-compiler_rt-8.0.1 \ + lld-11.1.0-windows-x64 targetToolchain.linux_x64-mingw_x86 = msys2-mingw-w64-i686-clang-llvm-lld-compiler_rt-8.0.1 targetToolchain.macos_x64-mingw_x86 = msys2-mingw-w64-i686-clang-llvm-lld-compiler_rt-8.0.1 dependencies.linux_x64-mingw_x86 = \ @@ -924,7 +928,7 @@ linkerDynamicFlags.mingw_x86 = -shared linkerKonanFlags.mingw_x86 = -static-libgcc -static-libstdc++ \ -Wl,--dynamicbase \ -Wl,-Bstatic,--whole-archive -lwinpthread -Wl,--no-whole-archive,-Bdynamic \ - -Wl,--defsym,___cxa_demangle=_Konan_cxa_demangle + -Wl,--defsym,__cxa_demangle=Konan_cxa_demangle mimallocLinkerDependencies.mingw_x86 = -lbcrypt linkerOptimizationFlags.mingw_x86 = -Wl,--gc-sections runtimeDefinitions.mingw_x86 = USE_GCC_UNWIND=1 USE_PE_COFF_SYMBOLS=1 KONAN_WINDOWS=1 \ diff --git a/kotlin-native/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Configurables.kt b/kotlin-native/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Configurables.kt index 8b1ba264aa0..92e2a0b8c2b 100644 --- a/kotlin-native/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Configurables.kt +++ b/kotlin-native/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Configurables.kt @@ -104,7 +104,10 @@ interface AppleConfigurables : Configurables, ClangFlags { val absoluteAdditionalToolsDir get() = absolute(additionalToolsDir) } -interface MingwConfigurables : Configurables, ClangFlags +interface MingwConfigurables : Configurables, ClangFlags { + val lldLocation get() = targetString("lldLocation")!! + val absoluteLldLocation get() = absolute(lldLocation) +} interface GccConfigurables : Configurables, ClangFlags { val gccToolchain get() = targetString("gccToolchain") diff --git a/kotlin-native/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Linker.kt b/kotlin-native/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Linker.kt index 11b057dd0db..37247e0e099 100644 --- a/kotlin-native/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Linker.kt +++ b/kotlin-native/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Linker.kt @@ -438,6 +438,11 @@ class MingwLinker(targetProperties: MingwConfigurables) return if (dir != null) "$dir/lib/windows/libclang_rt.$libraryName-$targetSuffix.a" else null } + /** + * Handle to command that runs LLD -### (i.e. without actual linkage) with arguments from [finalLinkCommands]. + */ + var lldCompatibilityChecker: ((Command) -> Unit)? = null + override fun finalLinkCommands(objectFiles: List, executable: ExecutableFile, libraries: List, linkerArgs: List, optimize: Boolean, debug: Boolean, @@ -451,10 +456,11 @@ class MingwLinker(targetProperties: MingwConfigurables) return staticGnuArCommands(ar, executable, objectFiles, libraries) val dynamic = kind == LinkerOutputKind.DYNAMIC_LIBRARY - return listOf(when { - HostManager.hostIsMingw -> Command(linker) - else -> Command("wine64", "$linker.exe") - }.apply { + + fun Command.constructLinkerArguments( + additionalArguments: List = listOf(), + skipDefaultArguments: List = listOf() + ): Command = apply { +listOf("-o", executable) +objectFiles // --gc-sections flag may affect profiling. @@ -466,9 +472,28 @@ class MingwLinker(targetProperties: MingwConfigurables) +libraries if (needsProfileLibrary) +profileLibrary!! +linkerArgs - +linkerKonanFlags + +linkerKonanFlags.filterNot { it in skipDefaultArguments } if (mimallocEnabled) +mimallocLinkerDependencies - }) + +additionalArguments + } + + if (HostManager.hostIsMingw) { + lldCompatibilityChecker?.let { checkLldCompatibiity -> + // -### flag allows to avoid actual linkage process. + val lldCommand = Command(linker).constructLinkerArguments( + // Add -fuse-ld to the end of the list to override previous appearances. + additionalArguments = listOf("-fuse-ld=$absoluteLldLocation", "-Wl,-###"), + // LLD doesn't support defsym. + skipDefaultArguments = listOf("-Wl,--defsym,__cxa_demangle=Konan_cxa_demangle") + ) + checkLldCompatibiity(lldCommand) + } + } + + return listOf(when { + HostManager.hostIsMingw -> Command(linker) + else -> Command("wine64", "$linker.exe") + }.constructLinkerArguments()) } }