- 
                Notifications
    You must be signed in to change notification settings 
- Fork 15k
[clang][Driver] Fix triple config loading for clang-cl #111397
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
base: main
Are you sure you want to change the base?
Conversation
| Thank you for submitting a Pull Request (PR) to the LLVM Project! This PR will be automatically labeled and the relevant teams will be notified. If you wish to, you can add reviewers by using the "Reviewers" section on this page. If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using  If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers. If you have further questions, they may be answered by the LLVM GitHub User Guide. You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums. | 
| @llvm/pr-subscribers-clang-driver Author: Bo Anderson (Bo98) Changes
 Move the default triple calculation earlier to handle this. Full diff: https://github.com/llvm/llvm-project/pull/111397.diff 4 Files Affected: 
 diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index a5d43bdac23735..541e79ec97056b 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -1247,6 +1247,19 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
   CLOptions = std::make_unique<InputArgList>(
       ParseArgStrings(ArgList.slice(1), /*UseDriverMode=*/true, ContainsError));
 
+  // We want to determine the triple early so that we load the correct config.
+  if (IsCLMode()) {
+    // clang-cl targets MSVC-style Win32.
+    llvm::Triple T(TargetTriple);
+    T.setOS(llvm::Triple::Win32);
+    T.setVendor(llvm::Triple::PC);
+    T.setEnvironment(llvm::Triple::MSVC);
+    T.setObjectFormat(llvm::Triple::COFF);
+    if (CLOptions->hasArg(options::OPT__SLASH_arm64EC))
+      T.setArch(llvm::Triple::aarch64, llvm::Triple::AArch64SubArch_arm64ec);
+    TargetTriple = T.str();
+  }
+
   // Try parsing configuration file.
   if (!ContainsError)
     ContainsError = loadConfigFiles();
@@ -1286,6 +1299,16 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
           appendOneArg(Args, Opt, nullptr);
         }
     }
+
+    // The config file may have changed the architecture so apply it.
+    if (HasConfigFile && Args.hasArg(options::OPT__SLASH_arm64EC)) {
+      llvm::Triple T(TargetTriple);
+      if (T.getArch() != llvm::Triple::aarch64 ||
+          T.getSubArch() != llvm::Triple::AArch64SubArch_arm64ec) {
+        T.setArch(llvm::Triple::aarch64, llvm::Triple::AArch64SubArch_arm64ec);
+        TargetTriple = T.str();
+      }
+    }
   }
 
   // Check for working directory option before accessing any files
@@ -1336,17 +1359,7 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
 
   // FIXME: TargetTriple is used by the target-prefixed calls to as/ld
   // and getToolChain is const.
-  if (IsCLMode()) {
-    // clang-cl targets MSVC-style Win32.
-    llvm::Triple T(TargetTriple);
-    T.setOS(llvm::Triple::Win32);
-    T.setVendor(llvm::Triple::PC);
-    T.setEnvironment(llvm::Triple::MSVC);
-    T.setObjectFormat(llvm::Triple::COFF);
-    if (Args.hasArg(options::OPT__SLASH_arm64EC))
-      T.setArch(llvm::Triple::aarch64, llvm::Triple::AArch64SubArch_arm64ec);
-    TargetTriple = T.str();
-  } else if (IsDXCMode()) {
+  if (IsDXCMode()) {
     // Build TargetTriple from target_profile option for clang-dxc.
     if (const Arg *A = Args.getLastArg(options::OPT_target_profile)) {
       StringRef TargetProfile = A->getValue();
diff --git a/clang/test/Driver/Inputs/config-cl/arm64ec-pc-windows-msvc.cfg b/clang/test/Driver/Inputs/config-cl/arm64ec-pc-windows-msvc.cfg
new file mode 100644
index 00000000000000..e69de29bb2d1d6
diff --git a/clang/test/Driver/Inputs/config-cl/config-arm64ec-arg.cfg b/clang/test/Driver/Inputs/config-cl/config-arm64ec-arg.cfg
new file mode 100644
index 00000000000000..2eb4402ddf6cd7
--- /dev/null
+++ b/clang/test/Driver/Inputs/config-cl/config-arm64ec-arg.cfg
@@ -0,0 +1 @@
+/arm64EC
diff --git a/clang/test/Driver/config-file.c b/clang/test/Driver/config-file.c
index 9df830ca4c7538..cbad0e408b9ab4 100644
--- a/clang/test/Driver/config-file.c
+++ b/clang/test/Driver/config-file.c
@@ -82,3 +82,13 @@
 // CHECK-TWO-CONFIGS: -isysroot
 // CHECK-TWO-CONFIGS-SAME: /opt/data
 // CHECK-TWO-CONFIGS-SAME: -Wall
+
+
+//--- clang-cl loads the correct triple
+// RUN: env -u CLANG_NO_DEFAULT_CONFIG %clang_cl /arm64EC --config-system-dir=%S/Inputs/config-cl --config-user-dir= -v 2>&1 | FileCheck %s -check-prefix CHECK-CL --implicit-check-not 'Configuration file:'
+// CHECK-CL: Configuration file: {{.*}}Inputs{{.}}config-cl{{.}}arm64ec-pc-windows-msvc.cfg
+
+//--- clang-cl configs support setting /arm64EC
+// RUN: %clang_cl --config-system-dir=%S/Inputs/config-cl --config-user-dir= --config=config-arm64ec-arg.cfg -v 2>&1 | FileCheck %s -check-prefix CHECK-CL-FLAG --implicit-check-not 'Configuration file:'
+// CHECK-CL-FLAG: Target: arm64ec-pc-windows-msvc
+// CHECK-CL-FLAG: Configuration file: {{.*}}Inputs{{.}}config-cl{{.}}config-arm64ec-arg.cfg
 | 
| @llvm/pr-subscribers-clang Author: Bo Anderson (Bo98) Changes
 Move the default triple calculation earlier to handle this. Full diff: https://github.com/llvm/llvm-project/pull/111397.diff 4 Files Affected: 
 diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index a5d43bdac23735..541e79ec97056b 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -1247,6 +1247,19 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
   CLOptions = std::make_unique<InputArgList>(
       ParseArgStrings(ArgList.slice(1), /*UseDriverMode=*/true, ContainsError));
 
+  // We want to determine the triple early so that we load the correct config.
+  if (IsCLMode()) {
+    // clang-cl targets MSVC-style Win32.
+    llvm::Triple T(TargetTriple);
+    T.setOS(llvm::Triple::Win32);
+    T.setVendor(llvm::Triple::PC);
+    T.setEnvironment(llvm::Triple::MSVC);
+    T.setObjectFormat(llvm::Triple::COFF);
+    if (CLOptions->hasArg(options::OPT__SLASH_arm64EC))
+      T.setArch(llvm::Triple::aarch64, llvm::Triple::AArch64SubArch_arm64ec);
+    TargetTriple = T.str();
+  }
+
   // Try parsing configuration file.
   if (!ContainsError)
     ContainsError = loadConfigFiles();
@@ -1286,6 +1299,16 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
           appendOneArg(Args, Opt, nullptr);
         }
     }
+
+    // The config file may have changed the architecture so apply it.
+    if (HasConfigFile && Args.hasArg(options::OPT__SLASH_arm64EC)) {
+      llvm::Triple T(TargetTriple);
+      if (T.getArch() != llvm::Triple::aarch64 ||
+          T.getSubArch() != llvm::Triple::AArch64SubArch_arm64ec) {
+        T.setArch(llvm::Triple::aarch64, llvm::Triple::AArch64SubArch_arm64ec);
+        TargetTriple = T.str();
+      }
+    }
   }
 
   // Check for working directory option before accessing any files
@@ -1336,17 +1359,7 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
 
   // FIXME: TargetTriple is used by the target-prefixed calls to as/ld
   // and getToolChain is const.
-  if (IsCLMode()) {
-    // clang-cl targets MSVC-style Win32.
-    llvm::Triple T(TargetTriple);
-    T.setOS(llvm::Triple::Win32);
-    T.setVendor(llvm::Triple::PC);
-    T.setEnvironment(llvm::Triple::MSVC);
-    T.setObjectFormat(llvm::Triple::COFF);
-    if (Args.hasArg(options::OPT__SLASH_arm64EC))
-      T.setArch(llvm::Triple::aarch64, llvm::Triple::AArch64SubArch_arm64ec);
-    TargetTriple = T.str();
-  } else if (IsDXCMode()) {
+  if (IsDXCMode()) {
     // Build TargetTriple from target_profile option for clang-dxc.
     if (const Arg *A = Args.getLastArg(options::OPT_target_profile)) {
       StringRef TargetProfile = A->getValue();
diff --git a/clang/test/Driver/Inputs/config-cl/arm64ec-pc-windows-msvc.cfg b/clang/test/Driver/Inputs/config-cl/arm64ec-pc-windows-msvc.cfg
new file mode 100644
index 00000000000000..e69de29bb2d1d6
diff --git a/clang/test/Driver/Inputs/config-cl/config-arm64ec-arg.cfg b/clang/test/Driver/Inputs/config-cl/config-arm64ec-arg.cfg
new file mode 100644
index 00000000000000..2eb4402ddf6cd7
--- /dev/null
+++ b/clang/test/Driver/Inputs/config-cl/config-arm64ec-arg.cfg
@@ -0,0 +1 @@
+/arm64EC
diff --git a/clang/test/Driver/config-file.c b/clang/test/Driver/config-file.c
index 9df830ca4c7538..cbad0e408b9ab4 100644
--- a/clang/test/Driver/config-file.c
+++ b/clang/test/Driver/config-file.c
@@ -82,3 +82,13 @@
 // CHECK-TWO-CONFIGS: -isysroot
 // CHECK-TWO-CONFIGS-SAME: /opt/data
 // CHECK-TWO-CONFIGS-SAME: -Wall
+
+
+//--- clang-cl loads the correct triple
+// RUN: env -u CLANG_NO_DEFAULT_CONFIG %clang_cl /arm64EC --config-system-dir=%S/Inputs/config-cl --config-user-dir= -v 2>&1 | FileCheck %s -check-prefix CHECK-CL --implicit-check-not 'Configuration file:'
+// CHECK-CL: Configuration file: {{.*}}Inputs{{.}}config-cl{{.}}arm64ec-pc-windows-msvc.cfg
+
+//--- clang-cl configs support setting /arm64EC
+// RUN: %clang_cl --config-system-dir=%S/Inputs/config-cl --config-user-dir= --config=config-arm64ec-arg.cfg -v 2>&1 | FileCheck %s -check-prefix CHECK-CL-FLAG --implicit-check-not 'Configuration file:'
+// CHECK-CL-FLAG: Target: arm64ec-pc-windows-msvc
+// CHECK-CL-FLAG: Configuration file: {{.*}}Inputs{{.}}config-cl{{.}}config-arm64ec-arg.cfg
 | 
| Ping? | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs conflict resolution, but otherwise LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't even realize people are using config files with clang-cl (since IIRC it doesn't support the config file command-line options).
| // The config file may have changed the architecture so apply it. | ||
| if (HasConfigFile && Args.hasArg(options::OPT__SLASH_arm64EC)) { | ||
| llvm::Triple T(TargetTriple); | ||
| if (T.getArch() != llvm::Triple::aarch64 || | ||
| T.getSubArch() != llvm::Triple::AArch64SubArch_arm64ec) { | ||
| T.setArch(llvm::Triple::aarch64, llvm::Triple::AArch64SubArch_arm64ec); | ||
| TargetTriple = T.str(); | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PR description only mentions moving the default triple calculation earlier, but not this part.
The config file can presumably change many things, so why does the target triple need special handling? Also what about flags that affect x86 vs. x86_64 (-m32 and -m64), do we need to handle those too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's two triple systems in the driver: TargetTriple and the triple fed to ToolChain (computed by computeTargetTriple). The former is feeds into the latter but is otherwise separate.
-m32 and -m64 are already handled in computeTargetTriple and so never affect TargetTriple. The code here was to keep the existing behaviour of /arm64EC affecting TargetTriple.
To be honest: I have no idea why there is two triples. It would be simpler if TargetTriple became DefaultTriple (and remains immutable), Driver.getTargetTriple() was removed and we just moved all this to computeTargetTriple but I was hesitant to change something I don't understand the history of.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for explaining. I haven't looked at this code in a long time, and TargetTriple vs. computeTargetTriple() is pretty confusing.
It seems /arm64EC is not handled in computeTargetTriple()(?) so I suppose it needs to be handled somewhere :)
| T.setArch(llvm::Triple::aarch64, llvm::Triple::AArch64SubArch_arm64ec); | ||
| TargetTriple = T.str(); | ||
| } else if (IsDXCMode()) { | ||
| if (IsDXCMode()) { | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't this mode have the same issue?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does but it relies on argument parsing so is trickier to move and I didn't have a DXC build on hand. I can have a look again though and see if I can get a build going.
I guess this is the same problem as above. It would be trivial to move this to computeTargetTriple but it seems this changes TargetTriple instead.
| 
 You don't need to use config files with  | 
| 
 Okay, but that sounds like a case where clang-cl users do not want to use config files. Maybe the fix is to always ignore config files in clang-cl mode, since there is no corresponding concept in MSVC cl, and it's not commonly used for the embedded/cross compiling scenarios where config files are used? | 
| // The config file may have changed the architecture so apply it. | ||
| if (HasConfigFile && Args.hasArg(options::OPT__SLASH_arm64EC)) { | ||
| llvm::Triple T(TargetTriple); | ||
| if (T.getArch() != llvm::Triple::aarch64 || | ||
| T.getSubArch() != llvm::Triple::AArch64SubArch_arm64ec) { | ||
| T.setArch(llvm::Triple::aarch64, llvm::Triple::AArch64SubArch_arm64ec); | ||
| TargetTriple = T.str(); | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for explaining. I haven't looked at this code in a long time, and TargetTriple vs. computeTargetTriple() is pretty confusing.
It seems /arm64EC is not handled in computeTargetTriple()(?) so I suppose it needs to be handled somewhere :)
| if (CLOptions->hasArg(options::OPT__SLASH_arm64EC)) | ||
| T.setArch(llvm::Triple::aarch64, llvm::Triple::AArch64SubArch_arm64ec); | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought normally the arguments in the config file should get processed before the command-line arguments? What are the rules for this really?
| 
 I'd be fine with doing this too -- I just wasn't aware that clang-cl did not support config files (since this fact doesn't seem to be documented anywhere, unless I missed it). | 
| Looks like I was wrong. clang-cl does support  Your change to update the triple based on clang-cl mode earlier makes sense to me. I'm less sure about manually inspecting the  I'm even less sure about the "The config file may have changed the architecture so apply it" part. It's not clear to me that we need it at all, and if we do need it, why is it specific to the  | 
| LLVM 20 is branching soon -- would be good to try to get this in before then. | 
| Okay. I think most of my previous comments (except for "maybe the fix is to always ignore config files in clang-cl mode) still apply though. | 
| Yep, was just trying to ping @Bo98 for an update here. | 
| I've resolved the conflicts in commit http://github.com/PaulWagener/llvm-project/commit/40a8c7c0ff3f688b690e4c74db734de67f0f89e9 (Bo98#1) I don't know much about the technical details of this PR but I can confirm that this correctly builds and fixes the issue with cross compiling with homebrew clang-cl that I've been having. Would be happy to see this upstreamed. | 
| Hello guys, Can you check/confirm if this fix is in any way related to #159229 that I observe? | 
clang-clsets the default triple to<arch>-pc-windows-msvc. The host triple was however being used instead when loading default configs. As a result,<triple>.cfgfiles could breakclang-clby applying flags that aren't compatible with Windows/MSVC.Move the default triple calculation earlier to handle this and correctly load
<arch>-pc-windows-msvc.cfginstead.