[llvm-branch-commits] [clang] [clang] Add aarch64-unknown-uefi target (PR #232705)
Prabhu Rajasekaran via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri Oct 16 04:57:14 PDT 2026
https://github.com/Prabhuk created https://github.com/llvm/llvm-project/pull/232705
Add the aarch64-unknown-uefi target to clang, building on the backend
support added in the preceding patches. Like x86_64-unknown-uefi it
follows the Microsoft ABI conventions, matching the Windows ARM64 target
which is the only way EFI applications for AArch64 can be built with
Clang/LLVM today:
* add UEFIAArch64TargetInfo: LLP64 types, Microsoft C++ ABI, char*
based va_list and the Win64 calling convention kind,
* use the Windows AArch64 ABI info in CodeGen for UEFI,
* accept aarch64 for *-uefi triples in the driver.
The UEFI toolchain already handles the linker invocation (lld-link with
/subsystem:efi_application), so no further driver changes are needed.
Assisted-by: Claude
---
<sub>Stack created with <a href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
>From b92d123a71db05336e703e5e5ddb5d331d54bb23 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Fri, 16 Oct 2026 04:51:33 -0700
Subject: [PATCH] [clang] Add aarch64-unknown-uefi target
Add the aarch64-unknown-uefi target to clang, building on the backend
support added in the preceding patches. Like x86_64-unknown-uefi it
follows the Microsoft ABI conventions, matching the Windows ARM64 target
which is the only way EFI applications for AArch64 can be built with
Clang/LLVM today:
* add UEFIAArch64TargetInfo: LLP64 types, Microsoft C++ ABI, char*
based va_list and the Win64 calling convention kind,
* use the Windows AArch64 ABI info in CodeGen for UEFI,
* accept aarch64 for *-uefi triples in the driver.
The UEFI toolchain already handles the linker invocation (lld-link with
/subsystem:efi_application), so no further driver changes are needed.
Assisted-by: Claude
---
clang/docs/ReleaseNotes.md | 4 ++
clang/lib/Basic/Targets.cpp | 2 +
clang/lib/Basic/Targets/AArch64.cpp | 50 +++++++++++++++++++
clang/lib/Basic/Targets/AArch64.h | 14 ++++++
clang/lib/CodeGen/CodeGenModule.cpp | 2 +-
clang/lib/Driver/Driver.cpp | 6 ++-
clang/test/CodeGen/AArch64/uefi-abi.c | 43 ++++++++++++++++
clang/test/CodeGen/AArch64/uefi-data-layout.c | 3 ++
clang/test/CodeGen/ms_abi_aarch64.c | 1 +
clang/test/CodeGen/stack-arg-probe.c | 2 +
clang/test/CodeGenCXX/mangle-ms.cpp | 1 +
.../Driver/aarch64-uefi-constructed-args.c | 19 +++++++
clang/test/Driver/uefi-defines.c | 1 +
clang/test/Driver/unsupported-target-arch.c | 6 ---
clang/test/Preprocessor/init.c | 2 +
15 files changed, 147 insertions(+), 9 deletions(-)
create mode 100644 clang/test/CodeGen/AArch64/uefi-abi.c
create mode 100644 clang/test/CodeGen/AArch64/uefi-data-layout.c
create mode 100644 clang/test/Driver/aarch64-uefi-constructed-args.c
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 3f0c48cf36644d..9b9732a06f5938 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -1114,6 +1114,10 @@ features cannot lower the translation-unit ABI level;
pointers stored in VTTs via the `-fptrauth-vtt-vtable-pointer-discrimination`
option.
+- Added support for the `aarch64-unknown-uefi` target triple. Like
+ `x86_64-unknown-uefi`, it follows the Microsoft ABI conventions (LLP64,
+ Microsoft C++ ABI, `char *` based `va_list`) and links with `lld-link`.
+
#### Android Support
- Enabled PAC and BTI by default for AArch64 Android targets.
diff --git a/clang/lib/Basic/Targets.cpp b/clang/lib/Basic/Targets.cpp
index 8c01cfca8ccb48..6314cdf12728ac 100644
--- a/clang/lib/Basic/Targets.cpp
+++ b/clang/lib/Basic/Targets.cpp
@@ -182,6 +182,8 @@ std::unique_ptr<TargetInfo> AllocateTarget(const llvm::Triple &Triple,
case llvm::Triple::Serenity:
return std::make_unique<SerenityTargetInfo<AArch64leTargetInfo>>(Triple,
Opts);
+ case llvm::Triple::UEFI:
+ return std::make_unique<UEFIAArch64TargetInfo>(Triple, Opts);
case llvm::Triple::Win32:
switch (Triple.getEnvironment()) {
case llvm::Triple::GNU:
diff --git a/clang/lib/Basic/Targets/AArch64.cpp b/clang/lib/Basic/Targets/AArch64.cpp
index 2e1c9336152f85..7ddf9d87ad163e 100644
--- a/clang/lib/Basic/Targets/AArch64.cpp
+++ b/clang/lib/Basic/Targets/AArch64.cpp
@@ -1900,6 +1900,56 @@ MinGWARM64TargetInfo::MinGWARM64TargetInfo(const llvm::Triple &Triple,
TheCXXABI.set(TargetCXXABI::GenericAArch64);
}
+UEFIAArch64TargetInfo::UEFIAArch64TargetInfo(const llvm::Triple &Triple,
+ const TargetOptions &Opts)
+ : UEFITargetInfo<AArch64leTargetInfo>(Triple, Opts) {
+ // The UEFI spec does not mandate specific C++ ABI, integer widths, or
+ // alignment. We are setting these defaults to match the Windows ARM64 target
+ // as it is the only way to build EFI applications with Clang/LLVM today.
+ // This is an LLP64 platform.
+ // int:4, long:4, long long:8, long double:8.
+ TheCXXABI.set(TargetCXXABI::Microsoft);
+ 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;
+}
+
+TargetInfo::BuiltinVaListKind
+UEFIAArch64TargetInfo::getBuiltinVaListKind() const {
+ return TargetInfo::CharPtrBuiltinVaList;
+}
+
+TargetInfo::CallingConvCheckResult
+UEFIAArch64TargetInfo::checkCallingConvention(CallingConv CC) const {
+ switch (CC) {
+ case CC_X86StdCall:
+ case CC_X86ThisCall:
+ case CC_X86FastCall:
+ case CC_X86VectorCall:
+ return CCCR_Ignore;
+ case CC_C:
+ case CC_PreserveMost:
+ case CC_PreserveAll:
+ case CC_PreserveNone:
+ case CC_Win64:
+ return CCCR_OK;
+ default:
+ return CCCR_Warning;
+ }
+}
+
+TargetInfo::CallingConvKind
+UEFIAArch64TargetInfo::getCallingConvKind(bool ClangABICompat4) const {
+ return CCK_MicrosoftWin64;
+}
+
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 922b2b453b750d..00e84cc4864399 100644
--- a/clang/lib/Basic/Targets/AArch64.h
+++ b/clang/lib/Basic/Targets/AArch64.h
@@ -320,6 +320,20 @@ class LLVM_LIBRARY_VISIBILITY MinGWARM64TargetInfo
MinGWARM64TargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts);
};
+// ARM64 UEFI target
+class LLVM_LIBRARY_VISIBILITY UEFIAArch64TargetInfo
+ : public UEFITargetInfo<AArch64leTargetInfo> {
+public:
+ UEFIAArch64TargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts);
+
+ BuiltinVaListKind getBuiltinVaListKind() const override;
+
+ CallingConvCheckResult checkCallingConvention(CallingConv CC) const override;
+
+ TargetInfo::CallingConvKind
+ getCallingConvKind(bool ClangABICompat4) const override;
+};
+
class LLVM_LIBRARY_VISIBILITY AArch64beTargetInfo : public AArch64TargetInfo {
public:
AArch64beTargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts);
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 00a82819681c14..75e7c292d113d8 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -153,7 +153,7 @@ createTargetCodeGenInfo(CodeGenModule &CGM) {
AArch64ABIKind Kind = AArch64ABIKind::AAPCS;
if (Target.getABI() == "darwinpcs")
Kind = AArch64ABIKind::DarwinPCS;
- else if (Triple.isOSWindows())
+ else if (Triple.isOSWindowsOrUEFI())
return createWindowsAArch64TargetCodeGenInfo(CGM, AArch64ABIKind::Win64);
else if (Target.getABI() == "aapcs-soft")
Kind = AArch64ABIKind::AAPCSSoft;
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index a9a21175af9c5e..555a4ada8477e6 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -734,8 +734,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)
+ // Currently the only architectures supported by *-uefi triples are x86_64
+ // and aarch64.
+ 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/clang/test/CodeGen/AArch64/uefi-abi.c b/clang/test/CodeGen/AArch64/uefi-abi.c
new file mode 100644
index 00000000000000..1b1bfacb9b3b92
--- /dev/null
+++ b/clang/test/CodeGen/AArch64/uefi-abi.c
@@ -0,0 +1,43 @@
+// RUN: %clang_cc1 -triple aarch64-unknown-uefi -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple aarch64-pc-windows-msvc -emit-llvm -o - %s | FileCheck %s
+
+// aarch64-unknown-uefi follows the Windows ARM64 ABI: LLP64 types, char* based
+// va_list and the Microsoft C++ ABI.
+
+#include <stdarg.h>
+
+// CHECK: @sizeof_long = {{.*}}global i32 4
+// CHECK: @sizeof_wchar = {{.*}}global i32 2
+// CHECK: @sizeof_long_double = {{.*}}global i32 8
+// CHECK: @sizeof_va_list = {{.*}}global i32 8
+int sizeof_long = sizeof(long);
+int sizeof_wchar = sizeof(__WCHAR_TYPE__);
+int sizeof_long_double = sizeof(long double);
+int sizeof_va_list = sizeof(va_list);
+
+// char is signed on AArch64 Windows and UEFI.
+// CHECK: @char_is_signed = {{.*}}global i32 1
+int char_is_signed = (char)-1 < 0;
+
+// CHECK-LABEL: define dso_local i32 @sum(i32 noundef %n, ...)
+// CHECK: %ap = alloca ptr
+// CHECK: call void @llvm.va_start.p0(ptr %ap)
+// CHECK: load ptr, ptr %ap
+// CHECK: getelementptr inbounds i8, ptr %{{.*}}, i64 8
+int sum(int n, ...) {
+ va_list ap;
+ va_start(ap, n);
+ int s = 0;
+ for (int i = 0; i < n; i++)
+ s += va_arg(ap, int);
+ va_end(ap);
+ return s;
+}
+
+// Small aggregates larger than 8 bytes are passed indirectly as in the
+// Windows ARM64 ABI.
+struct larger {
+ int a, b, c, d, e;
+};
+// CHECK-LABEL: define dso_local void @pass_larger(ptr {{.*}}dead_on_return{{.*}} %s)
+void pass_larger(struct larger s) {}
diff --git a/clang/test/CodeGen/AArch64/uefi-data-layout.c b/clang/test/CodeGen/AArch64/uefi-data-layout.c
new file mode 100644
index 00000000000000..2892b975babf5c
--- /dev/null
+++ b/clang/test/CodeGen/AArch64/uefi-data-layout.c
@@ -0,0 +1,3 @@
+// RUN: %clang -target aarch64-unknown-uefi -S -emit-llvm -o - %s | \
+// RUN: FileCheck --check-prefix=AARCH64_UEFI %s
+// AARCH64_UEFI: target datalayout = "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"
diff --git a/clang/test/CodeGen/ms_abi_aarch64.c b/clang/test/CodeGen/ms_abi_aarch64.c
index b8717e8738d0e5..ac0da13a2efdff 100644
--- a/clang/test/CodeGen/ms_abi_aarch64.c
+++ b/clang/test/CodeGen/ms_abi_aarch64.c
@@ -1,5 +1,6 @@
// RUN: %clang_cc1 -triple aarch64-linux-gnu -emit-llvm < %s | FileCheck -check-prefixes=LINUX,COMMON %s
// RUN: %clang_cc1 -triple aarch64-pc-win32 -emit-llvm < %s | FileCheck -check-prefixes=WIN64,COMMON %s
+// RUN: %clang_cc1 -triple aarch64-unknown-uefi -emit-llvm < %s | FileCheck -check-prefixes=WIN64,COMMON %s
struct small_odd {
char a, b, c;
diff --git a/clang/test/CodeGen/stack-arg-probe.c b/clang/test/CodeGen/stack-arg-probe.c
index 255ae482b68083..74631f51375ae9 100644
--- a/clang/test/CodeGen/stack-arg-probe.c
+++ b/clang/test/CodeGen/stack-arg-probe.c
@@ -2,10 +2,12 @@
// RUN: %clang_cc1 %s -triple=x86_64-windows-msvc -emit-llvm -o - -mno-stack-arg-probe | FileCheck %s -check-prefix=NO-STACKPROBE
// RUN: %clang_cc1 %s -triple=armv7-windows-msvc -emit-llvm -o - -mno-stack-arg-probe | FileCheck %s -check-prefix=NO-STACKPROBE
// RUN: %clang_cc1 %s -triple=aarch64-windows-msvc -emit-llvm -o - -mno-stack-arg-probe | FileCheck %s -check-prefix=NO-STACKPROBE
+// RUN: %clang_cc1 %s -triple=aarch64-unknown-uefi -emit-llvm -o - -mno-stack-arg-probe | FileCheck %s -check-prefix=NO-STACKPROBE
// RUN: %clang_cc1 %s -triple=i686-windows-msvc -emit-llvm -o - | FileCheck %s -check-prefix=STACKPROBE
// RUN: %clang_cc1 %s -triple=x86_64-windows-msvc -emit-llvm -o - | FileCheck %s -check-prefix=STACKPROBE
// RUN: %clang_cc1 %s -triple=armv7-windows-msvc -emit-llvm -o - | FileCheck %s -check-prefix=STACKPROBE
// RUN: %clang_cc1 %s -triple=aarch64-windows-msvc -emit-llvm -o - | FileCheck %s -check-prefix=STACKPROBE
+// RUN: %clang_cc1 %s -triple=aarch64-unknown-uefi -emit-llvm -o - | FileCheck %s -check-prefix=STACKPROBE
// NO-STACKPROBE: attributes #{{[0-9]+}} = {{{.*}} "no-stack-arg-probe"
diff --git a/clang/test/CodeGenCXX/mangle-ms.cpp b/clang/test/CodeGenCXX/mangle-ms.cpp
index 606aeb86ac9320..f18ac17c82432c 100644
--- a/clang/test/CodeGenCXX/mangle-ms.cpp
+++ b/clang/test/CodeGenCXX/mangle-ms.cpp
@@ -2,6 +2,7 @@
// RUN: %clang_cc1 -fblocks -emit-llvm %s -o - -triple=x86_64-pc-win32 -std=c++98| FileCheck -check-prefix X64 %s
// RUN: %clang_cc1 -fblocks -emit-llvm %s -o - -triple=x86_64-uefi -std=c++98| FileCheck -check-prefix X64 %s
// RUN: %clang_cc1 -fblocks -emit-llvm %s -o - -triple=aarch64-pc-win32 -std=c++98 -DARM | FileCheck -check-prefixes=X64,ARM %s
+// RUN: %clang_cc1 -fblocks -emit-llvm %s -o - -triple=aarch64-uefi -std=c++98 -DARM | FileCheck -check-prefixes=X64,ARM %s
int a;
// CHECK-DAG: @"?a@@3HA"
diff --git a/clang/test/Driver/aarch64-uefi-constructed-args.c b/clang/test/Driver/aarch64-uefi-constructed-args.c
new file mode 100644
index 00000000000000..704bdf6a79db3c
--- /dev/null
+++ b/clang/test/Driver/aarch64-uefi-constructed-args.c
@@ -0,0 +1,19 @@
+// RUN: %clang -### --target=aarch64-unknown-uefi -mno-incremental-linker-compatible -g -- %s 2>&1 \
+// RUN: | FileCheck -check-prefixes=CHECK %s
+// RUN: %clang_cl -### --target=aarch64-unknown-uefi -mno-incremental-linker-compatible -g -- %s 2>&1 \
+// RUN: | FileCheck -check-prefixes=CHECK %s
+// CHECK: "-cc1"
+// CHECK-SAME: "-triple" "aarch64-unknown-uefi"
+// CHECK-SAME: "-mrelocation-model" "pic" "-pic-level" "2"
+// CHECK-SAME: "-fms-extensions"
+// CHECK-NEXT: "/nologo"
+// CHECK-SAME: "/subsystem:efi_application"
+// CHECK-SAME: "/entry:EfiMain"
+// CHECK-SAME: "/tsaware:no"
+// CHECK-SAME: "/debug"
+// CHECK-SAME: "/Brepro"
+
+// RUN: %clang -### --target=aarch64-unknown-uefi -print-search-dirs 2>&1 \
+// RUN: | FileCheck -check-prefixes=PROGPATH %s
+// PROGPATH: InstalledDir: [[DRIVER_INSTALLED_DIR:.*]]
+// PROGPATH: programs: =[[DRIVER_INSTALLED_DIR]]
diff --git a/clang/test/Driver/uefi-defines.c b/clang/test/Driver/uefi-defines.c
index 45f27bfdb9fa98..5f446853a4399c 100644
--- a/clang/test/Driver/uefi-defines.c
+++ b/clang/test/Driver/uefi-defines.c
@@ -1,4 +1,5 @@
// RUN: %clang -target x86_64-unknown-uefi %s -emit-llvm -S -c -o - | FileCheck %s
+// RUN: %clang -target aarch64-unknown-uefi %s -emit-llvm -S -c -o - | FileCheck %s
// CHECK: __UEFI__defined
#ifdef __UEFI__
diff --git a/clang/test/Driver/unsupported-target-arch.c b/clang/test/Driver/unsupported-target-arch.c
index 426f646db50b05..0d52f44e1eafb1 100644
--- a/clang/test/Driver/unsupported-target-arch.c
+++ b/clang/test/Driver/unsupported-target-arch.c
@@ -60,12 +60,6 @@
// RUN: FileCheck --input-file=%t.err --check-prefix=CHECK-PPCMAC %s
// CHECK-PPCMAC: error: unknown target triple 'unknown-apple-macosx{{.*}}'
-// RUN: not %clang --target=aarch64-unknown-uefi -o %t.o %s 2> %t.err
-// RUN: FileCheck --input-file=%t.err --check-prefix=CHECK-AARCH64 %s
-// RUN: not %clang_cl --target=aarch64-unknown-uefi -o %t.o %s 2> %t.err
-// RUN: FileCheck --input-file=%t.err --check-prefix=CHECK-AARCH64 %s
-// CHECK-AARCH64: error: unknown target triple 'aarch64-unknown-uefi'{{$}}
-
// RUN: not %clang --target=arm-unknown-uefi -o %t.o %s 2> %t.err
// RUN: FileCheck --input-file=%t.err -check-prefixes=CHECK-ARM %s
// RUN: not %clang_cl --target=arm-unknown-uefi -o %t.o %s 2> %t.err
diff --git a/clang/test/Preprocessor/init.c b/clang/test/Preprocessor/init.c
index df8142b4c273fe..8a36e40ab96a0f 100644
--- a/clang/test/Preprocessor/init.c
+++ b/clang/test/Preprocessor/init.c
@@ -2900,6 +2900,8 @@
// RUN: %clang_cc1 -dM -triple=x86_64-uefi -E /dev/null | FileCheck -match-full-lines -check-prefix UEFI %s
// RUN: %clang_cc1 -dM -triple=x86_64-unknown-uefi -E /dev/null | FileCheck -match-full-lines -check-prefix UEFI %s
+// RUN: %clang_cc1 -dM -triple=aarch64-uefi -E /dev/null | FileCheck -match-full-lines -check-prefix UEFI %s
+// RUN: %clang_cc1 -dM -triple=aarch64-unknown-uefi -E /dev/null | FileCheck -match-full-lines -check-prefix UEFI %s
// UEFI: #define __UEFI__ 1
More information about the llvm-branch-commits
mailing list