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 <[email protected]> Date: Tue, 20 Oct 2026 22:20:37 -0700 Subject: [PATCH 1/7] =?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 <[email protected]> Date: Tue, 20 Oct 2026 22:37:41 -0700 Subject: [PATCH 2/7] 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]] +// >From 41553adec6a7ad57ac9b6538938fb008d616b2e4 Mon Sep 17 00:00:00 2001 From: Vitaly Buka <[email protected]> Date: Tue, 20 Oct 2026 22:42:59 -0700 Subject: [PATCH 3/7] Rebase onto test regeneration Created using spr 2.0.0-beta.1 --- .../CodeGen/memcpy-inline-builtin-no-extern.c | 71 ++++++++++++++++++- 1 file changed, 68 insertions(+), 3 deletions(-) diff --git a/clang/test/CodeGen/memcpy-inline-builtin-no-extern.c b/clang/test/CodeGen/memcpy-inline-builtin-no-extern.c index f6edbbf6aeb44f..7ce4e16d793454 100644 --- a/clang/test/CodeGen/memcpy-inline-builtin-no-extern.c +++ b/clang/test/CodeGen/memcpy-inline-builtin-no-extern.c @@ -1,5 +1,5 @@ +// 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; @@ -10,7 +10,6 @@ AVAILABLE_EXTERNALLY int memcmp(const void *a, const void *b, size_t c) { return __builtin_memcmp(a, b, c); } -// CHECK: internal{{.*}}memcmp.inline int bar(const void *a, const void *b, size_t c) { return memcmp(a, b, c); } @@ -23,7 +22,73 @@ TRIPLE_INLINE void *memcpy(void *a, const void *b, size_t c) { return __builtin_memcpy(a, b, c); } -// CHECK: internal{{.*}}memcpy.inline void *foo(void *a, const void *b, size_t c) { return memcpy(a, b, c); } +// 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 nounwind +// CHECK-LABEL: define internal i32 @memcmp.inline( +// CHECK-SAME: ptr [[A:%.*]], ptr [[B:%.*]], i64 [[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 nounwind +// CHECK-LABEL: define internal ptr @memcpy.inline( +// CHECK-SAME: ptr [[A:%.*]], ptr [[B:%.*]], i64 [[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]] +// >From b94d59748f0f559f1c3567f25dacec6b6cc2cabd Mon Sep 17 00:00:00 2001 From: Vitaly Buka <[email protected]> Date: Tue, 20 Oct 2026 22:55:26 -0700 Subject: [PATCH 4/7] Split calling convention fix into separate PR Created using spr 2.0.0-beta.1 --- .../CodeGen/memcpy-inline-builtin-no-extern.c | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/clang/test/CodeGen/memcpy-inline-builtin-no-extern.c b/clang/test/CodeGen/memcpy-inline-builtin-no-extern.c index 7ce4e16d793454..a373346f5fcee7 100644 --- a/clang/test/CodeGen/memcpy-inline-builtin-no-extern.c +++ b/clang/test/CodeGen/memcpy-inline-builtin-no-extern.c @@ -25,6 +25,14 @@ TRIPLE_INLINE void *memcpy(void *a, const void *b, size_t c) { void *foo(void *a, const void *b, size_t c) { return memcpy(a, b, c); } + +AVAILABLE_EXTERNALLY __attribute__((preserve_most)) int umask(int m) { + return m; +} + +int call_umask(int m) { + return umask(m); +} // 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]+]] { @@ -92,3 +100,24 @@ void *foo(void *a, const void *b, size_t c) { // CHECK-NEXT: [[CALL:%.*]] = call ptr @memcpy.inline(ptr noundef [[TMP0]], ptr noundef [[TMP1]], i64 noundef [[TMP2]]) #[[ATTR4]] // CHECK-NEXT: ret ptr [[CALL]] // +// +// CHECK: Function Attrs: noinline nounwind optnone +// CHECK-LABEL: define dso_local i32 @call_umask( +// CHECK-SAME: i32 noundef [[M:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[M_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[M]], ptr [[M_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[M_ADDR]], align 4 +// CHECK-NEXT: [[CALL:%.*]] = call preserve_mostcc i32 @umask.inline(i32 noundef [[TMP0]]) #[[ATTR4]] +// CHECK-NEXT: ret i32 [[CALL]] +// +// +// CHECK: Function Attrs: alwaysinline nounwind +// CHECK-LABEL: define internal i32 @umask.inline( +// CHECK-SAME: i32 [[M:%.*]]) #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[M_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[M]], ptr [[M_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[M_ADDR]], align 4 +// CHECK-NEXT: ret i32 [[TMP0]] +// >From 55e650809abb5b99bb0c1da850391f3eb469085a Mon Sep 17 00:00:00 2001 From: Vitaly Buka <[email protected]> Date: Wed, 21 Oct 2026 15:41:47 -0700 Subject: [PATCH 5/7] split and address review comments Created using spr 2.0.0-beta.1 --- clang/test/CodeGen/memcmp-inline-builtin.c | 24 ++++ .../CodeGen/memcpy-inline-builtin-no-extern.c | 112 ++---------------- clang/test/CodeGen/strlen-inline-builtin.c | 25 ++++ clang/test/CodeGen/umask-inline-builtin.c | 20 ++++ 4 files changed, 76 insertions(+), 105 deletions(-) create mode 100644 clang/test/CodeGen/memcmp-inline-builtin.c create mode 100644 clang/test/CodeGen/strlen-inline-builtin.c create mode 100644 clang/test/CodeGen/umask-inline-builtin.c diff --git a/clang/test/CodeGen/memcmp-inline-builtin.c b/clang/test/CodeGen/memcmp-inline-builtin.c new file mode 100644 index 00000000000000..c018ce2ccceae6 --- /dev/null +++ b/clang/test/CodeGen/memcmp-inline-builtin.c @@ -0,0 +1,24 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --check-attributes --filter "call " --include-generated-funcs --version 6 +// RUN: %clang_cc1 -triple x86_64 -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s + +typedef unsigned long size_t; + +extern inline __attribute__((always_inline)) __attribute__((gnu_inline)) +int memcmp(const void *a, const void *b, size_t c) { + return __builtin_memcmp(a, b, c); +} + +int bar(const void *a, const void *b, size_t c) { + return memcmp(a, b, c); +} +// 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: [[CALL:%.*]] = call i32 @memcmp.inline(ptr noundef [[TMP0:%.*]], ptr noundef [[TMP1:%.*]], i64 noundef [[TMP2:%.*]]) +// +// +// CHECK: Function Attrs: alwaysinline nounwind +// CHECK-LABEL: define internal i32 @memcmp.inline( +// CHECK-SAME: ptr [[A:%.*]], ptr [[B:%.*]], i64 [[C:%.*]]) #[[ATTR2:[0-9]+]] { +// CHECK: [[CALL:%.*]] = call i32 @memcmp(ptr noundef [[TMP0:%.*]], ptr noundef [[TMP1:%.*]], i64 noundef [[TMP2:%.*]]) #[[ATTR3:[0-9]+]] +// diff --git a/clang/test/CodeGen/memcpy-inline-builtin-no-extern.c b/clang/test/CodeGen/memcpy-inline-builtin-no-extern.c index a373346f5fcee7..4cb0b5f06309c5 100644 --- a/clang/test/CodeGen/memcpy-inline-builtin-no-extern.c +++ b/clang/test/CodeGen/memcpy-inline-builtin-no-extern.c @@ -1,123 +1,25 @@ -// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --check-attributes --include-generated-funcs --version 6 +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --check-attributes --filter "call " --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; -#define AVAILABLE_EXTERNALLY extern inline __attribute__((always_inline)) __attribute__((gnu_inline)) -// Clang recognizes an inline builtin and renames it to memcmp.inline to prevent conflict with builtins. -AVAILABLE_EXTERNALLY int memcmp(const void *a, const void *b, size_t c) { - return __builtin_memcmp(a, b, c); -} - -int bar(const void *a, const void *b, size_t c) { - return memcmp(a, b, c); -} - // 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) { +inline __attribute__((always_inline)) __attribute__((gnu_inline)) +void *memcpy(void *a, const void *b, size_t c) { return __builtin_memcpy(a, b, c); } void *foo(void *a, const void *b, size_t c) { return memcpy(a, b, c); } - -AVAILABLE_EXTERNALLY __attribute__((preserve_most)) int umask(int m) { - return m; -} - -int call_umask(int m) { - return umask(m); -} -// 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 nounwind -// CHECK-LABEL: define internal i32 @memcmp.inline( -// CHECK-SAME: ptr [[A:%.*]], ptr [[B:%.*]], i64 [[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 nounwind // CHECK-LABEL: define internal ptr @memcpy.inline( -// CHECK-SAME: ptr [[A:%.*]], ptr [[B:%.*]], i64 [[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-SAME: ptr [[A:%.*]], ptr [[B:%.*]], i64 [[C:%.*]]) #[[ATTR1:[0-9]+]] { +// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[TMP0:%.*]], ptr align 1 [[TMP1:%.*]], i64 [[TMP2:%.*]], i1 false) // // // 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]] -// -// -// CHECK: Function Attrs: noinline nounwind optnone -// CHECK-LABEL: define dso_local i32 @call_umask( -// CHECK-SAME: i32 noundef [[M:%.*]]) #[[ATTR0]] { -// CHECK-NEXT: [[ENTRY:.*:]] -// CHECK-NEXT: [[M_ADDR:%.*]] = alloca i32, align 4 -// CHECK-NEXT: store i32 [[M]], ptr [[M_ADDR]], align 4 -// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[M_ADDR]], align 4 -// CHECK-NEXT: [[CALL:%.*]] = call preserve_mostcc i32 @umask.inline(i32 noundef [[TMP0]]) #[[ATTR4]] -// CHECK-NEXT: ret i32 [[CALL]] -// -// -// CHECK: Function Attrs: alwaysinline nounwind -// CHECK-LABEL: define internal i32 @umask.inline( -// CHECK-SAME: i32 [[M:%.*]]) #[[ATTR2]] { -// CHECK-NEXT: [[ENTRY:.*:]] -// CHECK-NEXT: [[M_ADDR:%.*]] = alloca i32, align 4 -// CHECK-NEXT: store i32 [[M]], ptr [[M_ADDR]], align 4 -// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[M_ADDR]], align 4 -// CHECK-NEXT: ret i32 [[TMP0]] +// CHECK-SAME: ptr noundef [[A:%.*]], ptr noundef [[B:%.*]], i64 noundef [[C:%.*]]) #[[ATTR3:[0-9]+]] { +// CHECK: [[CALL:%.*]] = call ptr @memcpy.inline(ptr noundef [[TMP0:%.*]], ptr noundef [[TMP1:%.*]], i64 noundef [[TMP2:%.*]]) #[[ATTR4:[0-9]+]] // diff --git a/clang/test/CodeGen/strlen-inline-builtin.c b/clang/test/CodeGen/strlen-inline-builtin.c new file mode 100644 index 00000000000000..f24f5adb6b490c --- /dev/null +++ b/clang/test/CodeGen/strlen-inline-builtin.c @@ -0,0 +1,25 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --check-attributes --filter "call " --include-generated-funcs --version 6 +// RUN: %clang_cc1 -triple x86_64 -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s + +typedef unsigned long size_t; + +size_t strlen(const char *s); +void *strlen_ptr = strlen; + +extern inline __attribute__((always_inline)) __attribute__((gnu_inline)) +__attribute__((section("my_sec"))) size_t strlen(const char *s) { + return 0; +} + +size_t call_strlen(const char *s) { + return strlen(s); +} +// CHECK: Function Attrs: noinline nounwind optnone +// CHECK-LABEL: define dso_local i64 @call_strlen( +// CHECK-SAME: ptr noundef [[S:%.*]]) #[[ATTR1:[0-9]+]] { +// CHECK: [[CALL:%.*]] = call i64 @strlen.inline(ptr noundef [[TMP0:%.*]]) #[[ATTR3:[0-9]+]] +// +// +// CHECK: Function Attrs: alwaysinline nounwind +// CHECK-LABEL: define internal i64 @strlen.inline( +// CHECK-SAME: ptr [[S:%.*]]) #[[ATTR2:[0-9]+]] { diff --git a/clang/test/CodeGen/umask-inline-builtin.c b/clang/test/CodeGen/umask-inline-builtin.c new file mode 100644 index 00000000000000..1b66b9dff1bdb2 --- /dev/null +++ b/clang/test/CodeGen/umask-inline-builtin.c @@ -0,0 +1,20 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --check-attributes --filter "call " --include-generated-funcs --version 6 +// RUN: %clang_cc1 -triple x86_64 -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s + +extern inline __attribute__((always_inline)) __attribute__((gnu_inline)) +__attribute__((preserve_most)) int umask(int m) { + return m; +} + +int call_umask(int m) { + return umask(m); +} +// CHECK: Function Attrs: noinline nounwind optnone +// CHECK-LABEL: define dso_local i32 @call_umask( +// CHECK-SAME: i32 noundef [[M:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK: [[CALL:%.*]] = call preserve_mostcc i32 @umask.inline(i32 noundef [[TMP0:%.*]]) #[[ATTR3:[0-9]+]] +// +// +// CHECK: Function Attrs: alwaysinline nounwind +// CHECK-LABEL: define internal i32 @umask.inline( +// CHECK-SAME: i32 [[M:%.*]]) #[[ATTR2:[0-9]+]] { >From 52c768b9e4e91d19736f7d9a195b68416ad701e8 Mon Sep 17 00:00:00 2001 From: Vitaly Buka <[email protected]> Date: Wed, 21 Oct 2026 15:42:35 -0700 Subject: [PATCH 6/7] split and address review comments Created using spr 2.0.0-beta.1 --- clang/lib/CodeGen/CodeGenFunction.cpp | 35 ---------------------- clang/lib/CodeGen/CodeGenModule.cpp | 35 +++++++++++++++++++++- clang/test/CodeGen/strlen-inline-builtin.c | 2 +- 3 files changed, 35 insertions(+), 37 deletions(-) diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index e5bca359e6a855..966268214c4653 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -1494,41 +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->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 372d975d62c2ea..918e5940f39a17 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -7065,7 +7065,40 @@ 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; + 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.c b/clang/test/CodeGen/strlen-inline-builtin.c index f24f5adb6b490c..68fe5217ec5db1 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 [[S:%.*]]) #[[ATTR2:[0-9]+]] { +// CHECK-SAME: ptr [[S:%.*]]) #[[ATTR2:[0-9]+]] section "my_sec" { >From b8715fe8cf912ef00fb3874bce31d99fd9d0ed2a Mon Sep 17 00:00:00 2001 From: Vitaly Buka <[email protected]> Date: Wed, 21 Oct 2026 16:31:42 -0700 Subject: [PATCH 7/7] reorder stack and squash fixups Created using spr 2.0.0-beta.1 --- .../CodeGen/strlen-inline-builtin-dllexport.c | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 clang/test/CodeGen/strlen-inline-builtin-dllexport.c diff --git a/clang/test/CodeGen/strlen-inline-builtin-dllexport.c b/clang/test/CodeGen/strlen-inline-builtin-dllexport.c new file mode 100644 index 00000000000000..059a43cdd16e16 --- /dev/null +++ b/clang/test/CodeGen/strlen-inline-builtin-dllexport.c @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -triple x86_64-windows-gnu -DDLLEXPORT='__attribute__((dllexport))' -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple powerpc64-ibm-aix -mdefault-visibility-export-mapping=all -DDLLEXPORT= -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s +// +// The `.inline` clone of an inline builtin has internal linkage, so it must +// not get a DLL storage class even if the declaration is dllexport. + +typedef __SIZE_TYPE__ size_t; + +size_t strlen(const char *s); + +extern inline __attribute__((always_inline)) __attribute__((gnu_inline)) +DLLEXPORT size_t strlen(const char *s) { + return 0; +} + +size_t call_strlen(const char *s) { + return strlen(s); +} + +// CHECK-LABEL: define {{.*}}i64 @call_strlen( +// CHECK: call i64 @strlen.inline( +// +// CHECK: declare {{.*}}i64 @strlen( +// +// CHECK-LABEL: define internal i64 @strlen.inline( _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
