[clang] [llvm] [clang][Driver] Pass -machine argument to the linker explicitly for ARM64EC targets. (PR #86835)

Jacek Caban via cfe-commits cfe-commits at lists.llvm.org
Wed Mar 27 10:14:50 PDT 2024


https://github.com/cjacek created https://github.com/llvm/llvm-project/pull/86835

Currently, Clang doesn't explicitly pass the `-machine` argument to the linker, relying on the linker to infer it from the input. However, MSVC's link.exe requires `-machine` to be specified explicitly for ARM64EC/ARM64X targets. Although lld-link allows inferring the target from ARM64EC object files, this detection isn't entirely reliable as AMD64 object files are also valid input for ARM64EC target.

Handling of `-machine:arm64ec` is straightforward. For ARM64X, this PR extends the ARM64EC target triple to accept the "arm64x" subarch string, which serves as an alias to "arm64ec" in most cases. However, during linker invocation, "arm64x" distinguishes between `-machine:arm64ec` and `-machine:arm64x`.

The only analogue to MSVC's behavior is `cl.exe -arm64EC`, which is handled by `-machine:arm64ec` part of this PR. As far as I can tell, linking ARM64X images with MSVC requires a direct link.exe invocation. In cases like lib.exe (#85972) or some
aspects of link.exe, `-machine:arm64x` serves as an alias to `-machine:arm64ec` too.

(BTW, meantime, I worked around it with `-Wl,-machine:...`).
CC @bylaws 

>From 9f045fd8fa59ef11e2308c7d75b4f08c35c05c16 Mon Sep 17 00:00:00 2001
From: Jacek Caban <jacek at codeweavers.com>
Date: Mon, 19 Feb 2024 00:39:50 +0100
Subject: [PATCH] [clang][Driver] Pass -machine argument to the linker
 explicitly for ARM64EC targets.

Currently, Clang doesn't explicitly pass the -machine argument to the linker, relying
on the linker to infer it from the input. However, MSVC's link.exe requires -machine
to be specified explicitly for ARM64EC/ARM64X targets. Although lld-link allows
inferring the target from ARM64EC object files, this detection isn't entirely reliable
as AMD64 object files are also valid input for ARM64EC target.

Handling of -machine:arm64ec is straightforward. For ARM64X, this PR extends the
ARM64EC target triple to accept the "arm64x" subarch string, which serves as an alias
to "arm64ec" in most cases. However, during linker invocation, "arm64x" distinguishes
between -machine:arm64ec and -machine:arm64x.

The only analogue to MSVC's behavior is cl.exe -arm64EC, which is handled by
-machine:arm64ec part of this PR. As far as I can tell, linking ARM64X images with
MSVC requires a direct link.exe invocation. In cases like lib.exe (#85972) or some
aspects of link.exe, -machine:arm64x serves as an alias to -machine:arm64ec too.
---
 clang/lib/Driver/Driver.cpp                |  3 +--
 clang/lib/Driver/ToolChains/MSVC.cpp       |  7 ++++++
 clang/test/Driver/msvc-link.c              |  6 ++++++
 llvm/include/llvm/TargetParser/Triple.h    |  4 +++-
 llvm/lib/TargetParser/Triple.cpp           |  9 ++++----
 llvm/unittests/TargetParser/TripleTest.cpp | 25 ++++++++++++++++++++++
 6 files changed, 47 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 7a53764364ce4d..328ea93b679293 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -1458,8 +1458,7 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
   }
 
   // Report warning when arm64EC option is overridden by specified target
-  if ((TC.getTriple().getArch() != llvm::Triple::aarch64 ||
-       TC.getTriple().getSubArch() != llvm::Triple::AArch64SubArch_arm64ec) &&
+  if (!TC.getTriple().isWindowsArm64EC() &&
       UArgs->hasArg(options::OPT__SLASH_arm64EC)) {
     getDiags().Report(clang::diag::warn_target_override_arm64ec)
         << TC.getTriple().str();
diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp
index dc534a33e6d0ef..edf81ce7e425be 100644
--- a/clang/lib/Driver/ToolChains/MSVC.cpp
+++ b/clang/lib/Driver/ToolChains/MSVC.cpp
@@ -79,6 +79,13 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
     CmdArgs.push_back(
         Args.MakeArgString(std::string("-out:") + Output.getFilename()));
 
+  if (TC.getTriple().isWindowsArm64EC()) {
+    if (TC.getTriple().getSubArch() == llvm::Triple::AArch64SubArch_arm64x)
+      CmdArgs.push_back("-machine:arm64x");
+    else
+      CmdArgs.push_back("-machine:arm64ec");
+  }
+
   if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles) &&
       !C.getDriver().IsCLMode() && !C.getDriver().IsFlangMode()) {
     CmdArgs.push_back("-defaultlib:libcmt");
diff --git a/clang/test/Driver/msvc-link.c b/clang/test/Driver/msvc-link.c
index 64e099ea63042f..03ba3cd5805188 100644
--- a/clang/test/Driver/msvc-link.c
+++ b/clang/test/Driver/msvc-link.c
@@ -36,3 +36,9 @@
 // VFSOVERLAY: "--vfsoverlay"
 // VFSOVERLAY: lld-link
 // VFSOVERLAY: "/vfsoverlay:{{.*}}" "{{.*}}.obj"
+
+// RUN: %clang -target arm64ec-pc-windows-msvc -fuse-ld=link -### %s 2>&1 | FileCheck --check-prefix=ARM64EC %s
+// ARM64EC: "-machine:arm64ec"
+
+// RUN: %clang -target arm64x-pc-windows-msvc -fuse-ld=link -### %s 2>&1 | FileCheck --check-prefix=ARM64X %s
+// ARM64X: "-machine:arm64x"
diff --git a/llvm/include/llvm/TargetParser/Triple.h b/llvm/include/llvm/TargetParser/Triple.h
index f256e2b205a889..fb55011d68c6bf 100644
--- a/llvm/include/llvm/TargetParser/Triple.h
+++ b/llvm/include/llvm/TargetParser/Triple.h
@@ -148,6 +148,7 @@ class Triple {
 
     AArch64SubArch_arm64e,
     AArch64SubArch_arm64ec,
+    AArch64SubArch_arm64x,
 
     KalimbaSubArch_v3,
     KalimbaSubArch_v4,
@@ -623,7 +624,8 @@ class Triple {
   // Checks if we're using the Windows Arm64EC ABI.
   bool isWindowsArm64EC() const {
     return getArch() == Triple::aarch64 &&
-           getSubArch() == Triple::AArch64SubArch_arm64ec;
+           (getSubArch() == Triple::AArch64SubArch_arm64ec ||
+            getSubArch() == Triple::AArch64SubArch_arm64x);
   }
 
   bool isWindowsCoreCLREnvironment() const {
diff --git a/llvm/lib/TargetParser/Triple.cpp b/llvm/lib/TargetParser/Triple.cpp
index 624679ff507a7f..29c634eec7866a 100644
--- a/llvm/lib/TargetParser/Triple.cpp
+++ b/llvm/lib/TargetParser/Triple.cpp
@@ -110,7 +110,7 @@ StringRef Triple::getArchName(ArchType Kind, SubArchType SubArch) {
       return "mipsisa64r6el";
     break;
   case Triple::aarch64:
-    if (SubArch == AArch64SubArch_arm64ec)
+    if (SubArch == AArch64SubArch_arm64ec || SubArch == AArch64SubArch_arm64x)
       return "arm64ec";
     if (SubArch == AArch64SubArch_arm64e)
       return "arm64e";
@@ -515,10 +515,8 @@ static Triple::ArchType parseArch(StringRef ArchName) {
     .Case("aarch64_be", Triple::aarch64_be)
     .Case("aarch64_32", Triple::aarch64_32)
     .Case("arc", Triple::arc)
-    .Case("arm64", Triple::aarch64)
+    .Cases("arm64", "arm64e", "arm64ec", "arm64x", Triple::aarch64)
     .Case("arm64_32", Triple::aarch64_32)
-    .Case("arm64e", Triple::aarch64)
-    .Case("arm64ec", Triple::aarch64)
     .Case("arm", Triple::arm)
     .Case("armeb", Triple::armeb)
     .Case("thumb", Triple::thumb)
@@ -729,6 +727,9 @@ static Triple::SubArchType parseSubArch(StringRef SubArchName) {
   if (SubArchName == "arm64ec")
     return Triple::AArch64SubArch_arm64ec;
 
+  if (SubArchName == "arm64x")
+    return Triple::AArch64SubArch_arm64x;
+
   if (SubArchName.starts_with("spirv"))
     return StringSwitch<Triple::SubArchType>(SubArchName)
         .EndsWith("v1.0", Triple::SPIRVSubArch_v10)
diff --git a/llvm/unittests/TargetParser/TripleTest.cpp b/llvm/unittests/TargetParser/TripleTest.cpp
index e1e1bbd76ecda6..76a22c5995c42b 100644
--- a/llvm/unittests/TargetParser/TripleTest.cpp
+++ b/llvm/unittests/TargetParser/TripleTest.cpp
@@ -2226,6 +2226,16 @@ TEST(TripleTest, ParseARMArch) {
     T.setArch(Triple::aarch64, Triple::AArch64SubArch_arm64ec);
     EXPECT_EQ("arm64ec", T.getArchName());
   }
+  {
+    Triple T = Triple("arm64x");
+    EXPECT_EQ(Triple::aarch64, T.getArch());
+    EXPECT_EQ(Triple::AArch64SubArch_arm64x, T.getSubArch());
+  }
+  {
+    Triple T;
+    T.setArch(Triple::aarch64, Triple::AArch64SubArch_arm64x);
+    EXPECT_EQ("arm64ec", T.getArchName());
+  }
 }
 
 TEST(TripleTest, isArmT32) {
@@ -2371,4 +2381,19 @@ TEST(TripleTest, isArmMClass) {
     EXPECT_TRUE(T.isArmMClass());
   }
 }
+
+TEST(TripleTest, isWindowsArm64EC) {
+  {
+    Triple T = Triple("arm64ec");
+    EXPECT_TRUE(T.isWindowsArm64EC());
+  }
+  {
+    Triple T = Triple("arm64x");
+    EXPECT_TRUE(T.isWindowsArm64EC());
+  }
+  {
+    Triple T = Triple("aarch64");
+    EXPECT_FALSE(T.isWindowsArm64EC());
+  }
+}
 } // end anonymous namespace



More information about the cfe-commits mailing list