[llvm-branch-commits] [clang] [clang][CodeGen] Create .inline builtin clones in EmitGlobalFunctionDefinition (PR #234415)

Vitaly Buka via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Oct 22 19:47:24 PDT 2026


https://github.com/vitalybuka updated https://github.com/llvm/llvm-project/pull/234415

>From 74575a52b515ebd576262042cfa1d3d523fe5f7b Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Wed, 21 Oct 2026 16:33:50 -0700
Subject: [PATCH] reorder stack and squash fixups

Created using spr 2.0.0-beta.1
---
 clang/lib/CodeGen/CodeGenFunction.cpp         | 36 ----------------
 clang/lib/CodeGen/CodeGenModule.cpp           | 41 ++++++++++++++++++-
 .../strlen-inline-builtin-redefinition.c      | 35 ++++++++++++++++
 clang/test/CodeGen/strlen-inline-builtin.c    |  2 +-
 4 files changed, 75 insertions(+), 39 deletions(-)
 create mode 100644 clang/test/CodeGen/strlen-inline-builtin-redefinition.c

diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 8a9c0c3bc8c50..966268214c465 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -1494,42 +1494,6 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn,
 
   CGM.getTargetCodeGenInfo().checkFunctionABI(CGM, FD);
 
-  if (FD->isInlineBuiltinDeclaration()) {
-    // When generating code for a builtin with an inline declaration, use a
-    // mangled name to hold the actual body, while keeping an external
-    // definition in case the function pointer is referenced somewhere.
-    std::string FDInlineName = (Fn->getName() + ".inline").str();
-    llvm::Module *M = Fn->getParent();
-    llvm::Function *Clone = M->getFunction(FDInlineName);
-    if (!Clone)
-      Clone = llvm::Function::Create(Fn->getFunctionType(),
-                                     llvm::GlobalValue::InternalLinkage,
-                                     Fn->getAddressSpace(), FDInlineName, M);
-    Clone->setCallingConv(Fn->getCallingConv());
-    Clone->setAttributes(Fn->getAttributes());
-    Clone->addFnAttr(llvm::Attribute::AlwaysInline);
-    Fn->setLinkage(llvm::GlobalValue::ExternalLinkage);
-    Fn = Clone;
-  } else {
-    // Detect the unusual situation where an inline version is shadowed by a
-    // non-inline version. In that case we should pick the external one
-    // everywhere. That's GCC behavior too. Unfortunately, I cannot find a way
-    // to detect that situation before we reach codegen, so do some late
-    // replacement.
-    for (const FunctionDecl *PD = FD->getPreviousDecl(); PD;
-         PD = PD->getPreviousDecl()) {
-      if (LLVM_UNLIKELY(PD->isInlineBuiltinDeclaration())) {
-        std::string FDInlineName = (Fn->getName() + ".inline").str();
-        llvm::Module *M = Fn->getParent();
-        if (llvm::Function *Clone = M->getFunction(FDInlineName)) {
-          Clone->replaceAllUsesWith(Fn);
-          Clone->eraseFromParent();
-        }
-        break;
-      }
-    }
-  }
-
   // Check if we should generate debug info for this function.
   if (FD->hasAttr<NoDebugAttr>()) {
     // Clear non-distinct debug info that was possibly attached to the function
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 372d975d62c2e..0fb613df65a2a 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -2446,7 +2446,9 @@ void CodeGenModule::setDLLImportDLLExport(llvm::GlobalValue *GV,
 
 void CodeGenModule::setDLLImportDLLExport(llvm::GlobalValue *GV,
                                           const NamedDecl *D) const {
-  if (D && D->isExternallyVisible()) {
+  // Local-linkage globals (e.g. `.inline` builtin clones) cannot carry a DLL
+  // storage class.
+  if (D && D->isExternallyVisible() && !GV->hasLocalLinkage()) {
     if (D->hasAttr<DLLImportAttr>())
       GV->setDLLStorageClass(llvm::GlobalVariable::DLLImportStorageClass);
     else if ((D->hasAttr<DLLExportAttr>() ||
@@ -7065,7 +7067,42 @@ void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD,
   // want to propagate this information down (e.g. to local static
   // declarations).
   auto *Fn = cast<llvm::Function>(GV);
-  setFunctionLinkage(GD, Fn);
+  if (D->isInlineBuiltinDeclaration()) {
+    // When generating code for a builtin with an inline declaration, use a
+    // mangled name to hold the actual body, while keeping an external
+    // definition in case the function pointer is referenced somewhere.
+    std::string FDInlineName = (Fn->getName() + ".inline").str();
+    llvm::Function *Clone = getModule().getFunction(FDInlineName);
+    if (!Clone) {
+      Clone = llvm::Function::Create(
+          Fn->getFunctionType(), llvm::GlobalValue::InternalLinkage,
+          Fn->getAddressSpace(), FDInlineName, &getModule());
+      Clone->addFnAttr(llvm::Attribute::AlwaysInline);
+    }
+    if (!Clone->isDeclaration())
+      return;
+    Clone->setCallingConv(Fn->getCallingConv());
+    Clone->setAttributes(Fn->getAttributes());
+    Fn = Clone;
+  } else {
+    // Detect the unusual situation where an inline version is shadowed by a
+    // non-inline version. In that case we should pick the external one
+    // everywhere. That's GCC behavior too. Unfortunately, I cannot find a way
+    // to detect that situation before we reach codegen, so do some late
+    // replacement.
+    for (const FunctionDecl *PD = D->getPreviousDecl(); PD;
+         PD = PD->getPreviousDecl()) {
+      if (LLVM_UNLIKELY(PD->isInlineBuiltinDeclaration())) {
+        std::string FDInlineName = (Fn->getName() + ".inline").str();
+        if (llvm::Function *Clone = getModule().getFunction(FDInlineName)) {
+          Clone->replaceAllUsesWith(Fn);
+          Clone->eraseFromParent();
+        }
+        break;
+      }
+    }
+    setFunctionLinkage(GD, Fn);
+  }
 
   if (getTriple().isOSAIX() && D->isTargetClonesMultiVersion())
     Fn->setLinkage(llvm::GlobalValue::InternalLinkage);
diff --git a/clang/test/CodeGen/strlen-inline-builtin-redefinition.c b/clang/test/CodeGen/strlen-inline-builtin-redefinition.c
new file mode 100644
index 0000000000000..64489b1d78705
--- /dev/null
+++ b/clang/test/CodeGen/strlen-inline-builtin-redefinition.c
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -triple x86_64 -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s
+//
+// C accepts a second `extern inline gnu_inline` definition of a builtin. Both
+// definitions reach EmitGlobalFunctionDefinition when the builtin is used in
+// between; only the first body is emitted into the `.inline` clone.
+
+typedef unsigned long size_t;
+
+extern inline __attribute__((always_inline)) __attribute__((gnu_inline))
+size_t strlen(const char *s) {
+  return 1;
+}
+
+size_t call_strlen_1(const char *s) {
+  return strlen(s);
+}
+
+extern inline __attribute__((always_inline)) __attribute__((gnu_inline))
+size_t strlen(const char *s) {
+  return 2;
+}
+
+size_t call_strlen_2(const char *s) {
+  return strlen(s);
+}
+
+// CHECK-LABEL: define {{.*}}i64 @call_strlen_1(
+// CHECK: call i64 @strlen.inline(
+//
+// CHECK-LABEL: define internal i64 @strlen.inline(
+// CHECK: ret i64 1
+// CHECK-NOT: ret i64 2
+//
+// CHECK-LABEL: define {{.*}}i64 @call_strlen_2(
+// CHECK: call i64 @strlen.inline(
diff --git a/clang/test/CodeGen/strlen-inline-builtin.c b/clang/test/CodeGen/strlen-inline-builtin.c
index 903d691dd42ae..61bdcc6be8a62 100644
--- a/clang/test/CodeGen/strlen-inline-builtin.c
+++ b/clang/test/CodeGen/strlen-inline-builtin.c
@@ -22,4 +22,4 @@ size_t call_strlen(const char *s) {
 //
 // CHECK: Function Attrs: alwaysinline nounwind
 // CHECK-LABEL: define internal i64 @strlen.inline(
-// CHECK-SAME: ptr noundef [[S:%.*]]) #[[ATTR2:[0-9]+]] {
+// CHECK-SAME: ptr noundef [[S:%.*]]) #[[ATTR2:[0-9]+]] section "my_sec" {



More information about the llvm-branch-commits mailing list