Skip to content

[ARM64EC] Add support for function aliases on ARM64EC #132295

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

Merged
merged 1 commit into from
Mar 31, 2025

Conversation

cjacek
Copy link
Contributor

@cjacek cjacek commented Mar 20, 2025

Required for mingw-w64, which uses the alias attribute in its CRT.

Follows ARM64EC mangling rules by mangling the alias symbol and emitting an unmangled anti-dependency alias. Since metadata is not allowed on GlobalAlias objects, extend arm64ec_unmangled_name to support multiple unmangled names and attach the alias anti-dependency name to the target function's metadata.

@llvmbot
Copy link
Member

llvmbot commented Mar 20, 2025

@llvm/pr-subscribers-backend-aarch64

Author: Jacek Caban (cjacek)

Changes

Required for mingw-w64, which uses the alias attribute in its CRT.

Follows ARM64EC mangling rules by mangling the alias symbol and emitting an unmangled anti-dependency alias. Since metadata is not allowed on GlobalAlias objects, extend arm64ec_unmangled_name to support multiple unmangled names and attach the alias anti-dependency name to the target function's metadata.


Full diff: https://github.com/llvm/llvm-project/pull/132295.diff

4 Files Affected:

  • (modified) llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp (+29-8)
  • (modified) llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp (+13-13)
  • (added) llvm/test/CodeGen/AArch64/arm64ec-alias.ll (+42)
  • (modified) llvm/test/CodeGen/AArch64/dllexport.ll (+8-8)
diff --git a/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp b/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp
index 066d62b3d4b4b..9553a44fb317e 100644
--- a/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp
@@ -627,10 +627,10 @@ Function *AArch64Arm64ECCallLowering::buildGuestExitThunk(Function *F) {
       Function::Create(Arm64Ty, GlobalValue::WeakODRLinkage, 0, ThunkName, M);
   GuestExit->setComdat(M->getOrInsertComdat(ThunkName));
   GuestExit->setSection(".wowthk$aa");
-  GuestExit->setMetadata(
+  GuestExit->addMetadata(
       "arm64ec_unmangled_name",
-      MDNode::get(M->getContext(),
-                  MDString::get(M->getContext(), F->getName())));
+      *MDNode::get(M->getContext(),
+                   MDString::get(M->getContext(), F->getName())));
   GuestExit->setMetadata(
       "arm64ec_ecmangled_name",
       MDNode::get(M->getContext(),
@@ -803,6 +803,23 @@ bool AArch64Arm64ECCallLowering::runOnModule(Module &Mod) {
   DispatchFnGlobal =
       M->getOrInsertGlobal("__os_arm64x_dispatch_call", DispatchFnPtrType);
 
+  // Mangle names of function aliases and add the alias name to
+  // arm64ec_unmangled_name metadata to ensure a weak anti-dependency symbol is
+  // emitted for the alias as well. Do this early, before handling
+  // hybrid_patchable functions, to avoid mangling their aliases.
+  for (GlobalAlias &A : Mod.aliases()) {
+    auto F = dyn_cast_or_null<Function>(A.getAliaseeObject());
+    if (!F)
+      continue;
+    if (std::optional<std::string> MangledName =
+            getArm64ECMangledFunctionName(A.getName().str())) {
+      F->addMetadata("arm64ec_unmangled_name",
+                     *MDNode::get(M->getContext(),
+                                  MDString::get(M->getContext(), A.getName())));
+      A.setName(MangledName.value());
+    }
+  }
+
   DenseMap<GlobalAlias *, GlobalAlias *> FnsMap;
   SetVector<GlobalAlias *> PatchableFns;
 
@@ -837,20 +854,24 @@ bool AArch64Arm64ECCallLowering::runOnModule(Module &Mod) {
       // emitGlobalAlias to emit the right alias.
       auto *A =
           GlobalAlias::create(GlobalValue::LinkOnceODRLinkage, OrigName, &F);
+      auto *AM = GlobalAlias::create(GlobalValue::LinkOnceODRLinkage,
+                                     MangledName.value(), &F);
+      F.replaceUsesWithIf(AM,
+                          [](Use &U) { return isa<GlobalAlias>(U.getUser()); });
       F.replaceAllUsesWith(A);
       F.setMetadata("arm64ec_exp_name",
                     MDNode::get(M->getContext(),
                                 MDString::get(M->getContext(),
                                               "EXP+" + MangledName.value())));
       A->setAliasee(&F);
+      AM->setAliasee(&F);
 
       if (F.hasDLLExportStorageClass()) {
         A->setDLLStorageClass(GlobalValue::DLLExportStorageClass);
         F.setDLLStorageClass(GlobalValue::DefaultStorageClass);
       }
 
-      FnsMap[A] = GlobalAlias::create(GlobalValue::LinkOnceODRLinkage,
-                                      MangledName.value(), &F);
+      FnsMap[A] = AM;
       PatchableFns.insert(A);
     }
   }
@@ -928,9 +949,9 @@ bool AArch64Arm64ECCallLowering::processFunction(
   if (!F.hasLocalLinkage() || F.hasAddressTaken()) {
     if (std::optional<std::string> MangledName =
             getArm64ECMangledFunctionName(F.getName().str())) {
-      F.setMetadata("arm64ec_unmangled_name",
-                    MDNode::get(M->getContext(),
-                                MDString::get(M->getContext(), F.getName())));
+      F.addMetadata("arm64ec_unmangled_name",
+                    *MDNode::get(M->getContext(),
+                                 MDString::get(M->getContext(), F.getName())));
       if (F.hasComdat() && F.getComdat()->getName() == F.getName()) {
         Comdat *MangledComdat = M->getOrInsertComdat(MangledName.value());
         SmallVector<GlobalObject *> ComdatUsers =
diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
index 47c0a1fab08f7..5e9bc4f48b0af 100644
--- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
+++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
@@ -45,6 +45,7 @@
 #include "llvm/CodeGen/TargetRegisterInfo.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/DebugInfoMetadata.h"
+#include "llvm/IR/Mangler.h"
 #include "llvm/IR/Module.h"
 #include "llvm/MC/MCAsmInfo.h"
 #include "llvm/MC/MCContext.h"
@@ -1381,22 +1382,21 @@ void AArch64AsmPrinter::emitFunctionEntryLabel() {
       return Sym;
     };
 
-    if (MCSymbol *UnmangledSym =
-            getSymbolFromMetadata("arm64ec_unmangled_name")) {
-      MCSymbol *ECMangledSym = getSymbolFromMetadata("arm64ec_ecmangled_name");
-
-      if (ECMangledSym) {
-        // An external function, emit the alias from the unmangled symbol to
-        // mangled symbol name and the alias from the mangled symbol to guest
-        // exit thunk.
+    SmallVector<MDNode *> UnmangledNames;
+    MF->getFunction().getMetadata("arm64ec_unmangled_name", UnmangledNames);
+    for (MDNode *Node : UnmangledNames) {
+      StringRef NameStr = cast<MDString>(Node->getOperand(0))->getString();
+      MCSymbol *UnmangledSym = MMI->getContext().getOrCreateSymbol(NameStr);
+      if (std::optional<std::string> MangledName =
+              getArm64ECMangledFunctionName(UnmangledSym->getName())) {
+        MCSymbol *ECMangledSym =
+            MMI->getContext().getOrCreateSymbol(*MangledName);
         emitFunctionAlias(UnmangledSym, ECMangledSym);
-        emitFunctionAlias(ECMangledSym, CurrentFnSym);
-      } else {
-        // A function implementation, emit the alias from the unmangled symbol
-        // to mangled symbol name.
-        emitFunctionAlias(UnmangledSym, CurrentFnSym);
       }
     }
+    if (MCSymbol *ECMangledSym =
+            getSymbolFromMetadata("arm64ec_ecmangled_name"))
+      emitFunctionAlias(ECMangledSym, CurrentFnSym);
   }
 }
 
diff --git a/llvm/test/CodeGen/AArch64/arm64ec-alias.ll b/llvm/test/CodeGen/AArch64/arm64ec-alias.ll
new file mode 100644
index 0000000000000..03cc873136940
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/arm64ec-alias.ll
@@ -0,0 +1,42 @@
+; RUN: llc -mtriple arm64ec-windows-msvc -filetype asm -o - %s | FileCheck %s
+
+define void @func() {
+  ret void
+}
+
+define dso_local void @patchable_func() hybrid_patchable {
+  ret void
+}
+
+@func_alias = alias void (), ptr @func
+@func_alias2 = alias void (), ptr @func_alias
+@patchable_alias = alias void (), ptr @patchable_func
+
+; CHECK:              .weak_anti_dep  func_alias
+; CHECK-NEXT: .set func_alias, "#func_alias"
+; CHECK-NEXT:         .weak_anti_dep  func_alias2
+; CHECK-NEXT: .set func_alias2, "#func_alias2"
+; CHECK-NEXT:         .weak_anti_dep  func
+; CHECK-NEXT: .set func, "#func"
+; CHECK:              .weak_anti_dep  patchable_alias
+; CHECK-NEXT: .set patchable_alias, "#patchable_alias"
+
+; CHECK:              .globl  "#func_alias"
+; CHECK-NEXT:         .def    "#func_alias";
+; CHECK-NEXT:         .scl    2;
+; CHECK-NEXT:         .type   32;
+; CHECK-NEXT:         .endef
+; CHECK-NEXT: .set "#func_alias", "#func"
+; CHECK-NEXT:         .globl  "#func_alias2"
+; CHECK-NEXT:         .def    "#func_alias2";
+; CHECK-NEXT:         .scl    2;
+; CHECK-NEXT:         .type   32;
+; CHECK-NEXT:         .endef
+; CHECK-NEXT: .set "#func_alias2", "#func_alias"
+
+; CHECK:              .globl  "#patchable_alias"
+; CHECK-NEXT:         .def    "#patchable_alias";
+; CHECK-NEXT:         .scl    2;
+; CHECK-NEXT:         .type   32;
+; CHECK-NEXT:         .endef
+; CHECK-NEXT: .set "#patchable_alias", "#patchable_func"
diff --git a/llvm/test/CodeGen/AArch64/dllexport.ll b/llvm/test/CodeGen/AArch64/dllexport.ll
index 580fb5fd9e79e..e15fc0a928b66 100644
--- a/llvm/test/CodeGen/AArch64/dllexport.ll
+++ b/llvm/test/CodeGen/AArch64/dllexport.ll
@@ -88,10 +88,10 @@ define weak_odr dllexport void @l() {
 ; CHECK-GNU-EC: .ascii " -export:o,data"
 ; CHECK-GNU-EC: .ascii " -export:p,data"
 ; CHECK-GNU-EC: .ascii " -export:q,data"
-; CHECK-GNU-EC: .ascii " -export:r"
-; CHECK-GNU-EC: .ascii " -export:s"
-; CHECK-GNU-EC: .ascii " -export:t"
-; CHECK-GNU-EC: .ascii " -export:u"
+; CHECK-GNU-EC: .ascii " -export:#r,EXPORTAS,r"
+; CHECK-GNU-EC: .ascii " -export:#s,EXPORTAS,s"
+; CHECK-GNU-EC: .ascii " -export:#t,EXPORTAS,t"
+; CHECK-GNU-EC: .ascii " -export:#u,EXPORTAS,u"
 ; CHECK-MSVC-EC-NOT: /EXPORT:f
 ; CHECK-MSVC-EC-NOT: /EXPORT:#f,EXPORTAS,f
 ; CHECK-MSVC-EC: .ascii "  /EXPORT:#g,EXPORTAS,g"
@@ -106,7 +106,7 @@ define weak_odr dllexport void @l() {
 ; CHECK-MSVC-EC: .ascii "  /EXPORT:o,DATA"
 ; CHECK-MSVC-EC: .ascii "  /EXPORT:p,DATA"
 ; CHECK-MSVC-EC: .ascii "  /EXPORT:q,DATA"
-; CHECK-MSVC-EC: .ascii "  /EXPORT:r"
-; CHECK-MSVC-EC: .ascii "  /EXPORT:s"
-; CHECK-MSVC-EC: .ascii "  /EXPORT:t"
-; CHECK-MSVC-EC: .ascii "  /EXPORT:u"
+; CHECK-MSVC-EC: .ascii "  /EXPORT:#r,EXPORTAS,r"
+; CHECK-MSVC-EC: .ascii "  /EXPORT:#s,EXPORTAS,s"
+; CHECK-MSVC-EC: .ascii "  /EXPORT:#t,EXPORTAS,t"
+; CHECK-MSVC-EC: .ascii "  /EXPORT:#u,EXPORTAS,u"

; CHECK-MSVC-EC: .ascii " /EXPORT:s"
; CHECK-MSVC-EC: .ascii " /EXPORT:t"
; CHECK-MSVC-EC: .ascii " /EXPORT:u"
; CHECK-MSVC-EC: .ascii " /EXPORT:#r,EXPORTAS,r"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this work with the MSVC linker?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it works, I rechecked to be sure. Thanks!

Required for mingw-w64, which uses the alias attribute in its CRT.

Follows ARM64EC mangling rules by mangling the alias symbol and emitting an unmangled
anti-dependency alias. Since metadata is not allowed on GlobalAlias objects, extend
arm64ec_unmangled_name to support multiple unmangled names and attach the alias
anti-dependency name to the target function's metadata.
@cjacek cjacek merged commit 606e0b4 into llvm:main Mar 31, 2025
9 of 11 checks passed
@cjacek cjacek deleted the arm64ec-alias branch March 31, 2025 10:56
SchrodingerZhu pushed a commit to SchrodingerZhu/llvm-project that referenced this pull request Mar 31, 2025
Required for mingw-w64, which uses the alias attribute in its CRT.

Follows ARM64EC mangling rules by mangling the alias symbol and emitting
an unmangled anti-dependency alias. Since metadata is not allowed on
GlobalAlias objects, extend arm64ec_unmangled_name to support multiple
unmangled names and attach the alias anti-dependency name to the target
function's metadata.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants