https://github.com/qchateau updated https://github.com/llvm/llvm-project/pull/158424
>From a522a156087841170fd99c0f65d985bda525ae6f Mon Sep 17 00:00:00 2001 From: Quentin Chateau <[email protected]> Date: Sat, 13 Sep 2025 00:48:59 +0200 Subject: [PATCH] [clang] Add missing readonly/readnone annotations When arg memory effects are lost due to indirect arguments, apply readonly/readnone attribute to the other pointer arguments of the function. --- clang/lib/CodeGen/CGCall.cpp | 36 +++++++++++++++++++++++++++++ clang/test/CodeGen/struct-passing.c | 22 ++++++++++++------ 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 0b2fce4244fb6..13a4a53744880 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -2438,7 +2438,10 @@ void CodeGenModule::ConstructAttributeList(StringRef Name, // Some ABIs may result in additional accesses to arguments that may // otherwise not be present. + std::optional<llvm::Attribute::AttrKind> MemAttrForPtrArgs; + bool AddedPotentialArgAccess = false; auto AddPotentialArgAccess = [&]() { + AddedPotentialArgAccess = true; llvm::Attribute A = FuncAttrs.getAttribute(llvm::Attribute::Memory); if (A.isValid()) FuncAttrs.addMemoryAttr(A.getMemoryEffects() | @@ -2499,11 +2502,13 @@ void CodeGenModule::ConstructAttributeList(StringRef Name, // gcc specifies that 'const' functions have greater restrictions than // 'pure' functions, so they also cannot have infinite loops. FuncAttrs.addAttribute(llvm::Attribute::WillReturn); + MemAttrForPtrArgs = llvm::Attribute::ReadNone; } else if (TargetDecl->hasAttr<PureAttr>()) { FuncAttrs.addMemoryAttr(llvm::MemoryEffects::readOnly()); FuncAttrs.addAttribute(llvm::Attribute::NoUnwind); // gcc specifies that 'pure' functions cannot have infinite loops. FuncAttrs.addAttribute(llvm::Attribute::WillReturn); + MemAttrForPtrArgs = llvm::Attribute::ReadOnly; } else if (TargetDecl->hasAttr<NoAliasAttr>()) { FuncAttrs.addMemoryAttr(llvm::MemoryEffects::inaccessibleOrArgMemOnly()); FuncAttrs.addAttribute(llvm::Attribute::NoUnwind); @@ -3011,6 +3016,37 @@ void CodeGenModule::ConstructAttributeList(StringRef Name, } assert(ArgNo == FI.arg_size()); + ArgNo = 0; + if (AddedPotentialArgAccess && MemAttrForPtrArgs) { + llvm::SmallSet<unsigned, 8> ArgsToSkip; + if (IRFunctionArgs.hasSRetArg()) { + ArgsToSkip.insert(IRFunctionArgs.getSRetArgNo()); + } + if (IRFunctionArgs.hasInallocaArg()) { + ArgsToSkip.insert(IRFunctionArgs.getInallocaArgNo()); + } + + for (CGFunctionInfo::const_arg_iterator I = FI.arg_begin(), + E = FI.arg_end(); + I != E; ++I, ++ArgNo) { + if (I->info.getKind() == ABIArgInfo::Indirect) { + unsigned FirstIRArg, NumIRArgs; + std::tie(FirstIRArg, NumIRArgs) = IRFunctionArgs.getIRArgs(ArgNo); + for (unsigned i = FirstIRArg; i < FirstIRArg + NumIRArgs; ++i) { + ArgsToSkip.insert(i); + } + } + } + + llvm::FunctionType *FctType = getTypes().GetFunctionType(FI); + for (unsigned i = 0; i < IRFunctionArgs.totalIRArgs(); i++) { + if (FctType->getParamType(i)->isPointerTy() && !ArgsToSkip.contains(i)) { + ArgAttrs[i] = + ArgAttrs[i].addAttribute(getLLVMContext(), *MemAttrForPtrArgs); + } + } + } + AttrList = llvm::AttributeList::get( getLLVMContext(), llvm::AttributeSet::get(getLLVMContext(), FuncAttrs), llvm::AttributeSet::get(getLLVMContext(), RetAttrs), ArgAttrs); diff --git a/clang/test/CodeGen/struct-passing.c b/clang/test/CodeGen/struct-passing.c index c8cfeb9c8168a..ba96798dc51ef 100644 --- a/clang/test/CodeGen/struct-passing.c +++ b/clang/test/CodeGen/struct-passing.c @@ -11,17 +11,25 @@ T0 __attribute__((const)) f0(void); T0 __attribute__((pure)) f1(void); T1 __attribute__((const)) f2(void); T1 __attribute__((pure)) f3(void); -void __attribute__((const)) f4(T1 a); -void __attribute__((pure)) f5(T1 a); +int __attribute__((const)) f4(T1 a); +int __attribute__((pure)) f5(T1 a); -void *ps[] = { f0, f1, f2, f3, f4, f5 }; +// NOTE: The int parameters verifies non-ptr parameters are not a problem +T1 __attribute__((const)) f6(void*, int); +T1 __attribute__((pure)) f7(void*, int); + +void *ps[] = { f0, f1, f2, f3, f4, f5, f6, f7 }; // CHECK: declare i32 @f0() [[RN:#[0-9]+]] // CHECK: declare i32 @f1() [[RO:#[0-9]+]] -// CHECK: declare void @f2({{.*}} sret({{.*}}) align 4) -// CHECK: declare void @f3({{.*}} sret({{.*}}) align 4) -// CHECK: declare void @f4({{.*}} byval({{.*}}) align 4) -// CHECK: declare void @f5({{.*}} byval({{.*}}) align 4) +// CHECK: declare void @f2(ptr {{[^,]*}} sret({{[^)]*}}) align 4) [[RNRW:#[0-9]+]] +// CHECK: declare void @f3(ptr {{[^,]*}} sret({{[^)]*}}) align 4) [[RORW:#[0-9]+]] +// CHECK: declare i32 @f4(ptr {{[^,]*}} byval({{[^)]*}}) align 4) [[RNRW:#[0-9]+]] +// CHECK: declare i32 @f5(ptr {{[^,]*}} byval({{[^)]*}}) align 4) [[RORW:#[0-9]+]] +// CHECK: declare void @f6(ptr {{[^,]*}} sret({{[^)]*}}) align 4, ptr {{[^,]*}} readnone, i32 {{[^,]*}}) [[RNRW:#[0-9]+]] +// CHECK: declare void @f7(ptr {{[^,]*}} sret({{[^)]*}}) align 4, ptr {{[^,]*}} readonly, i32 {{[^,]*}}) [[RORW:#[0-9]+]] // CHECK: attributes [[RN]] = { nounwind willreturn memory(none){{.*}} } // CHECK: attributes [[RO]] = { nounwind willreturn memory(read){{.*}} } +// CHECK: attributes [[RNRW]] = { nounwind willreturn memory(argmem: readwrite){{.*}} } +// CHECK: attributes [[RORW]] = { nounwind willreturn memory(read, argmem: readwrite){{.*}} } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
