[clang] [llvm] [AArch64] Add aarch64-unknown-uefi target support (PR #213275)

via cfe-commits cfe-commits at lists.llvm.org
Fri Jul 31 08:25:08 PDT 2026


https://github.com/paulober updated https://github.com/llvm/llvm-project/pull/213275

>From a5d76397a8d4e30cde988a04b4caa9dff48e9703 Mon Sep 17 00:00:00 2001
From: paulober <44974737+paulober at users.noreply.github.com>
Date: Fri, 31 Jul 2026 14:39:08 +0200
Subject: [PATCH] [AArch64] Add aarch64-unknown-uefi target support

Add a clang AArch64 UEFI target (PE/COFF, Microsoft ARM64 ABI, LLP64) with a COFF data layout, route the triple to it in the target and driver, mirroring the existing x86_64 UEFI target.

In the AArch64 backend, allow COFF on UEFI in addition to Windows: add Subtarget::isTargetUEFI(), relax the MCInstLower Windows-only COFF assertion, and treat UEFI like Windows for frame-record layout and Windows CFI. This answers the existing TODO in AArch64FrameLowering's isTargetWindows() and keeps getCalleeSavedRegs() aligned so the (FP,LR) pairing stays consistent.
---
 clang/lib/Basic/Targets.cpp                   |  2 +
 clang/lib/Basic/Targets/AArch64.cpp           | 37 +++++++++++++++++++
 clang/lib/Basic/Targets/AArch64.h             | 11 ++++++
 clang/lib/Driver/Driver.cpp                   |  6 ++-
 .../Target/AArch64/AArch64FrameLowering.cpp   | 11 +++---
 .../lib/Target/AArch64/AArch64MCInstLower.cpp |  4 +-
 .../Target/AArch64/AArch64RegisterInfo.cpp    |  6 ++-
 llvm/lib/Target/AArch64/AArch64Subtarget.h    |  1 +
 8 files changed, 68 insertions(+), 10 deletions(-)

diff --git a/clang/lib/Basic/Targets.cpp b/clang/lib/Basic/Targets.cpp
index 8c01cfca8ccb4..823a2f08943ff 100644
--- a/clang/lib/Basic/Targets.cpp
+++ b/clang/lib/Basic/Targets.cpp
@@ -190,6 +190,8 @@ std::unique_ptr<TargetInfo> AllocateTarget(const llvm::Triple &Triple,
       default: // Assume MSVC for unknown environments
         return std::make_unique<MicrosoftARM64TargetInfo>(Triple, Opts);
       }
+    case llvm::Triple::UEFI:
+      return std::make_unique<UEFIAArch64TargetInfo>(Triple, Opts);
     default:
       return std::make_unique<AArch64leTargetInfo>(Triple, Opts);
     }
diff --git a/clang/lib/Basic/Targets/AArch64.cpp b/clang/lib/Basic/Targets/AArch64.cpp
index d531e26ade84a..126791d725cd2 100644
--- a/clang/lib/Basic/Targets/AArch64.cpp
+++ b/clang/lib/Basic/Targets/AArch64.cpp
@@ -1859,6 +1859,43 @@ MinGWARM64TargetInfo::MinGWARM64TargetInfo(const llvm::Triple &Triple,
   TheCXXABI.set(TargetCXXABI::GenericAArch64);
 }
 
+UEFIAArch64TargetInfo::UEFIAArch64TargetInfo(const llvm::Triple &Triple,
+                                             const TargetOptions &Opts)
+    : UEFITargetInfo<AArch64leTargetInfo>(Triple, Opts) {
+  // UEFI images are PE/COFF and follow the Microsoft ARM64 ABI. The UEFI spec
+  // does not mandate a specific C++ ABI or integer model, so we match the
+  // Windows ARM64 target -- the only supported way to produce AArch64 EFI
+  // binaries with Clang/LLVM today.
+  TheCXXABI.set(TargetCXXABI::Microsoft);
+
+  // LLP64 data model: int:4, long:4, long long:8, long double:8.
+  IntWidth = IntAlign = 32;
+  LongWidth = LongAlign = 32;
+  DoubleAlign = LongLongAlign = 64;
+  LongDoubleWidth = LongDoubleAlign = 64;
+  LongDoubleFormat = &llvm::APFloat::IEEEdouble();
+  IntMaxType = SignedLongLong;
+  Int64Type = SignedLongLong;
+  SizeType = UnsignedLongLong;
+  PtrDiffType = SignedLongLong;
+  IntPtrType = SignedLongLong;
+}
+
+void UEFIAArch64TargetInfo::setDataLayout() {
+  // PE/COFF image: use the same data layout the LLVM AArch64 TargetMachine
+  // computes for COFF. This must be an override (not just a ctor call) because
+  // handleTargetFeatures() re-invokes setDataLayout() after construction, and
+  // the AArch64le base only knows MachO/ELF -- it would otherwise revert to an
+  // ELF layout that mismatches the backend.
+  resetDataLayout("e-m:w-p270:32:32-p271:32:32-p272:64:64-p:64:64-i32:32-"
+                  "i64:64-i128:128-n32:64-S128-Fn32");
+}
+
+AArch64TargetInfo::BuiltinVaListKind
+UEFIAArch64TargetInfo::getBuiltinVaListKind() const {
+  return TargetInfo::CharPtrBuiltinVaList;
+}
+
 AppleMachOAArch64TargetInfo::AppleMachOAArch64TargetInfo(
     const llvm::Triple &Triple, const TargetOptions &Opts)
     : AppleMachOTargetInfo<AArch64leTargetInfo>(Triple, Opts) {}
diff --git a/clang/lib/Basic/Targets/AArch64.h b/clang/lib/Basic/Targets/AArch64.h
index 3ccfa265399be..046713668bb1e 100644
--- a/clang/lib/Basic/Targets/AArch64.h
+++ b/clang/lib/Basic/Targets/AArch64.h
@@ -316,6 +316,17 @@ class LLVM_LIBRARY_VISIBILITY MinGWARM64TargetInfo
   MinGWARM64TargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts);
 };
 
+// ARM64 UEFI target (PE/COFF image, Microsoft ARM64 ABI)
+class LLVM_LIBRARY_VISIBILITY UEFIAArch64TargetInfo
+    : public UEFITargetInfo<AArch64leTargetInfo> {
+public:
+  UEFIAArch64TargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts);
+
+  void setDataLayout() override;
+
+  BuiltinVaListKind getBuiltinVaListKind() const override;
+};
+
 class LLVM_LIBRARY_VISIBILITY AArch64beTargetInfo : public AArch64TargetInfo {
 public:
   AArch64beTargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts);
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 38795f7c2ae7a..5bb1716902f1c 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -714,8 +714,10 @@ static llvm::Triple computeTargetTriple(const Driver &D, StringRef TargetTriple,
   }
 #endif
 
-  // Currently the only architecture supported by *-uefi triples are x86_64.
-  if (Target.isUEFI() && Target.getArch() != llvm::Triple::x86_64)
+  // The architectures currently supported by *-uefi triples are x86_64 and
+  // aarch64 (both emit PE/COFF images under the Microsoft ABI).
+  if (Target.isUEFI() && Target.getArch() != llvm::Triple::x86_64 &&
+      Target.getArch() != llvm::Triple::aarch64)
     D.Diag(diag::err_target_unknown_triple) << Target.str();
 
   // The `-maix[32|64]` flags are only valid for AIX targets.
diff --git a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
index 6ef64b06932ab..3eeea74c98453 100644
--- a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
@@ -384,12 +384,13 @@ static bool isLikelyToHaveSVEStack(const AArch64FrameLowering &AFL,
 }
 
 static bool isTargetWindows(const MachineFunction &MF) {
-  // TODO: Should this include targets like UEFI (which use Windows CFI)?
-  // Note: Currently, there is not AArch64 support for UEFI. The value returned
-  // here must align with the predicate used for returning the list of callee
-  // saved regs in AArch64RegisterInfo::getCalleeSavedRegs(), so that we use
+  // UEFI images are PE/COFF and use Windows CFI, so for frame-record layout and
+  // unwind they behave like Windows. This value must align with the predicate
+  // used for returning the list of callee saved regs in
+  // AArch64RegisterInfo::getCalleeSavedRegs(), so that we use
   // invalidateWindowsRegisterPairing() where appropriate.
-  return MF.getSubtarget<AArch64Subtarget>().isTargetWindows();
+  const AArch64Subtarget &STI = MF.getSubtarget<AArch64Subtarget>();
+  return STI.isTargetWindows() || STI.isTargetUEFI();
 }
 
 bool AArch64FrameLowering::hasSVECalleeSavesAboveFrameRecord(
diff --git a/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp b/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp
index d3a38624488e8..ce0e0ef39ad0e 100644
--- a/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp
+++ b/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp
@@ -49,8 +49,8 @@ MCSymbol *AArch64MCInstLower::GetGlobalValueSymbol(const GlobalValue *GV,
   if (!TheTriple.isOSBinFormatCOFF())
     return Printer.getSymbolPreferLocal(*GV);
 
-  assert(TheTriple.isOSWindows() &&
-         "Windows is the only supported COFF target");
+  assert((TheTriple.isOSWindows() || TheTriple.isUEFI()) &&
+         "COFF is only supported on Windows and UEFI targets");
 
   bool IsIndirect =
       (TargetFlags & (AArch64II::MO_DLLIMPORT | AArch64II::MO_COFFSTUB));
diff --git a/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp b/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp
index 502c91fe3e531..920c1bf29963c 100644
--- a/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp
@@ -76,7 +76,11 @@ AArch64RegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
   const auto &F = MF->getFunction();
   const auto *TLI = MF->getSubtarget<AArch64Subtarget>().getTargetLowering();
   const bool Darwin = MF->getSubtarget<AArch64Subtarget>().isTargetDarwin();
-  const bool Windows = MF->getSubtarget<AArch64Subtarget>().isTargetWindows();
+  // UEFI uses the Windows PE/COFF ABI, so it takes the same callee-saved
+  // register lists (this must align with isTargetWindows() in
+  // AArch64FrameLowering, which drives invalidateWindowsRegisterPairing()).
+  const bool Windows = MF->getSubtarget<AArch64Subtarget>().isTargetWindows() ||
+                       MF->getSubtarget<AArch64Subtarget>().isTargetUEFI();
 
   if (TLI->supportSwiftError() &&
       F.getAttributes().hasAttrSomewhere(Attribute::SwiftError)) {
diff --git a/llvm/lib/Target/AArch64/AArch64Subtarget.h b/llvm/lib/Target/AArch64/AArch64Subtarget.h
index 380c3e11fcbf2..68a9947e8e88d 100644
--- a/llvm/lib/Target/AArch64/AArch64Subtarget.h
+++ b/llvm/lib/Target/AArch64/AArch64Subtarget.h
@@ -312,6 +312,7 @@ class AArch64Subtarget final : public AArch64GenSubtargetInfo {
   bool isTargetIOS() const { return TargetTriple.isiOS(); }
   bool isTargetLinux() const { return TargetTriple.isOSLinux(); }
   bool isTargetWindows() const { return TargetTriple.isOSWindows(); }
+  bool isTargetUEFI() const { return TargetTriple.isUEFI(); }
   bool isTargetAndroid() const { return TargetTriple.isAndroid(); }
   bool isTargetFuchsia() const { return TargetTriple.isOSFuchsia(); }
   bool isWindowsArm64EC() const { return TargetTriple.isWindowsArm64EC(); }



More information about the cfe-commits mailing list