[llvm-branch-commits] [clang] [clang][CodeGen] Copy function attributes to .inline builtin clones (PR #234081)

Vitaly Buka via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Tue Oct 20 22:56:36 PDT 2026


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

>From 37cdcd48c1f20e6011e8aada7cd31a296b6bced7 Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Tue, 20 Oct 2026 22:20:37 -0700
Subject: [PATCH 1/2] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20in?=
 =?UTF-8?q?itial=20version?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 2.0.0-beta.1
---
 clang/lib/CodeGen/CGExpr.cpp                  |  2 ++
 clang/lib/CodeGen/CodeGenFunction.cpp         |  8 ++++---
 .../CodeGen/memcpy-inline-builtin-no-extern.c | 22 ++++++++++++++-----
 3 files changed, 24 insertions(+), 8 deletions(-)

diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index f6810d84cd235f..86a8aa34318f8b 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -6723,6 +6723,8 @@ static CGCallee EmitDirectCallee(CodeGenFunction &CGF, GlobalDecl GD) {
         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);
       }
       return CGCallee::forDirect(Clone, GD);
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index e5bca359e6a855..51588a7ebc5567 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -1501,12 +1501,14 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn,
     std::string FDInlineName = (Fn->getName() + ".inline").str();
     llvm::Module *M = Fn->getParent();
     llvm::Function *Clone = M->getFunction(FDInlineName);
-    if (!Clone) {
+    if (!Clone)
       Clone = llvm::Function::Create(Fn->getFunctionType(),
                                      llvm::GlobalValue::InternalLinkage,
                                      Fn->getAddressSpace(), FDInlineName, M);
-      Clone->addFnAttr(llvm::Attribute::AlwaysInline);
-    }
+    Clone->setCallingConv(Fn->getCallingConv());
+    Clone->setAttributes(Fn->getAttributes());
+    CGM.SetLLVMFunctionAttributesForDefinition(FD, Clone);
+    Clone->addFnAttr(llvm::Attribute::AlwaysInline);
     Fn->setLinkage(llvm::GlobalValue::ExternalLinkage);
     Fn = Clone;
   } else {
diff --git a/clang/test/CodeGen/memcpy-inline-builtin-no-extern.c b/clang/test/CodeGen/memcpy-inline-builtin-no-extern.c
index f6edbbf6aeb44f..72da4803edf46b 100644
--- a/clang/test/CodeGen/memcpy-inline-builtin-no-extern.c
+++ b/clang/test/CodeGen/memcpy-inline-builtin-no-extern.c
@@ -1,6 +1,7 @@
-// RUN: %clang_cc1 -triple x86_64 -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64 -target-cpu x86-64 -tune-cpu generic -funwind-tables=2 -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s
 //
-// Verifies that clang-generated *.inline are flagged as internal.
+// Verifies that clang-generated *.inline are flagged as internal and preserve
+// function, return, and parameter attributes.
 
 typedef unsigned long size_t;
 
@@ -10,20 +11,31 @@ AVAILABLE_EXTERNALLY int memcmp(const void *a, const void *b, size_t c) {
   return __builtin_memcmp(a, b, c);
 }
 
-// CHECK: internal{{.*}}memcmp.inline
+// CHECK-LABEL: define dso_local i32 @bar(
+// CHECK:         call i32 @memcmp.inline(ptr noundef %{{.*}}, ptr noundef %{{.*}}, i64 noundef %{{.*}})
 int bar(const void *a, const void *b, size_t c) {
   return memcmp(a, b, c);
 }
 
+// CHECK: declare i32 @memcmp(ptr noundef, ptr noundef, i64 noundef) #[[ATTR_DECL:[0-9]+]]
+// CHECK: define internal i32 @memcmp.inline(ptr noundef %a, ptr noundef %b, i64 noundef %c) #[[ATTR_INLINE:[0-9]+]]
+
 // Note that extern has been omitted here.
 #define TRIPLE_INLINE inline __attribute__((always_inline)) __attribute__((gnu_inline))
 
 // Clang recognizes an inline builtin and renames it to memcpy.inline to prevent conflict with builtins.
-TRIPLE_INLINE void *memcpy(void *a, const void *b, size_t c) {
+TRIPLE_INLINE __attribute__((returns_nonnull)) void *memcpy(void *a, const void *b, size_t c) {
   return __builtin_memcpy(a, b, c);
 }
 
-// CHECK: internal{{.*}}memcpy.inline
+// CHECK: declare nonnull ptr @memcpy(ptr noundef, ptr noundef, i64 noundef) #[[ATTR_DECL]]
+// CHECK: define internal nonnull ptr @memcpy.inline(ptr noundef %a, ptr noundef %b, i64 noundef %c) #[[ATTR_INLINE]]
+
+// CHECK-LABEL: define dso_local ptr @foo(
+// CHECK:         call nonnull ptr @memcpy.inline(ptr noundef %{{.*}}, ptr noundef %{{.*}}, i64 noundef %{{.*}})
 void *foo(void *a, const void *b, size_t c) {
   return memcpy(a, b, c);
 }
+
+// CHECK: attributes #[[ATTR_DECL]] = { alwaysinline nobuiltin nounwind uwtable {{.*}}"target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+// CHECK: attributes #[[ATTR_INLINE]] = { alwaysinline nobuiltin nounwind uwtable "min-legal-vector-width"="0" {{.*}}"target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }

>From 3b83f1158db8a650f1dd038284fada1eca0f3ab8 Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Tue, 20 Oct 2026 22:37:41 -0700
Subject: [PATCH 2/2] Update test using update_cc_test_checks.py

Created using spr 2.0.0-beta.1
---
 .../CodeGen/memcpy-inline-builtin-no-extern.c | 89 +++++++++++++++----
 1 file changed, 71 insertions(+), 18 deletions(-)

diff --git a/clang/test/CodeGen/memcpy-inline-builtin-no-extern.c b/clang/test/CodeGen/memcpy-inline-builtin-no-extern.c
index 72da4803edf46b..937f485a56118d 100644
--- a/clang/test/CodeGen/memcpy-inline-builtin-no-extern.c
+++ b/clang/test/CodeGen/memcpy-inline-builtin-no-extern.c
@@ -1,7 +1,6 @@
-// RUN: %clang_cc1 -triple x86_64 -target-cpu x86-64 -tune-cpu generic -funwind-tables=2 -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s
-//
-// Verifies that clang-generated *.inline are flagged as internal and preserve
-// function, return, and parameter attributes.
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --check-attributes --include-generated-funcs --version 6
+// RUN: %clang_cc1 -triple x86_64 -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s
+// Verifies that clang-generated *.inline are flagged as internal.
 
 typedef unsigned long size_t;
 
@@ -11,31 +10,85 @@ AVAILABLE_EXTERNALLY int memcmp(const void *a, const void *b, size_t c) {
   return __builtin_memcmp(a, b, c);
 }
 
-// CHECK-LABEL: define dso_local i32 @bar(
-// CHECK:         call i32 @memcmp.inline(ptr noundef %{{.*}}, ptr noundef %{{.*}}, i64 noundef %{{.*}})
 int bar(const void *a, const void *b, size_t c) {
   return memcmp(a, b, c);
 }
 
-// CHECK: declare i32 @memcmp(ptr noundef, ptr noundef, i64 noundef) #[[ATTR_DECL:[0-9]+]]
-// CHECK: define internal i32 @memcmp.inline(ptr noundef %a, ptr noundef %b, i64 noundef %c) #[[ATTR_INLINE:[0-9]+]]
-
 // Note that extern has been omitted here.
 #define TRIPLE_INLINE inline __attribute__((always_inline)) __attribute__((gnu_inline))
 
 // Clang recognizes an inline builtin and renames it to memcpy.inline to prevent conflict with builtins.
-TRIPLE_INLINE __attribute__((returns_nonnull)) void *memcpy(void *a, const void *b, size_t c) {
+TRIPLE_INLINE void *memcpy(void *a, const void *b, size_t c) {
   return __builtin_memcpy(a, b, c);
 }
 
-// CHECK: declare nonnull ptr @memcpy(ptr noundef, ptr noundef, i64 noundef) #[[ATTR_DECL]]
-// CHECK: define internal nonnull ptr @memcpy.inline(ptr noundef %a, ptr noundef %b, i64 noundef %c) #[[ATTR_INLINE]]
-
-// CHECK-LABEL: define dso_local ptr @foo(
-// CHECK:         call nonnull ptr @memcpy.inline(ptr noundef %{{.*}}, ptr noundef %{{.*}}, i64 noundef %{{.*}})
 void *foo(void *a, const void *b, size_t c) {
   return memcpy(a, b, c);
 }
-
-// CHECK: attributes #[[ATTR_DECL]] = { alwaysinline nobuiltin nounwind uwtable {{.*}}"target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
-// CHECK: attributes #[[ATTR_INLINE]] = { alwaysinline nobuiltin nounwind uwtable "min-legal-vector-width"="0" {{.*}}"target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+// CHECK: Function Attrs: noinline nounwind optnone
+// CHECK-LABEL: define dso_local i32 @bar(
+// CHECK-SAME: ptr noundef [[A:%.*]], ptr noundef [[B:%.*]], i64 noundef [[C:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[B_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[C_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store ptr [[A]], ptr [[A_ADDR]], align 8
+// CHECK-NEXT:    store ptr [[B]], ptr [[B_ADDR]], align 8
+// CHECK-NEXT:    store i64 [[C]], ptr [[C_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[A_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[B_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[C_ADDR]], align 8
+// CHECK-NEXT:    [[CALL:%.*]] = call i32 @memcmp.inline(ptr noundef [[TMP0]], ptr noundef [[TMP1]], i64 noundef [[TMP2]])
+// CHECK-NEXT:    ret i32 [[CALL]]
+//
+//
+// CHECK: Function Attrs: alwaysinline nobuiltin nounwind
+// CHECK-LABEL: define internal i32 @memcmp.inline(
+// CHECK-SAME: ptr noundef [[A:%.*]], ptr noundef [[B:%.*]], i64 noundef [[C:%.*]]) #[[ATTR2:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[B_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[C_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store ptr [[A]], ptr [[A_ADDR]], align 8
+// CHECK-NEXT:    store ptr [[B]], ptr [[B_ADDR]], align 8
+// CHECK-NEXT:    store i64 [[C]], ptr [[C_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[A_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[B_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[C_ADDR]], align 8
+// CHECK-NEXT:    [[CALL:%.*]] = call i32 @memcmp(ptr noundef [[TMP0]], ptr noundef [[TMP1]], i64 noundef [[TMP2]]) #[[ATTR4:[0-9]+]]
+// CHECK-NEXT:    ret i32 [[CALL]]
+//
+//
+// CHECK: Function Attrs: alwaysinline nobuiltin nounwind
+// CHECK-LABEL: define internal ptr @memcpy.inline(
+// CHECK-SAME: ptr noundef [[A:%.*]], ptr noundef [[B:%.*]], i64 noundef [[C:%.*]]) #[[ATTR2]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[B_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[C_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store ptr [[A]], ptr [[A_ADDR]], align 8
+// CHECK-NEXT:    store ptr [[B]], ptr [[B_ADDR]], align 8
+// CHECK-NEXT:    store i64 [[C]], ptr [[C_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[A_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[B_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[C_ADDR]], align 8
+// CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[TMP0]], ptr align 1 [[TMP1]], i64 [[TMP2]], i1 false)
+// CHECK-NEXT:    ret ptr [[TMP0]]
+//
+//
+// CHECK: Function Attrs: noinline nounwind optnone
+// CHECK-LABEL: define dso_local ptr @foo(
+// CHECK-SAME: ptr noundef [[A:%.*]], ptr noundef [[B:%.*]], i64 noundef [[C:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[B_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[C_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store ptr [[A]], ptr [[A_ADDR]], align 8
+// CHECK-NEXT:    store ptr [[B]], ptr [[B_ADDR]], align 8
+// CHECK-NEXT:    store i64 [[C]], ptr [[C_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[A_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[B_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[C_ADDR]], align 8
+// CHECK-NEXT:    [[CALL:%.*]] = call ptr @memcpy.inline(ptr noundef [[TMP0]], ptr noundef [[TMP1]], i64 noundef [[TMP2]]) #[[ATTR4]]
+// CHECK-NEXT:    ret ptr [[CALL]]
+//



More information about the llvm-branch-commits mailing list