Skip to content

[clang][MinGW] Implement -mcrtdll option to switch crt choice #149469

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

Keno
Copy link
Member

@Keno Keno commented Jul 18, 2025

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).

Keno added 2 commits July 18, 2025 07:43
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.
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 llvm#149434
@Keno Keno requested a review from mstorsjo July 18, 2025 07:56
@Keno Keno added the julialang label Jul 18, 2025
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Jul 18, 2025
@llvmbot
Copy link
Member

llvmbot commented Jul 18, 2025

@llvm/pr-subscribers-clang-driver

Author: Keno Fischer (Keno)

Changes

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).


Full diff: https://github.com/llvm/llvm-project/pull/149469.diff

10 Files Affected:

  • (modified) clang/include/clang/Basic/DiagnosticFrontendKinds.td (+3)
  • (modified) clang/include/clang/Basic/LangOptions.def (+3)
  • (modified) clang/include/clang/Basic/LangOptions.h (+17)
  • (modified) clang/include/clang/Driver/Options.td (+6)
  • (modified) clang/lib/Basic/Targets/OSTargets.cpp (+47-1)
  • (modified) clang/lib/Driver/ToolChains/Clang.cpp (+1)
  • (modified) clang/lib/Driver/ToolChains/MinGW.cpp (+18-6)
  • (modified) clang/lib/Frontend/CompilerInvocation.cpp (+38)
  • (added) clang/test/Driver/mingw-mcrtdll.c (+30)
  • (modified) clang/test/Driver/mingw-msvcrt.c (+4-4)
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 7d093d20b3dd9..7223cda83fa70 100644
--- a/clang/lib/Driver/ToolChains/MinGW.cpp
+++ b/clang/lib/Driver/ToolChains/MinGW.cpp
@@ -85,12 +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"))
-      return;
-  CmdArgs.push_back("-lmsvcrt");
+
+  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");
+  }
 }
 
 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'
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"

@llvmbot
Copy link
Member

llvmbot commented Jul 18, 2025

@llvm/pr-subscribers-clang

Author: Keno Fischer (Keno)

Changes

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).


Full diff: https://github.com/llvm/llvm-project/pull/149469.diff

10 Files Affected:

  • (modified) clang/include/clang/Basic/DiagnosticFrontendKinds.td (+3)
  • (modified) clang/include/clang/Basic/LangOptions.def (+3)
  • (modified) clang/include/clang/Basic/LangOptions.h (+17)
  • (modified) clang/include/clang/Driver/Options.td (+6)
  • (modified) clang/lib/Basic/Targets/OSTargets.cpp (+47-1)
  • (modified) clang/lib/Driver/ToolChains/Clang.cpp (+1)
  • (modified) clang/lib/Driver/ToolChains/MinGW.cpp (+18-6)
  • (modified) clang/lib/Frontend/CompilerInvocation.cpp (+38)
  • (added) clang/test/Driver/mingw-mcrtdll.c (+30)
  • (modified) clang/test/Driver/mingw-msvcrt.c (+4-4)
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 7d093d20b3dd9..7223cda83fa70 100644
--- a/clang/lib/Driver/ToolChains/MinGW.cpp
+++ b/clang/lib/Driver/ToolChains/MinGW.cpp
@@ -85,12 +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"))
-      return;
-  CmdArgs.push_back("-lmsvcrt");
+
+  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");
+  }
 }
 
 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'
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"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category julialang
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants