https://github.com/Keno created https://github.com/llvm/llvm-project/pull/149469
This implements the mingw `-mcrtdll` option recently added to gcc. This option is useful for having the compiler be in charge of crt version selection while only shipping a single copy of mingw for a multi-ABI toolchain. That said, there are various ABI dependent compiler libraries (e.g. libstdc++), so a certain degree of ABI awareness is nevertheless required in order to use this option correctly. See also #149434 (which this branch includes, since it touches the same code). >From e3dd50c14f79dca0d28e482f707849c92ec0df76 Mon Sep 17 00:00:00 2001 From: Keno Fischer <k...@juliahub.com> Date: Fri, 18 Jul 2025 01:02:00 +0000 Subject: [PATCH 1/2] [Driver][MinGW] Always put libc argument last, even if non-standard I was attempting to build openblas with clang in msys2's `ucrt64` environment (I'm aware of the `clang64` environment, but I wanted libstdc++). The openblas link failed with the following: ``` clang -march=native -mtune=native -m64 -O2 -fno-asynchronous-unwind-tables -O2 -DSMALL_MATRIX_OPT -DMS_ABI -DMAX_STACK_ALLOC=2048 -Wall -m64 -DF_INTERFACE_GFORT -DDYNAMIC_ARCH -DSMP_SERVER -DNO_WARMUP -DMAX_CPU_NUMBER=512 -DMAX_PARALLEL_NUMBER=1 -DBUILD_SINGLE=1 -DBUILD_DOUBLE=1 -DBUILD_COMPLEX=1 -DBUILD_COMPLEX16=1 -DVERSION=\"0.3.29\" -UASMNAME -UASMFNAME -UNAME -UCNAME -UCHAR_NAME -UCHAR_CNAME -DASMNAME= -DASMFNAME=_ -DNAME=_ -DCNAME= -DCHAR_NAME=\"_\" -DCHAR_CNAME=\"\" -DNO_AFFINITY -I.. libopenblas64_.def dllinit.obj \ -shared -o ../libopenblas64_.dll -Wl,--out-implib,../libopenblas64_.dll.a \ -Wl,--whole-archive ../libopenblas64_p-r0.3.29.a -Wl,--no-whole-archive -LC:/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/15.1.0 -LC:/msys64/ucrt64/bin/../lib/gcc -LC:/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/15.1.0/../../../../x86_64-w64-mingw32/lib/../lib -LC:/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/15.1.0/../../../../lib -LC:/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/15.1.0/../../../../x86_64-w64-mingw32/lib -LC:/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/15.1.0/../../.. -lgfortran -lmingwex -lmsvcrt -lquadmath -lm -lpthread -lmingwex -lmsvcrt -defaultlib:advapi32 -lgfortran -defaultlib:advapi32 -lgfortran C:/msys64/ucrt64/bin/ld: C:/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/15.1.0/../../../../lib/libmingw32.a(lib64_libmingw32_a-pseudo-reloc.o): in function `__report_error': D:/W/B/src/mingw-w64/mingw-w64-crt/crt/pseudo-reloc.c:157:(.text+0x59): undefined reference to `abort' C:/msys64/ucrt64/bin/ld: C:/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/15.1.0/../../../../lib/libmingw32.a(lib64_libmingw32_a-tlsthrd.o): in function `___w64_mingwthr_add_key_dtor': D:/W/B/src/mingw-w64/mingw-w64-crt/crt/tlsthrd.c:48:(.text+0xa5): undefined reference to `calloc' C:/msys64/ucrt64/bin/ld: C:/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/15.1.0/../../../../lib/libmingw32.a(lib64_libmingw32_a-pesect.o): in function `_FindPESectionByName': D:/W/B/src/mingw-w64/mingw-w64-crt/crt/pesect.c:79:(.text+0xfd): undefined reference to `strncmp' ``` These symbols come from the `-lmingw32` dep that the driver added and are ordinarily found in `-lmsvcrt`, which got skipped here, because openblas passed `-lmsvcrt` explicitly. Since we always add these libraries at the end here, I think that clang is "at fault" (as opposed to a user or packaging mistake) and should have added some crt here. To preserve the intent of letting the user override which crt is chosen, duplicate the (first) user chosen crt `-l` into this position, although we should perhaps consider an explicit `-mcrtdll` like gcc has as well. --- clang/lib/Driver/ToolChains/MinGW.cpp | 11 +++++++++-- clang/test/Driver/mingw-msvcrt.c | 8 ++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/clang/lib/Driver/ToolChains/MinGW.cpp b/clang/lib/Driver/ToolChains/MinGW.cpp index 7d093d20b3dd9..58c59aa7f2237 100644 --- a/clang/lib/Driver/ToolChains/MinGW.cpp +++ b/clang/lib/Driver/ToolChains/MinGW.cpp @@ -85,11 +85,18 @@ void tools::MinGW::Linker::AddLibGCC(const ArgList &Args, CmdArgs.push_back("-lmoldname"); CmdArgs.push_back("-lmingwex"); - for (auto Lib : Args.getAllArgValues(options::OPT_l)) + for (auto Lib : Args.getAllArgValues(options::OPT_l)) { if (StringRef(Lib).starts_with("msvcr") || StringRef(Lib).starts_with("ucrt") || - StringRef(Lib).starts_with("crtdll")) + StringRef(Lib).starts_with("crtdll")) { + Lib = (llvm::Twine("-l") + Lib).str(); + // Respect the user's chosen crt variant, but still provide it + // again as the last linker argument, because some of the libraries + // we added above may depend on it. + CmdArgs.push_back(Args.MakeArgStringRef(Lib)); return; + } + } CmdArgs.push_back("-lmsvcrt"); } diff --git a/clang/test/Driver/mingw-msvcrt.c b/clang/test/Driver/mingw-msvcrt.c index 340ce1f57b0f8..e1648630476a0 100644 --- a/clang/test/Driver/mingw-msvcrt.c +++ b/clang/test/Driver/mingw-msvcrt.c @@ -7,10 +7,10 @@ // CHECK_DEFAULT: "-lmingwex" "-lmsvcrt" "-ladvapi32" // CHECK_DEFAULT-SAME: "-lmsvcrt" "-lkernel32" "{{.*}}crtend.o" // CHECK_MSVCR120: "-lmsvcr120" -// CHECK_MSVCR120-SAME: "-lmingwex" "-ladvapi32" +// CHECK_MSVCR120-SAME: "-lmingwex" "-lmsvcr120" "-ladvapi32" // CHECK_UCRTBASE: "-lucrtbase" -// CHECK_UCRTBASE-SAME: "-lmingwex" "-ladvapi32" +// CHECK_UCRTBASE-SAME: "-lmingwex" "-lucrtbase" "-ladvapi32" // CHECK_UCRT: "-lucrt" -// CHECK_UCRT-SAME: "-lmingwex" "-ladvapi32" +// CHECK_UCRT-SAME: "-lmingwex" "-lucrt" "-ladvapi32" // CHECK_CRTDLL: "-lcrtdll" -// CHECK_CRTDLL-SAME: "-lmingwex" "-ladvapi32" +// CHECK_CRTDLL-SAME: "-lmingwex" "-lcrtdll" "-ladvapi32" >From 544048cfbd4d8fd2fc293aab119f190df40671aa Mon Sep 17 00:00:00 2001 From: Keno Fischer <k...@juliahub.com> Date: Fri, 18 Jul 2025 07:29:15 +0000 Subject: [PATCH 2/2] [clang][MinGW] Implement -mcrtdll option to swtich crt choice This implements the mingw `-mcrtdll` option recently added to gcc. This option is useful for having the compiler be in charge of crt version selection while only shipping a single copy of mingw for a multi-ABI toolchain. That said, there are various ABI dependent compiler libraries (e.g. libstdc++), so a certain degree of ABI awareness is nevertheless required in order to use this option correctly. See also #149434 --- .../clang/Basic/DiagnosticFrontendKinds.td | 3 ++ clang/include/clang/Basic/LangOptions.def | 3 ++ clang/include/clang/Basic/LangOptions.h | 17 +++++++ clang/include/clang/Driver/Options.td | 6 +++ clang/lib/Basic/Targets/OSTargets.cpp | 48 ++++++++++++++++++- clang/lib/Driver/ToolChains/Clang.cpp | 1 + clang/lib/Driver/ToolChains/MinGW.cpp | 29 ++++++----- clang/lib/Frontend/CompilerInvocation.cpp | 38 +++++++++++++++ clang/test/Driver/mingw-mcrtdll.c | 30 ++++++++++++ 9 files changed, 162 insertions(+), 13 deletions(-) create mode 100644 clang/test/Driver/mingw-mcrtdll.c diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td index 8a8db27490f06..3de97a0ec3955 100644 --- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -393,6 +393,9 @@ def warn_hlsl_langstd_minimal : "recommend using %1 instead">, InGroup<HLSLDXCCompat>; +def err_unknown_crtdll : Error<"unknown Windows/MinGW C runtime library '%0'">, + DefaultFatal; + // ClangIR frontend errors def err_cir_to_cir_transform_failed : Error< "CIR-to-CIR transformation failed">, DefaultFatal; diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index e43238ba683f2..46f03982a041b 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -493,6 +493,9 @@ LANGOPT(BoundsSafety, 1, 0, NotCompatible, "Bounds safety extension for C") LANGOPT(PreserveVec3Type, 1, 0, NotCompatible, "Preserve 3-component vector type") +ENUM_LANGOPT(MinGWCRTDll, WindowsCRTDLLVersion, 4, WindowsCRTDLLVersion::CRTDLL_Default, NotCompatible, + "MinGW specific. Controls the __MSVCRT_VERSION and related preprocessor defines.") + #undef LANGOPT #undef ENUM_LANGOPT #undef VALUE_LANGOPT diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h index 4c642c9e10c91..a0160017b6813 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -164,6 +164,23 @@ class LangOptionsBase { MSVC2022_9 = 1939, }; + enum WindowsCRTDLLVersion { + CRTDLL_Default, + CRTDLL, + MSVCRT10, + MSVCRT20, + MSVCRT40, + MSVCRTD, + MSVCR70, + MSVCR71, + MSVCR80, + MSVCR90, + MSVCR100, + MSVCR110, + MSVCR120, + UCRT + }; + enum SYCLMajorVersion { SYCL_None, SYCL_2017, diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index d0b54a446309b..6ad978c525812 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1625,6 +1625,12 @@ defm auto_import : BoolFOption<"auto-import", PosFlag<SetTrue, [], [], "MinGW specific. Enable code generation support for " "automatic dllimport, and enable support for it in the linker. " "Enabled by default.">>; +def mcrtdll_EQ : Joined<["-"], "mcrtdll=">, + Group<m_Group>, + Visibility<[ClangOption, CC1Option]>, + HelpText<"MinGW specific. Changes preprocessor flags and " + "linker options to use the" + "specified C runtime library.">; } // let Flags = [TargetSpecific] // In the future this option will be supported by other offloading diff --git a/clang/lib/Basic/Targets/OSTargets.cpp b/clang/lib/Basic/Targets/OSTargets.cpp index e744e84a5b079..8e48228d1220f 100644 --- a/clang/lib/Basic/Targets/OSTargets.cpp +++ b/clang/lib/Basic/Targets/OSTargets.cpp @@ -141,8 +141,54 @@ static void addMinGWDefines(const llvm::Triple &Triple, const LangOptions &Opts, DefineStd(Builder, "WIN64", Opts); Builder.defineMacro("__MINGW64__"); } - Builder.defineMacro("__MSVCRT__"); Builder.defineMacro("__MINGW32__"); + if (Opts.getMinGWCRTDll() == LangOptions::WindowsCRTDLLVersion::CRTDLL) { + Builder.defineMacro("__CRTDLL__"); + } else { + Builder.defineMacro("__MSVCRT__"); + switch (Opts.getMinGWCRTDll()) { + case LangOptions::WindowsCRTDLLVersion::CRTDLL_Default: + break; + case LangOptions::WindowsCRTDLLVersion::MSVCRT10: + Builder.defineMacro("__MSVCRT_VERSION__", "0x100"); + break; + case LangOptions::WindowsCRTDLLVersion::MSVCRT20: + Builder.defineMacro("__MSVCRT_VERSION__", "0x200"); + break; + case LangOptions::WindowsCRTDLLVersion::MSVCRT40: + Builder.defineMacro("__MSVCRT_VERSION__", "0x400"); + break; + case LangOptions::WindowsCRTDLLVersion::MSVCRTD: + Builder.defineMacro("__MSVCRT_VERSION__", "0x600"); + break; + case LangOptions::WindowsCRTDLLVersion::MSVCR70: + Builder.defineMacro("__MSVCRT_VERSION__", "0x700"); + break; + case LangOptions::WindowsCRTDLLVersion::MSVCR71: + Builder.defineMacro("__MSVCRT_VERSION__", "0x701"); + break; + case LangOptions::WindowsCRTDLLVersion::MSVCR80: + Builder.defineMacro("__MSVCRT_VERSION__", "0x800"); + break; + case LangOptions::WindowsCRTDLLVersion::MSVCR90: + Builder.defineMacro("__MSVCRT_VERSION__", "0x900"); + break; + case LangOptions::WindowsCRTDLLVersion::MSVCR100: + Builder.defineMacro("__MSVCRT_VERSION__", "0xA00"); + break; + case LangOptions::WindowsCRTDLLVersion::MSVCR110: + Builder.defineMacro("__MSVCRT_VERSION__", "0xB00"); + break; + case LangOptions::WindowsCRTDLLVersion::MSVCR120: + Builder.defineMacro("__MSVCRT_VERSION__", "0xC00"); + break; + case LangOptions::WindowsCRTDLLVersion::UCRT: + Builder.defineMacro("_UCRT"); + break; + default: + llvm_unreachable("Unknown MinGW CRT version"); + } + } addCygMingDefines(Opts, Builder); } diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index fe1865888bdd0..79344f4e760d9 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -5970,6 +5970,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, if (Triple.isWindowsGNUEnvironment()) { Args.addOptOutFlag(CmdArgs, options::OPT_fauto_import, options::OPT_fno_auto_import); + Args.addLastArg(CmdArgs, options::OPT_mcrtdll_EQ); } if (Args.hasFlag(options::OPT_fms_volatile, options::OPT_fno_ms_volatile, diff --git a/clang/lib/Driver/ToolChains/MinGW.cpp b/clang/lib/Driver/ToolChains/MinGW.cpp index 58c59aa7f2237..7223cda83fa70 100644 --- a/clang/lib/Driver/ToolChains/MinGW.cpp +++ b/clang/lib/Driver/ToolChains/MinGW.cpp @@ -85,19 +85,24 @@ void tools::MinGW::Linker::AddLibGCC(const ArgList &Args, CmdArgs.push_back("-lmoldname"); CmdArgs.push_back("-lmingwex"); - for (auto Lib : Args.getAllArgValues(options::OPT_l)) { - if (StringRef(Lib).starts_with("msvcr") || - StringRef(Lib).starts_with("ucrt") || - StringRef(Lib).starts_with("crtdll")) { - Lib = (llvm::Twine("-l") + Lib).str(); - // Respect the user's chosen crt variant, but still provide it - // again as the last linker argument, because some of the libraries - // we added above may depend on it. - CmdArgs.push_back(Args.MakeArgStringRef(Lib)); - return; - } + + if (Arg *A = Args.getLastArg(options::OPT_mcrtdll_EQ)) { + std::string mcrtdll = (Twine("-l") + A->getValue()).str(); + CmdArgs.push_back(Args.MakeArgStringRef(mcrtdll)); + } else { + for (auto Lib : Args.getAllArgValues(options::OPT_l)) + if (StringRef(Lib).starts_with("msvcr") || + StringRef(Lib).starts_with("ucrt") || + StringRef(Lib).starts_with("crtdll")) { + Lib = (llvm::Twine("-l") + Lib).str(); + // Respect the user's chosen crt variant, but still provide it + // again as the last linker argument, because some of the libraries + // we added above may depend on it. + CmdArgs.push_back(Args.MakeArgStringRef(Lib)); + return; + } + CmdArgs.push_back("-lmsvcrt"); } - CmdArgs.push_back("-lmsvcrt"); } void tools::MinGW::Linker::ConstructJob(Compilation &C, const JobAction &JA, diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index b9f75796ecc16..26d05bc419ccb 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -4705,6 +4705,44 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args, } } + // Process MinGW -mcrtdll option + if (Arg *A = Args.getLastArg(OPT_mcrtdll_EQ)) { + Opts.MinGWCRTDll = + llvm::StringSwitch<enum LangOptions::WindowsCRTDLLVersion>( + A->getValue()) + .StartsWithLower("crtdll", + LangOptions::WindowsCRTDLLVersion::CRTDLL) + .StartsWithLower("msvcrt10", + LangOptions::WindowsCRTDLLVersion::MSVCRT10) + .StartsWithLower("msvcrt20", + LangOptions::WindowsCRTDLLVersion::MSVCRT20) + .StartsWithLower("msvcrt40", + LangOptions::WindowsCRTDLLVersion::MSVCRT40) + .StartsWithLower("msvcr40", + LangOptions::WindowsCRTDLLVersion::MSVCRT40) + .StartsWithLower("msvcrtd", + LangOptions::WindowsCRTDLLVersion::MSVCRTD) + .StartsWithLower("msvcr70", + LangOptions::WindowsCRTDLLVersion::MSVCR70) + .StartsWithLower("msvcr71", + LangOptions::WindowsCRTDLLVersion::MSVCR71) + .StartsWithLower("msvcr80", + LangOptions::WindowsCRTDLLVersion::MSVCR80) + .StartsWithLower("msvcr90", + LangOptions::WindowsCRTDLLVersion::MSVCR90) + .StartsWithLower("msvcr100", + LangOptions::WindowsCRTDLLVersion::MSVCR100) + .StartsWithLower("msvcr110", + LangOptions::WindowsCRTDLLVersion::MSVCR110) + .StartsWithLower("msvcr120", + LangOptions::WindowsCRTDLLVersion::MSVCR120) + .StartsWithLower("ucrt", LangOptions::WindowsCRTDLLVersion::UCRT) + .Default(LangOptions::WindowsCRTDLLVersion::CRTDLL_Default); + if (Opts.MinGWCRTDll == LangOptions::WindowsCRTDLLVersion::CRTDLL_Default) { + Diags.Report(diag::err_unknown_crtdll) << A->getValue(); + } + } + return Diags.getNumErrors() == NumErrorsBefore; } diff --git a/clang/test/Driver/mingw-mcrtdll.c b/clang/test/Driver/mingw-mcrtdll.c new file mode 100644 index 0000000000000..4558628766169 --- /dev/null +++ b/clang/test/Driver/mingw-mcrtdll.c @@ -0,0 +1,30 @@ +// RUN: %clang -v --target=x86_64-w64-mingw32 -### %s 2>&1 | FileCheck -check-prefix=DEFAULT %s +// RUN: %clang -v --target=x86_64-w64-mingw32 -mcrtdll=msvcr90 -### %s 2>&1 | FileCheck -check-prefix=MSVCR90 %s +// RUN: %clang -v --target=x86_64-w64-mingw32 -mcrtdll=msvcr90_suffix -### %s 2>&1 | FileCheck -check-prefix=MSVCR90_SUFFIX %s +// RUN: %clang -v --target=x86_64-w64-mingw32 -mcrtdll=ucrt -### %s 2>&1 | FileCheck -check-prefix=UCRT %s +// RUN: %clang -v --target=x86_64-w64-mingw32 -mcrtdll=ucrtbase -### %s 2>&1 | FileCheck -check-prefix=UCRTBASE %s + +// RUN: %clang -dM -E --target=x86_64-w64-mingw32 %s 2>&1 | FileCheck -check-prefix=DEFINE_DEFAULT %s +// RUN: %clang -dM -E --target=x86_64-w64-mingw32 -mcrtdll=msvcr90 %s 2>&1 | FileCheck -check-prefix=DEFINE_MSVCR90 %s +// RUN: %clang -dM -E --target=x86_64-w64-mingw32 -mcrtdll=msvcr90_suffix %s 2>&1 | FileCheck -check-prefix=DEFINE_MSVCR90 %s +// RUN: %clang -dM -E --target=x86_64-w64-mingw32 -mcrtdll=ucrt %s 2>&1 | FileCheck -check-prefix=DEFINE_UCRT %s +// RUN: %clang -dM -E --target=x86_64-w64-mingw32 -mcrtdll=ucrtbase %s 2>&1 | FileCheck -check-prefix=DEFINE_UCRT %s +// RUN: not %clang -dM -E --target=x86_64-w64-mingw32 -mcrtdll=bad %s 2>&1 | FileCheck -check-prefix=BAD %s + +// DEFAULT: "-lmingwex" "-lmsvcrt" +// DEFINE_DEFAULT: #define __MSVCRT__ +// MSVCR90: "-lmingwex" "-lmsvcr90" +// DEFINE_MSVCR90: #define __MSVCRT_VERSION__ 0x900 +// DEFINE_MSVCR90: #define __MSVCRT__ +// MSVCR90-NOT: "-lmsvcrt" +// MSVCR90_SUFFIX: "-lmingwex" "-lmsvcr90_suffix" +// MSVCR90_SUFFIX-NOT: "-lmsvcrt" +// UCRT: "-lmingwex" "-lucrt" +// DEFINE_UCRT: #define _UCRT +// DEFINE_UCRT-NOT: #define __MSVCRT_VERSION__ +// UCRT-NOT: "-lmsvcrt" +// UCRTBASE: "-lmingwex" "-lucrtbase" +// UCRTBASE-NOT: "-lmsvcrt" +// DEFINE_CRTDLL: #define __CRTDLL__ +// DEFINE_CRTDLL-NOT: #define __MSVCRT__ +// BAD: error: unknown Windows/MinGW C runtime library 'bad' _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits