[clang] [clang] Add missing readonly/readnone annotations (PR #158424)

Quentin Chateau via cfe-commits cfe-commits at lists.llvm.org
Thu Sep 18 12:22:14 PDT 2025


https://github.com/qchateau updated https://github.com/llvm/llvm-project/pull/158424

>From 750c54ddb4b7e0f9155ae25393aa5305e62d5965 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        | 26 ++++++++++++++++++++++++++
 clang/test/CodeGen/struct-passing.c | 22 +++++++++++++++-------
 2 files changed, 41 insertions(+), 7 deletions(-)

diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 0b2fce4244fb6..a931ce476b8ae 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,27 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,
   }
   assert(ArgNo == FI.arg_size());
 
+  ArgNo = 0;
+  if (AddedPotentialArgAccess && MemAttrForPtrArgs) {
+    llvm::FunctionType *FunctionType = FunctionType =
+        getTypes().GetFunctionType(FI);
+    for (CGFunctionInfo::const_arg_iterator I = FI.arg_begin(),
+                                            E = FI.arg_end();
+         I != E; ++I, ++ArgNo) {
+      if (I->info.isDirect() || I->info.isExpand() ||
+          I->info.isCoerceAndExpand()) {
+        unsigned FirstIRArg, NumIRArgs;
+        std::tie(FirstIRArg, NumIRArgs) = IRFunctionArgs.getIRArgs(ArgNo);
+        for (unsigned i = FirstIRArg; i < FirstIRArg + NumIRArgs; ++i) {
+          if (FunctionType->getParamType(i)->isPointerTy()) {
+            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){{.*}} }



More information about the cfe-commits mailing list