[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