[clang] [Clang] Allow vanilla C function symbol name to be used in __attribute__((alias)) when -funique-internal-linkage-names is specified (PR #145652)

via cfe-commits cfe-commits at lists.llvm.org
Sat Jul 12 08:42:08 PDT 2025


https://github.com/HighW4y2H3ll updated https://github.com/llvm/llvm-project/pull/145652

>From 9a29dd6fa3f28bf507f047a22597f8510bd096b3 Mon Sep 17 00:00:00 2001
From: h2h <h2h at meta.com>
Date: Tue, 24 Jun 2025 23:24:32 -0700
Subject: [PATCH 1/9] [Clang] Allow vanilla C function symbol name to be used
 in __attribute__((alias)) when -funique-internal-linkage-names is specified

---
 clang/lib/CodeGen/CodeGenModule.cpp           | 48 +++++++++++++++++--
 .../unique-internal-linkage-names-alias.c     | 10 ++++
 2 files changed, 54 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/CodeGen/unique-internal-linkage-names-alias.c

diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 16688810d0685..90f02220ec306 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -588,8 +588,9 @@ static const llvm::GlobalValue *getAliasedGlobal(const llvm::GlobalValue *GV) {
 }
 
 static bool checkAliasedGlobal(
-    const ASTContext &Context, DiagnosticsEngine &Diags, SourceLocation Location,
-    bool IsIFunc, const llvm::GlobalValue *Alias, const llvm::GlobalValue *&GV,
+    const CodeGenModule *CGM, const ASTContext &Context,
+    DiagnosticsEngine &Diags, SourceLocation Location, bool IsIFunc,
+    const llvm::GlobalValue *Alias, const llvm::GlobalValue *&GV,
     const llvm::MapVector<GlobalDecl, StringRef> &MangledDeclNames,
     SourceRange AliasRange) {
   GV = getAliasedGlobal(Alias);
@@ -598,6 +599,23 @@ static bool checkAliasedGlobal(
     return false;
   }
 
+  // Only resolve unique internal linkage symbols for C code
+  if (!CGM->getLangOpts().CPlusPlus) {
+    for (const auto &[Decl, Name] : MangledDeclNames) {
+      if (const auto *ND = dyn_cast<NamedDecl>(Decl.getDecl())) {
+        IdentifierInfo *II = ND->getIdentifier();
+        if (II && II->getName() == GV->getName() &&
+            Name.contains(llvm::FunctionSamples::UniqSuffix)) {
+          GlobalDecl GD;
+          if (CGM->lookupRepresentativeDecl(Name, GD)) {
+            GV = CGM->getModule().getNamedValue(Name);
+            break;
+          }
+        }
+      }
+    }
+  }
+
   if (GV->hasCommonLinkage()) {
     const llvm::Triple &Triple = Context.getTargetInfo().getTriple();
     if (Triple.getObjectFormat() == llvm::Triple::XCOFF) {
@@ -687,8 +705,8 @@ void CodeGenModule::checkAliases() {
     StringRef MangledName = getMangledName(GD);
     llvm::GlobalValue *Alias = GetGlobalValue(MangledName);
     const llvm::GlobalValue *GV = nullptr;
-    if (!checkAliasedGlobal(getContext(), Diags, Location, IsIFunc, Alias, GV,
-                            MangledDeclNames, Range)) {
+    if (!checkAliasedGlobal(this, getContext(), Diags, Location, IsIFunc, Alias,
+                            GV, MangledDeclNames, Range)) {
       Error = true;
       continue;
     }
@@ -4038,6 +4056,7 @@ void CodeGenModule::EmitGlobal(GlobalDecl GD) {
     CXXGlobalInits.push_back(nullptr);
   }
 
+  const auto *ND = dyn_cast<NamedDecl>(GD.getDecl());
   StringRef MangledName = getMangledName(GD);
   if (GetGlobalValue(MangledName) != nullptr) {
     // The value has already been used and should therefore be emitted.
@@ -4046,6 +4065,12 @@ void CodeGenModule::EmitGlobal(GlobalDecl GD) {
     // The value must be emitted, but cannot be emitted eagerly.
     assert(!MayBeEmittedEagerly(Global));
     addDeferredDeclToEmit(GD);
+  } else if (!getLangOpts().CPlusPlus && ND &&
+             GetGlobalValue(ND->getName()) != nullptr &&
+             MangledName.contains(llvm::FunctionSamples::UniqSuffix)) {
+    // Emit static C function that is mangled with
+    // -funique-internal-linkage-names.
+    addDeferredDeclToEmit(GD);
   } else {
     // Otherwise, remember that we saw a deferred decl with this name.  The
     // first use of the mangled name will cause it to move into
@@ -6189,6 +6214,21 @@ void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD,
                                                    /*DontDefer=*/true,
                                                    ForDefinition));
 
+  if (!getLangOpts().CPlusPlus &&
+      getCXXABI().getMangleContext().shouldMangleDeclName(D)) {
+    // -funique-internal-linkage-names may change the symbol name of C function.
+    // Replace all uses of old symbol with the emitted global value.
+    if (IdentifierInfo *II = D->getIdentifier()) {
+      if (II->getName() != GV->getName() &&
+          GV->getName().contains(llvm::FunctionSamples::UniqSuffix)) {
+        if (llvm::GlobalValue *GVDef =
+                getModule().getNamedValue(D->getName())) {
+          GVDef->replaceAllUsesWith(GV);
+        }
+      }
+    }
+  }
+
   // Already emitted.
   if (!GV->isDeclaration())
     return;
diff --git a/clang/test/CodeGen/unique-internal-linkage-names-alias.c b/clang/test/CodeGen/unique-internal-linkage-names-alias.c
new file mode 100644
index 0000000000000..14bfea08367d3
--- /dev/null
+++ b/clang/test/CodeGen/unique-internal-linkage-names-alias.c
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 %s -emit-llvm -funique-internal-linkage-names -o - | FileCheck %s
+
+struct A;
+static long foo(const struct A*p);
+
+long bar(const struct A*p);
+long bar(const struct A*p) __attribute__((__alias__("foo")));
+
+// CHECK: define internal i64 @_ZL3fooPK1A.__uniq.[[ATTR:[0-9]+]](ptr noundef %p) #1 {
+static long foo(const struct A*p) {return 1;}

>From aedff8d00362405ad73880eccf4bd7006f6facb2 Mon Sep 17 00:00:00 2001
From: h2h <h2h at meta.com>
Date: Wed, 25 Jun 2025 16:22:49 -0700
Subject: [PATCH 2/9] Check Itanium mangling only

---
 clang/test/CodeGen/unique-internal-linkage-names-alias.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/CodeGen/unique-internal-linkage-names-alias.c b/clang/test/CodeGen/unique-internal-linkage-names-alias.c
index 14bfea08367d3..85345233ad507 100644
--- a/clang/test/CodeGen/unique-internal-linkage-names-alias.c
+++ b/clang/test/CodeGen/unique-internal-linkage-names-alias.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -emit-llvm -funique-internal-linkage-names -o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux %s -emit-llvm -funique-internal-linkage-names -o - | FileCheck %s
 
 struct A;
 static long foo(const struct A*p);

>From 80e7bda91ae783ce02e8c3491a88e65779a1e01c Mon Sep 17 00:00:00 2001
From: h2h <h2h at meta.com>
Date: Thu, 3 Jul 2025 13:33:44 -0700
Subject: [PATCH 3/9] Fix frontend crash with extern_weak when using
 __attribute__((weak, alias()))

---
 clang/lib/CodeGen/CodeGenModule.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 90f02220ec306..83515a4488256 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -4066,7 +4066,6 @@ void CodeGenModule::EmitGlobal(GlobalDecl GD) {
     assert(!MayBeEmittedEagerly(Global));
     addDeferredDeclToEmit(GD);
   } else if (!getLangOpts().CPlusPlus && ND &&
-             GetGlobalValue(ND->getName()) != nullptr &&
              MangledName.contains(llvm::FunctionSamples::UniqSuffix)) {
     // Emit static C function that is mangled with
     // -funique-internal-linkage-names.

>From 1e116a9b6a7964a78ae1b795bc2ac80ec9f46be1 Mon Sep 17 00:00:00 2001
From: h2h <h2h at meta.com>
Date: Sun, 6 Jul 2025 13:17:35 -0700
Subject: [PATCH 4/9] Fix missed aliasee function emit when function alias is
 used after the definitation of the aliasee function

---
 clang/lib/CodeGen/CodeGenModule.cpp           | 33 +++++++++++++++++--
 clang/lib/CodeGen/CodeGenModule.h             |  1 +
 .../unique-internal-linkage-names-alias.c     |  2 +-
 3 files changed, 32 insertions(+), 4 deletions(-)

diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 83515a4488256..1c9c9c04b6d4a 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -3902,6 +3902,22 @@ bool CodeGenModule::shouldEmitCUDAGlobalVar(const VarDecl *Global) const {
          Global->getType()->isCUDADeviceBuiltinTextureType();
 }
 
+bool CodeGenModule::shouldEmitUniqLinkageName(GlobalDecl GD) {
+  const auto *ND = dyn_cast<FunctionDecl>(GD.getDecl());
+  if (!ND || !getCXXABI().getMangleContext().shouldMangleDeclName(ND))
+    return false;
+  StringRef MangledName = getMangledName(GD);
+  if (!MangledName.contains(llvm::FunctionSamples::UniqSuffix))
+    return false;
+  for (const GlobalDecl &AD : Aliases) {
+    const auto *D = cast<ValueDecl>(AD.getDecl());
+    const AliasAttr *AA = D->getAttr<AliasAttr>();
+    if (AA && AA->getAliasee() == ND->getName())
+      return true;
+  }
+  return false;
+}
+
 void CodeGenModule::EmitGlobal(GlobalDecl GD) {
   const auto *Global = cast<ValueDecl>(GD.getDecl());
 
@@ -4056,7 +4072,6 @@ void CodeGenModule::EmitGlobal(GlobalDecl GD) {
     CXXGlobalInits.push_back(nullptr);
   }
 
-  const auto *ND = dyn_cast<NamedDecl>(GD.getDecl());
   StringRef MangledName = getMangledName(GD);
   if (GetGlobalValue(MangledName) != nullptr) {
     // The value has already been used and should therefore be emitted.
@@ -4065,8 +4080,7 @@ void CodeGenModule::EmitGlobal(GlobalDecl GD) {
     // The value must be emitted, but cannot be emitted eagerly.
     assert(!MayBeEmittedEagerly(Global));
     addDeferredDeclToEmit(GD);
-  } else if (!getLangOpts().CPlusPlus && ND &&
-             MangledName.contains(llvm::FunctionSamples::UniqSuffix)) {
+  } else if (!getLangOpts().CPlusPlus && shouldEmitUniqLinkageName(GD)) {
     // Emit static C function that is mangled with
     // -funique-internal-linkage-names.
     addDeferredDeclToEmit(GD);
@@ -6223,6 +6237,7 @@ void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD,
         if (llvm::GlobalValue *GVDef =
                 getModule().getNamedValue(D->getName())) {
           GVDef->replaceAllUsesWith(GV);
+          GVDef->removeFromParent();
         }
       }
     }
@@ -6287,6 +6302,18 @@ void CodeGenModule::EmitAliasDefinition(GlobalDecl GD) {
     return;
   }
 
+  // Deferred emit for aliased C function when __attribute__((alias)) might be
+  // used after the definition of aliasee function
+  if (!getLangOpts().CPlusPlus) {
+    for (const auto &[Name, Decl] : DeferredDecls) {
+      const auto *FD = dyn_cast<FunctionDecl>(Decl.getDecl());
+      if (FD && FD->getName() == AA->getAliasee() &&
+          Name.contains(llvm::FunctionSamples::UniqSuffix)) {
+        addDeferredDeclToEmit(Decl);
+      }
+    }
+  }
+
   // If there is a definition in the module, then it wins over the alias.
   // This is dubious, but allow it to be safe.  Just ignore the alias.
   llvm::GlobalValue *Entry = GetGlobalValue(MangledName);
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index 1b67d4354efc0..e7d3f0276566c 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -613,6 +613,7 @@ class CodeGenModule : public CodeGenTypeCache {
   // related attributes.
   bool shouldEmitCUDAGlobalVar(const VarDecl *VD) const;
   bool shouldOpportunisticallyEmitVTables();
+  bool shouldEmitUniqLinkageName(GlobalDecl GD);
   /// Map used to be sure we don't emit the same CompoundLiteral twice.
   llvm::DenseMap<const CompoundLiteralExpr *, llvm::GlobalVariable *>
       EmittedCompoundLiterals;
diff --git a/clang/test/CodeGen/unique-internal-linkage-names-alias.c b/clang/test/CodeGen/unique-internal-linkage-names-alias.c
index 85345233ad507..e9cedc9dd8957 100644
--- a/clang/test/CodeGen/unique-internal-linkage-names-alias.c
+++ b/clang/test/CodeGen/unique-internal-linkage-names-alias.c
@@ -6,5 +6,5 @@ static long foo(const struct A*p);
 long bar(const struct A*p);
 long bar(const struct A*p) __attribute__((__alias__("foo")));
 
-// CHECK: define internal i64 @_ZL3fooPK1A.__uniq.[[ATTR:[0-9]+]](ptr noundef %p) #1 {
+// CHECK: define internal i64 @_ZL3fooPK1A.__uniq.[[ATTR:[0-9]+]](ptr noundef %p) #0 {
 static long foo(const struct A*p) {return 1;}

>From 3beeb6a17ba5e3e1cc8be455505633aec89e9d94 Mon Sep 17 00:00:00 2001
From: h2h <h2h at meta.com>
Date: Mon, 7 Jul 2025 13:50:33 -0700
Subject: [PATCH 5/9] Make the original function GlobalAlias to avoid broken
 references in the inline assembly

---
 clang/lib/CodeGen/CGCall.cpp        | 18 +++++++++++++++
 clang/lib/CodeGen/CodeGenModule.cpp | 35 +++++++++++++++++++++++++++--
 2 files changed, 51 insertions(+), 2 deletions(-)

diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index fd75de42515da..e44d9d3c6691c 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -5741,6 +5741,24 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
     IRFuncTy = OrigFn->getFunctionType();
   }
 
+  // Propogate Error Attribute if Callee is declared within the function body
+  // e.g.
+  // void foo() {
+  //   __extern__ void nocall(void) __attribute__((__error__(msg)));
+  //   if (nobranch)
+  //     nocall();
+  // }
+  if (CalleePtr && CalleeDecl) {
+    if (const auto *EA = CalleeDecl->getAttr<ErrorAttr>()) {
+      if (EA->isError())
+        dyn_cast<llvm::Function>(CalleePtr)->addFnAttr("dontcall-error",
+                                                       EA->getUserDiagnostic());
+      else if (EA->isWarning())
+        dyn_cast<llvm::Function>(CalleePtr)->addFnAttr("dontcall-warn",
+                                                       EA->getUserDiagnostic());
+    }
+  }
+
   // 3. Perform the actual call.
 
   // Deactivate any cleanups that we're supposed to do immediately before
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 1c9c9c04b6d4a..ae4273efcb4da 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -6227,6 +6227,7 @@ void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD,
                                                    /*DontDefer=*/true,
                                                    ForDefinition));
 
+  llvm::GlobalAlias *GA = nullptr;
   if (!getLangOpts().CPlusPlus &&
       getCXXABI().getMangleContext().shouldMangleDeclName(D)) {
     // -funique-internal-linkage-names may change the symbol name of C function.
@@ -6235,9 +6236,17 @@ void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD,
       if (II->getName() != GV->getName() &&
           GV->getName().contains(llvm::FunctionSamples::UniqSuffix)) {
         if (llvm::GlobalValue *GVDef =
-                getModule().getNamedValue(D->getName())) {
+                getModule().getNamedValue(II->getName())) {
           GVDef->replaceAllUsesWith(GV);
-          GVDef->removeFromParent();
+          GVDef->eraseFromParent();
+        } else if (!D->hasAttr<AlwaysInlineAttr>() &&
+                   !D->hasAttr<GNUInlineAttr>()) {
+          // Create a GlobalAlias to the original symbol in case it was
+          // referenced in the inline assembly
+          unsigned AS = GV->getType()->getPointerAddressSpace();
+          GA = llvm::GlobalAlias::create(GV->getValueType(), AS,
+                                         llvm::GlobalValue::InternalLinkage,
+                                         II->getName(), GV, &getModule());
         }
       }
     }
@@ -6282,6 +6291,28 @@ void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD,
 
   SetLLVMFunctionAttributesForDefinition(D, Fn);
 
+  // Avoid extra uses of the internal GlobalAlias if Callee has
+  // __attribute__((error)).
+  for (auto BB = Fn->begin(); GA && BB != Fn->end(); BB++) {
+    for (auto &I : *BB) {
+      if (auto *CI = dyn_cast<llvm::CallInst>(&I)) {
+        if (auto *Callee = CI->getCalledFunction()) {
+          if (Callee->hasFnAttribute("dontcall-error")) {
+            GA->eraseFromParent();
+            GA = nullptr;
+            break;
+          }
+        }
+      }
+    }
+  }
+
+  // Set Attributes to perserve the internal GlobalAlias
+  if (GA) {
+    SetCommonAttributes(GD, GA);
+    addUsedOrCompilerUsedGlobal(GA);
+  }
+
   if (const ConstructorAttr *CA = D->getAttr<ConstructorAttr>())
     AddGlobalCtor(Fn, CA->getPriority());
   if (const DestructorAttr *DA = D->getAttr<DestructorAttr>())

>From 0003a3f53bbaad91b1222f3549bf020ff0087772 Mon Sep 17 00:00:00 2001
From: h2h <h2h at meta.com>
Date: Tue, 8 Jul 2025 02:39:13 -0700
Subject: [PATCH 6/9] Create GlobalAlias for AlwaysInline internal functions

---
 clang/lib/CodeGen/CodeGenModule.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index ae4273efcb4da..e7258197936ed 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -6239,8 +6239,7 @@ void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD,
                 getModule().getNamedValue(II->getName())) {
           GVDef->replaceAllUsesWith(GV);
           GVDef->eraseFromParent();
-        } else if (!D->hasAttr<AlwaysInlineAttr>() &&
-                   !D->hasAttr<GNUInlineAttr>()) {
+        } else if (!D->hasAttr<GNUInlineAttr>()) {
           // Create a GlobalAlias to the original symbol in case it was
           // referenced in the inline assembly
           unsigned AS = GV->getType()->getPointerAddressSpace();

>From cab9bf6dde61ee8f337f33aac47fbd14ae33ce8f Mon Sep 17 00:00:00 2001
From: h2h <h2h at meta.com>
Date: Tue, 8 Jul 2025 21:43:17 -0700
Subject: [PATCH 7/9] Check static __always_inline/inline functions to avoid
 inline assembly breakages

---
 clang/lib/CodeGen/CodeGenModule.cpp | 68 ++++++++++++++++++++++++++---
 clang/lib/CodeGen/CodeGenModule.h   | 12 +++++
 2 files changed, 75 insertions(+), 5 deletions(-)

diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index e7258197936ed..47d4603bcc6d7 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -775,6 +775,7 @@ void CodeGenModule::clear() {
   DeferredAnnotations.clear();
   if (OpenMPRuntime)
     OpenMPRuntime->clear();
+  DeferredMaybeInlineFunctions.clear();
 }
 
 void InstrProfStats::reportDiagnostics(DiagnosticsEngine &Diags,
@@ -940,6 +941,7 @@ void CodeGenModule::Release() {
   emitAtAvailableLinkGuard();
   if (Context.getTargetInfo().getTriple().isWasm())
     EmitMainVoidAlias();
+  FixupMaybeInlineFunctions();
 
   if (getTriple().isAMDGPU() ||
       (getTriple().isSPIRV() && getTriple().getVendor() == llvm::Triple::AMD)) {
@@ -6213,6 +6215,10 @@ void CodeGenModule::HandleCXXStaticMemberVarInstantiation(VarDecl *VD) {
   EmitTopLevelDecl(VD);
 }
 
+static bool hasInlineAttr(const FunctionDecl *Decl) {
+  return Decl->hasAttr<AlwaysInlineAttr>() || Decl->hasAttr<GNUInlineAttr>();
+}
+
 void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD,
                                                  llvm::GlobalValue *GV) {
   const auto *D = cast<FunctionDecl>(GD.getDecl());
@@ -6239,7 +6245,7 @@ void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD,
                 getModule().getNamedValue(II->getName())) {
           GVDef->replaceAllUsesWith(GV);
           GVDef->eraseFromParent();
-        } else if (!D->hasAttr<GNUInlineAttr>()) {
+        } else {
           // Create a GlobalAlias to the original symbol in case it was
           // referenced in the inline assembly
           unsigned AS = GV->getType()->getPointerAddressSpace();
@@ -6294,22 +6300,43 @@ void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD,
   // __attribute__((error)).
   for (auto BB = Fn->begin(); GA && BB != Fn->end(); BB++) {
     for (auto &I : *BB) {
-      if (auto *CI = dyn_cast<llvm::CallInst>(&I)) {
+      bool shouldEraseAlias = false;
+      if (auto *CI = dyn_cast<llvm::CallBase>(&I)) {
         if (auto *Callee = CI->getCalledFunction()) {
-          if (Callee->hasFnAttribute("dontcall-error")) {
-            GA->eraseFromParent();
+          if (MustInlinedFunctions.contains(Callee->getName())) {
+            // Callee is a known always inline, inline assembly
+            if (hasInlineAttr(D))
+              MustInlinedFunctions.insert(Fn->getName());
+            shouldEraseAlias = true;
+          } else if (Callee->hasFnAttribute("dontcall-error")) {
+            // Callee has Error Attribute
+            shouldEraseAlias = true;
+          } else if (Callee->isDeclaration() && !Callee->isIntrinsic() &&
+                     hasInlineAttr(D)) {
+            // Callee has not emitted. Defer this check to a later stage
+            DeferredMaybeInlineFunctions[GD] = Callee;
             GA = nullptr;
             break;
           }
+        } else if (CI->isInlineAsm() && hasInlineAttr(D)) {
+          // Avoid alias towards always inline assembly to allow inlining
+          MustInlinedFunctions.insert(Fn->getName());
+          shouldEraseAlias = true;
         }
       }
+      if (shouldEraseAlias) {
+        GA->eraseFromParent();
+        GA = nullptr;
+        break;
+      }
     }
   }
 
-  // Set Attributes to perserve the internal GlobalAlias
+  // Set Attributes to perserve the internal GlobalValues
   if (GA) {
     SetCommonAttributes(GD, GA);
     addUsedOrCompilerUsedGlobal(GA);
+    addUsedOrCompilerUsedGlobal(GV);
   }
 
   if (const ConstructorAttr *CA = D->getAttr<ConstructorAttr>())
@@ -7592,6 +7619,37 @@ void CodeGenModule::EmitMainVoidAlias() {
   }
 }
 
+void CodeGenModule::FixupMaybeInlineFunctions() {
+  // Check if GlobalAlias need to be removed
+  unsigned long sz = 0;
+  while (sz != DeferredMaybeInlineFunctions.size()) {
+    sz = DeferredMaybeInlineFunctions.size();
+    for (auto I = DeferredMaybeInlineFunctions.begin(); I != DeferredMaybeInlineFunctions.end();) {
+      const auto *D = cast<FunctionDecl>(I->first.getDecl());
+      auto *GA = GetGlobalValue(D->getName());
+      StringRef MangledName = getMangledName(I->first);
+      if (GA && MustInlinedFunctions.contains(I->second->getName())) {
+        MustInlinedFunctions.insert(MangledName);
+        GA->eraseFromParent();
+        I = DeferredMaybeInlineFunctions.erase(I);
+      } else
+        I++;
+    }
+  }
+  // Fixup attributes
+  for (auto &[Decl, Callee] : DeferredMaybeInlineFunctions) {
+    const auto *D = cast<FunctionDecl>(Decl.getDecl());
+    auto *GA = GetGlobalValue(D->getName());
+    StringRef MangledName = getMangledName(Decl);
+    auto *GV = GetGlobalValue(MangledName);
+    if (!GA || !GV)
+      continue;
+    SetCommonAttributes(Decl, GA);
+    addUsedOrCompilerUsedGlobal(GA);
+    addUsedOrCompilerUsedGlobal(GV);
+  }
+}
+
 /// Turns the given pointer into a constant.
 static llvm::Constant *GetPointerConstant(llvm::LLVMContext &Context,
                                           const void *Ptr) {
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index e7d3f0276566c..bb05bdb8ca538 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -464,6 +464,14 @@ class CodeGenModule : public CodeGenTypeCache {
   std::vector<llvm::WeakTrackingVH> LLVMUsed;
   std::vector<llvm::WeakTrackingVH> LLVMCompilerUsed;
 
+  /// Set of function names that must be inlined. e.g.
+  ///    __always_inline void foo() { asm (...); }
+  llvm::DenseSet<StringRef> MustInlinedFunctions;
+
+  /// Deferred always inline functions that has a generated GlobalAlias due to
+  /// -funique-internal-linkage-names
+  llvm::MapVector<GlobalDecl, llvm::Function *> DeferredMaybeInlineFunctions;
+
   /// Store the list of global constructors and their respective priorities to
   /// be emitted when the translation unit is complete.
   CtorList GlobalCtors;
@@ -1257,6 +1265,10 @@ class CodeGenModule : public CodeGenTypeCache {
   /// Emit an alias for "main" if it has no arguments (needed for wasm).
   void EmitMainVoidAlias();
 
+  /// Fixup attributes or remove GlobalAlias for always inline functions due to
+  /// -funique-internal-linkage-names
+  void FixupMaybeInlineFunctions();
+
   /// Tell the consumer that this variable has been instantiated.
   void HandleCXXStaticMemberVarInstantiation(VarDecl *VD);
 

>From 18e9543d6127a02523368406f931d76f464efdc2 Mon Sep 17 00:00:00 2001
From: h2h <h2h at meta.com>
Date: Wed, 9 Jul 2025 22:13:38 -0700
Subject: [PATCH 8/9] Fixup deferred inline function check and inline assembly
 inlining

---
 clang/lib/CodeGen/CodeGenModule.cpp | 29 ++++++++++++++++++++++++-----
 clang/lib/CodeGen/CodeGenModule.h   |  1 +
 2 files changed, 25 insertions(+), 5 deletions(-)

diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 47d4603bcc6d7..6ae06475f3a09 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -6297,16 +6297,15 @@ void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD,
   SetLLVMFunctionAttributesForDefinition(D, Fn);
 
   // Avoid extra uses of the internal GlobalAlias if Callee has
-  // __attribute__((error)).
+  // __attribute__((error)) or inline assembly.
   for (auto BB = Fn->begin(); GA && BB != Fn->end(); BB++) {
     for (auto &I : *BB) {
       bool shouldEraseAlias = false;
       if (auto *CI = dyn_cast<llvm::CallBase>(&I)) {
         if (auto *Callee = CI->getCalledFunction()) {
+          GlobalDecl CalleeDecl;
           if (MustInlinedFunctions.contains(Callee->getName())) {
             // Callee is a known always inline, inline assembly
-            if (hasInlineAttr(D))
-              MustInlinedFunctions.insert(Fn->getName());
             shouldEraseAlias = true;
           } else if (Callee->hasFnAttribute("dontcall-error")) {
             // Callee has Error Attribute
@@ -6317,14 +6316,23 @@ void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD,
             DeferredMaybeInlineFunctions[GD] = Callee;
             GA = nullptr;
             break;
+          } else if (lookupRepresentativeDecl(Callee->getName(), CalleeDecl)) {
+            // Defer if Callee is also deferred
+            if (DeferredMaybeInlineFunctions.contains(CalleeDecl)) {
+              DeferredMaybeInlineFunctions[GD] = Callee;
+              GA = nullptr;
+              break;
+            }
           }
         } else if (CI->isInlineAsm() && hasInlineAttr(D)) {
           // Avoid alias towards always inline assembly to allow inlining
-          MustInlinedFunctions.insert(Fn->getName());
           shouldEraseAlias = true;
+          RenamedAsmInlineFunctions[Fn] = D->getName();
         }
       }
       if (shouldEraseAlias) {
+        if (hasInlineAttr(D))
+          MustInlinedFunctions.insert(Fn->getName());
         GA->eraseFromParent();
         GA = nullptr;
         break;
@@ -7624,7 +7632,8 @@ void CodeGenModule::FixupMaybeInlineFunctions() {
   unsigned long sz = 0;
   while (sz != DeferredMaybeInlineFunctions.size()) {
     sz = DeferredMaybeInlineFunctions.size();
-    for (auto I = DeferredMaybeInlineFunctions.begin(); I != DeferredMaybeInlineFunctions.end();) {
+    for (auto I = DeferredMaybeInlineFunctions.begin();
+         I != DeferredMaybeInlineFunctions.end();) {
       const auto *D = cast<FunctionDecl>(I->first.getDecl());
       auto *GA = GetGlobalValue(D->getName());
       StringRef MangledName = getMangledName(I->first);
@@ -7648,6 +7657,16 @@ void CodeGenModule::FixupMaybeInlineFunctions() {
     addUsedOrCompilerUsedGlobal(GA);
     addUsedOrCompilerUsedGlobal(GV);
   }
+
+  // Revert unique internal linkage name for all C inline functions that has
+  // inline assembly. This is a workaround to Linux static_call, because the
+  // original symbol name is used in an assembly trampoline. A more proper way
+  // here is to parse the inline assembly and figure out what kind of assembly
+  // constraints must inline (e.g. "i" through constant folding), but this might
+  // be difficult in the frontend..
+  for (auto &[Fn, Name] : RenamedAsmInlineFunctions) {
+    Fn->setName(Name);
+  }
 }
 
 /// Turns the given pointer into a constant.
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index bb05bdb8ca538..1729f7f57f8b6 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -471,6 +471,7 @@ class CodeGenModule : public CodeGenTypeCache {
   /// Deferred always inline functions that has a generated GlobalAlias due to
   /// -funique-internal-linkage-names
   llvm::MapVector<GlobalDecl, llvm::Function *> DeferredMaybeInlineFunctions;
+  llvm::MapVector<llvm::Function *, StringRef> RenamedAsmInlineFunctions;
 
   /// Store the list of global constructors and their respective priorities to
   /// be emitted when the translation unit is complete.

>From a81a5c74b67b1199c505fb199f8c975c96eb2868 Mon Sep 17 00:00:00 2001
From: h2h <h2h at meta.com>
Date: Fri, 11 Jul 2025 16:58:15 -0700
Subject: [PATCH 9/9] Refactor && Fix a bug when traversing the callees

---
 clang/lib/CodeGen/CodeGenModule.cpp | 86 +++++++++++++++++++----------
 clang/lib/CodeGen/CodeGenModule.h   |  9 +--
 2 files changed, 61 insertions(+), 34 deletions(-)

diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 6ae06475f3a09..ca7b77f3d90da 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -6215,10 +6215,27 @@ void CodeGenModule::HandleCXXStaticMemberVarInstantiation(VarDecl *VD) {
   EmitTopLevelDecl(VD);
 }
 
+static llvm::DenseSet<llvm::CallBase *> collectCallSites(llvm::Function *Fn) {
+  llvm::DenseSet<llvm::CallBase *> CIs;
+  for (auto &BB : *Fn) {
+    for (auto &I : BB) {
+      if (auto *CI = dyn_cast<llvm::CallBase>(&I))
+        CIs.insert(CI);
+    }
+  }
+  return CIs;
+}
+
 static bool hasInlineAttr(const FunctionDecl *Decl) {
   return Decl->hasAttr<AlwaysInlineAttr>() || Decl->hasAttr<GNUInlineAttr>();
 }
 
+static bool isLinuxInitCall(const FunctionDecl *Decl) {
+  if (auto *Attr = Decl->getAttr<SectionAttr>())
+    return Attr->getName() == ".init.text";
+  return false;
+}
+
 void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD,
                                                  llvm::GlobalValue *GV) {
   const auto *D = cast<FunctionDecl>(GD.getDecl());
@@ -6245,6 +6262,8 @@ void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD,
                 getModule().getNamedValue(II->getName())) {
           GVDef->replaceAllUsesWith(GV);
           GVDef->eraseFromParent();
+        } else if (isLinuxInitCall(D)) {
+          RenamedInlineFunctions[cast<llvm::Function>(GV)] = D->getName();
         } else {
           // Create a GlobalAlias to the original symbol in case it was
           // referenced in the inline assembly
@@ -6298,45 +6317,50 @@ void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD,
 
   // Avoid extra uses of the internal GlobalAlias if Callee has
   // __attribute__((error)) or inline assembly.
-  for (auto BB = Fn->begin(); GA && BB != Fn->end(); BB++) {
-    for (auto &I : *BB) {
-      bool shouldEraseAlias = false;
-      if (auto *CI = dyn_cast<llvm::CallBase>(&I)) {
+  if (GA) {
+    bool shouldEraseAlias = false;
+    auto CallSites = collectCallSites(Fn);
+    for (auto &CI : CallSites) {
+      if (auto *Callee = CI->getCalledFunction()) {
+        // Callee is a known always inline that contains either inline assembly
+        // or __attribute__((error))
+        if (MustInlinedFunctions.contains(Callee->getName()) ||
+            Callee->hasFnAttribute("dontcall-error"))
+          shouldEraseAlias = true;
+      } else if (CI->isInlineAsm() && hasInlineAttr(D)) {
+        // Avoid alias towards always inline assembly to allow inlining
+        shouldEraseAlias = true;
+        RenamedInlineFunctions[Fn] = D->getName();
+      }
+    }
+
+    if (shouldEraseAlias) {
+      if (hasInlineAttr(D))
+        MustInlinedFunctions.insert(Fn->getName());
+      else
+        RenamedInlineFunctions[Fn] = D->getName();
+      GA->eraseFromParent();
+      GA = nullptr;
+    } else {
+      // Collect callee info if it hasn't been seen yet, and deferred the
+      // process later
+      for (auto &CI : CallSites) {
         if (auto *Callee = CI->getCalledFunction()) {
           GlobalDecl CalleeDecl;
-          if (MustInlinedFunctions.contains(Callee->getName())) {
-            // Callee is a known always inline, inline assembly
-            shouldEraseAlias = true;
-          } else if (Callee->hasFnAttribute("dontcall-error")) {
-            // Callee has Error Attribute
-            shouldEraseAlias = true;
-          } else if (Callee->isDeclaration() && !Callee->isIntrinsic() &&
-                     hasInlineAttr(D)) {
+          if (Callee->isDeclaration() && !Callee->isIntrinsic() &&
+              hasInlineAttr(D)) {
             // Callee has not emitted. Defer this check to a later stage
-            DeferredMaybeInlineFunctions[GD] = Callee;
+            DeferredMaybeInlineFunctions[GD].insert(Callee);
             GA = nullptr;
-            break;
           } else if (lookupRepresentativeDecl(Callee->getName(), CalleeDecl)) {
             // Defer if Callee is also deferred
             if (DeferredMaybeInlineFunctions.contains(CalleeDecl)) {
-              DeferredMaybeInlineFunctions[GD] = Callee;
+              DeferredMaybeInlineFunctions[GD].insert(Callee);
               GA = nullptr;
-              break;
             }
           }
-        } else if (CI->isInlineAsm() && hasInlineAttr(D)) {
-          // Avoid alias towards always inline assembly to allow inlining
-          shouldEraseAlias = true;
-          RenamedAsmInlineFunctions[Fn] = D->getName();
         }
       }
-      if (shouldEraseAlias) {
-        if (hasInlineAttr(D))
-          MustInlinedFunctions.insert(Fn->getName());
-        GA->eraseFromParent();
-        GA = nullptr;
-        break;
-      }
     }
   }
 
@@ -7637,7 +7661,9 @@ void CodeGenModule::FixupMaybeInlineFunctions() {
       const auto *D = cast<FunctionDecl>(I->first.getDecl());
       auto *GA = GetGlobalValue(D->getName());
       StringRef MangledName = getMangledName(I->first);
-      if (GA && MustInlinedFunctions.contains(I->second->getName())) {
+      if (GA && llvm::any_of(I->second, [this](auto *C) {
+            return this->MustInlinedFunctions.contains(C->getName());
+          })) {
         MustInlinedFunctions.insert(MangledName);
         GA->eraseFromParent();
         I = DeferredMaybeInlineFunctions.erase(I);
@@ -7646,7 +7672,7 @@ void CodeGenModule::FixupMaybeInlineFunctions() {
     }
   }
   // Fixup attributes
-  for (auto &[Decl, Callee] : DeferredMaybeInlineFunctions) {
+  for (auto &[Decl, _] : DeferredMaybeInlineFunctions) {
     const auto *D = cast<FunctionDecl>(Decl.getDecl());
     auto *GA = GetGlobalValue(D->getName());
     StringRef MangledName = getMangledName(Decl);
@@ -7664,7 +7690,7 @@ void CodeGenModule::FixupMaybeInlineFunctions() {
   // here is to parse the inline assembly and figure out what kind of assembly
   // constraints must inline (e.g. "i" through constant folding), but this might
   // be difficult in the frontend..
-  for (auto &[Fn, Name] : RenamedAsmInlineFunctions) {
+  for (auto &[Fn, Name] : RenamedInlineFunctions) {
     Fn->setName(Name);
   }
 }
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index 1729f7f57f8b6..fa77bd9856ddd 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -464,14 +464,15 @@ class CodeGenModule : public CodeGenTypeCache {
   std::vector<llvm::WeakTrackingVH> LLVMUsed;
   std::vector<llvm::WeakTrackingVH> LLVMCompilerUsed;
 
-  /// Set of function names that must be inlined. e.g.
-  ///    __always_inline void foo() { asm (...); }
+  /// Set of function names that must be inlined -- inline assembly and
+  /// __attribute__((error)). e.g. __always_inline void foo() { asm (...); }
   llvm::DenseSet<StringRef> MustInlinedFunctions;
 
   /// Deferred always inline functions that has a generated GlobalAlias due to
   /// -funique-internal-linkage-names
-  llvm::MapVector<GlobalDecl, llvm::Function *> DeferredMaybeInlineFunctions;
-  llvm::MapVector<llvm::Function *, StringRef> RenamedAsmInlineFunctions;
+  llvm::MapVector<GlobalDecl, llvm::DenseSet<llvm::Function *>>
+      DeferredMaybeInlineFunctions;
+  llvm::MapVector<llvm::Function *, StringRef> RenamedInlineFunctions;
 
   /// Store the list of global constructors and their respective priorities to
   /// be emitted when the translation unit is complete.



More information about the cfe-commits mailing list