[clang] [clang] Add missing readonly/readnone annotations (PR #158424)
Quentin Chateau via cfe-commits
cfe-commits at lists.llvm.org
Sat Sep 13 08:16:30 PDT 2025
https://github.com/qchateau updated https://github.com/llvm/llvm-project/pull/158424
>From 0fc479ab84f2e9e2ce6d52c994a56c6678ecc937 Mon Sep 17 00:00:00 2001
From: Quentin Chateau <quentin.chateau at gmail.com>
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 | 8 ++++++-
2 files changed, 43 insertions(+), 1 deletion(-)
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..1934669ce7c0a 100644
--- a/clang/test/CodeGen/struct-passing.c
+++ b/clang/test/CodeGen/struct-passing.c
@@ -14,7 +14,11 @@ T1 __attribute__((pure)) f3(void);
void __attribute__((const)) f4(T1 a);
void __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]+]]
@@ -22,6 +26,8 @@ void *ps[] = { f0, f1, f2, f3, f4, f5 };
// CHECK: declare void @f3({{.*}} sret({{.*}}) align 4)
// CHECK: declare void @f4({{.*}} byval({{.*}}) align 4)
// CHECK: declare void @f5({{.*}} byval({{.*}}) align 4)
+// CHECK: declare void @f6({{.*}} sret({{.*}}) align 4, {{.*}} readnone{{.*}})
+// CHECK: declare void @f7({{.*}} sret({{.*}}) align 4, {{.*}} readonly{{.*}})
// CHECK: attributes [[RN]] = { nounwind willreturn memory(none){{.*}} }
// CHECK: attributes [[RO]] = { nounwind willreturn memory(read){{.*}} }
More information about the cfe-commits
mailing list