[llvm] [win][arm64ec] Handle empty function names (PR #151609)

Daniel Paoliello via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 1 10:53:10 PDT 2025


https://github.com/dpaoliello updated https://github.com/llvm/llvm-project/pull/151609

>From 59e8cda65f2a6b52815d14bdb28bf836813d6994 Mon Sep 17 00:00:00 2001
From: Daniel Paoliello <danpao at microsoft.com>
Date: Thu, 31 Jul 2025 15:27:29 -0700
Subject: [PATCH] [win][arm64ec] Handle empty function names

---
 llvm/lib/IR/Mangler.cpp                        |  3 +++
 .../AArch64/AArch64Arm64ECCallLowering.cpp     | 18 +++++++++++++-----
 .../test/CodeGen/AArch64/arm64ec-empty-name.ll | 15 +++++++++++++++
 3 files changed, 31 insertions(+), 5 deletions(-)
 create mode 100644 llvm/test/CodeGen/AArch64/arm64ec-empty-name.ll

diff --git a/llvm/lib/IR/Mangler.cpp b/llvm/lib/IR/Mangler.cpp
index 010bd15e256dc..ca6a4804087ac 100644
--- a/llvm/lib/IR/Mangler.cpp
+++ b/llvm/lib/IR/Mangler.cpp
@@ -292,6 +292,9 @@ void llvm::emitLinkerFlagsForUsedCOFF(raw_ostream &OS, const GlobalValue *GV,
 }
 
 std::optional<std::string> llvm::getArm64ECMangledFunctionName(StringRef Name) {
+  assert(!Name.empty() &&
+         "getArm64ECMangledFunctionName requires non-empty name");
+
   if (Name[0] != '?') {
     // For non-C++ symbols, prefix the name with "#" unless it's already
     // mangled.
diff --git a/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp b/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp
index 509cbb092705d..3748590ec6c4f 100644
--- a/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp
@@ -597,6 +597,14 @@ Function *AArch64Arm64ECCallLowering::buildEntryThunk(Function *F) {
   return Thunk;
 }
 
+std::optional<std::string> getArm64ECMangledFunctionName(GlobalValue &GV) {
+  if (!GV.hasName()) {
+    GV.setName("__unnamed");
+  }
+
+  return llvm::getArm64ECMangledFunctionName(GV.getName());
+}
+
 // Builds the "guest exit thunk", a helper to call a function which may or may
 // not be an exit thunk. (We optimistically assume non-dllimport function
 // declarations refer to functions defined in AArch64 code; if the linker
@@ -608,7 +616,7 @@ Function *AArch64Arm64ECCallLowering::buildGuestExitThunk(Function *F) {
   getThunkType(F->getFunctionType(), F->getAttributes(),
                Arm64ECThunkType::GuestExit, NullThunkName, Arm64Ty, X64Ty,
                ArgTranslations);
-  auto MangledName = getArm64ECMangledFunctionName(F->getName().str());
+  auto MangledName = getArm64ECMangledFunctionName(*F);
   assert(MangledName && "Can't guest exit to function that's already native");
   std::string ThunkName = *MangledName;
   if (ThunkName[0] == '?' && ThunkName.find("@") != std::string::npos) {
@@ -790,7 +798,7 @@ bool AArch64Arm64ECCallLowering::runOnModule(Module &Mod) {
     if (!F)
       continue;
     if (std::optional<std::string> MangledName =
-            getArm64ECMangledFunctionName(A.getName().str())) {
+            getArm64ECMangledFunctionName(A)) {
       F->addMetadata("arm64ec_unmangled_name",
                      *MDNode::get(M->getContext(),
                                   MDString::get(M->getContext(), A.getName())));
@@ -807,7 +815,7 @@ bool AArch64Arm64ECCallLowering::runOnModule(Module &Mod) {
           cast<GlobalValue>(F.getPersonalityFn()->stripPointerCasts());
       if (PersFn->getValueType() && PersFn->getValueType()->isFunctionTy()) {
         if (std::optional<std::string> MangledName =
-                getArm64ECMangledFunctionName(PersFn->getName().str())) {
+                getArm64ECMangledFunctionName(*PersFn)) {
           PersFn->setName(MangledName.value());
         }
       }
@@ -821,7 +829,7 @@ bool AArch64Arm64ECCallLowering::runOnModule(Module &Mod) {
     // Rename hybrid patchable functions and change callers to use a global
     // alias instead.
     if (std::optional<std::string> MangledName =
-            getArm64ECMangledFunctionName(F.getName().str())) {
+            getArm64ECMangledFunctionName(F)) {
       std::string OrigName(F.getName());
       F.setName(MangledName.value() + HybridPatchableTargetSuffix);
 
@@ -926,7 +934,7 @@ bool AArch64Arm64ECCallLowering::processFunction(
   // FIXME: Handle functions with weak linkage?
   if (!F.hasLocalLinkage() || F.hasAddressTaken()) {
     if (std::optional<std::string> MangledName =
-            getArm64ECMangledFunctionName(F.getName().str())) {
+            getArm64ECMangledFunctionName(F)) {
       F.addMetadata("arm64ec_unmangled_name",
                     *MDNode::get(M->getContext(),
                                  MDString::get(M->getContext(), F.getName())));
diff --git a/llvm/test/CodeGen/AArch64/arm64ec-empty-name.ll b/llvm/test/CodeGen/AArch64/arm64ec-empty-name.ll
new file mode 100644
index 0000000000000..c7c9ee59e1ccc
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/arm64ec-empty-name.ll
@@ -0,0 +1,15 @@
+; RUN: llc -mtriple=arm64ec-pc-windows-msvc %s -o - | FileCheck %s
+
+; Regression test: Arm64EC needs to look at the first character of a function
+; to decide if it will be mangled like a C or C++ function name, which caused
+; it to crash for empty function names.
+define void @""() {
+        ret void
+}
+
+define void @""() {
+        ret void
+}
+
+; CHECK: "#__unnamed":
+; CHECK: "#__unnamed.1":



More information about the llvm-commits mailing list