https://github.com/nikic updated 
https://github.com/llvm/llvm-project/pull/197199

>From 101f17f32a73f84964cbc8f8093d33bcae433054 Mon Sep 17 00:00:00 2001
From: Nikita Popov <[email protected]>
Date: Tue, 12 May 2026 15:46:33 +0200
Subject: [PATCH 1/4] [Clang] Mark new as inaccessiblememonly if sane
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

If `-fassume-sane-operator-new` (the default), assume that
`operator new` does not read or write accessible memory.

Currently, this assumption already exists (I think even if
`-fno-assume-sane-operator-new` is used) due to special treatment
in BasicAA. I'd like to remove this special treatment (see
https://github.com/llvm/llvm-project/pull/197180), and instead
rely only on the `memory` attribute.

It's worth noting that this is consistent with GCC's interpretation
of the flag (where it is also enabled by default):

> [...] With -fassume-sane-operators-new-delete option GCC may assume that 
> calls to the replaceable global operators from new or delete expressions or 
> from __builtin_operator_new or __builtin_operator_delete calls don’t read or 
> modify any global variables or variables whose address could escape to the 
> operators (global state; except for errno for the new and new[] operators). 
> [...]
---
 clang/lib/CodeGen/CGCall.cpp           | 7 +++++--
 clang/test/CodeGenCXX/new_hot_cold.cpp | 8 ++++----
 clang/test/CodeGenCXX/operator-new.cpp | 6 ++++--
 3 files changed, 13 insertions(+), 8 deletions(-)

diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 2468394929360..e0f79433381bc 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -2615,11 +2615,14 @@ void CodeGenModule::ConstructAttributeList(StringRef 
Name,
       AddAttributesFromFunctionProtoType(
           getContext(), FuncAttrs, Fn->getType()->getAs<FunctionProtoType>());
       if (AttrOnCallSite && Fn->isReplaceableGlobalAllocationFunction()) {
-        // A sane operator new returns a non-aliasing pointer.
+        // A sane operator new returns a non-aliasing pointer and does not
+        // read or write accessible memory.
         auto Kind = Fn->getDeclName().getCXXOverloadedOperator();
         if (getCodeGenOpts().AssumeSaneOperatorNew &&
-            (Kind == OO_New || Kind == OO_Array_New))
+            (Kind == OO_New || Kind == OO_Array_New)) {
           RetAttrs.addAttribute(llvm::Attribute::NoAlias);
+          FuncAttrs.addMemoryAttr(llvm::MemoryEffects::inaccessibleMemOnly());
+        }
       }
       const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Fn);
       const bool IsVirtualCall = MD && MD->isVirtual();
diff --git a/clang/test/CodeGenCXX/new_hot_cold.cpp 
b/clang/test/CodeGenCXX/new_hot_cold.cpp
index 014e815201485..385f6cc07f689 100644
--- a/clang/test/CodeGenCXX/new_hot_cold.cpp
+++ b/clang/test/CodeGenCXX/new_hot_cold.cpp
@@ -124,7 +124,7 @@ void hot_cold_new_align_nothrow_array() {
 
 // CHECK-DAG: attributes [[ATTR_NOBUILTIN]] = { nobuiltin allocsize(0) {{.*}} }
 // CHECK-DAG: attributes [[ATTR_NOBUILTIN_NOTHROW]] = { nobuiltin nounwind 
allocsize(0) {{.*}} }
-// CHECK-DAG: attributes [[ATTR_NO_BUILTIN_CALL]] = { allocsize(0) }
-// CHECK-DAG: attributes [[ATTR_BUILTIN_CALL]] = { builtin allocsize(0) }
-// CHECK-DAG: attributes [[ATTR_NO_BUILTIN_NOTHROW_CALL]] = { nounwind 
allocsize(0) }
-// CHECK-DAG: attributes [[ATTR_BUILTIN_NOTHROW_CALL]] = { builtin nounwind 
allocsize(0) }
+// CHECK-DAG: attributes [[ATTR_NO_BUILTIN_CALL]] = { allocsize(0) 
memory(inaccessiblemem: readwrite) }
+// CHECK-DAG: attributes [[ATTR_BUILTIN_CALL]] = { builtin allocsize(0) 
memory(inaccessiblemem: readwrite) }
+// CHECK-DAG: attributes [[ATTR_NO_BUILTIN_NOTHROW_CALL]] = { nounwind 
allocsize(0) memory(inaccessiblemem: readwrite) }
+// CHECK-DAG: attributes [[ATTR_BUILTIN_NOTHROW_CALL]] = { builtin nounwind 
allocsize(0) memory(inaccessiblemem: readwrite) }
diff --git a/clang/test/CodeGenCXX/operator-new.cpp 
b/clang/test/CodeGenCXX/operator-new.cpp
index 1d7887146705e..8a71bd96e9b97 100644
--- a/clang/test/CodeGenCXX/operator-new.cpp
+++ b/clang/test/CodeGenCXX/operator-new.cpp
@@ -22,8 +22,10 @@ void *f2(long N) {
   // ALL-NEXT: [[OVER:%.*]] = extractvalue {{.*}} [[UWO]], 1
   // ALL-NEXT: [[SUM:%.*]] = extractvalue {{.*}} [[UWO]], 0
   // ALL-NEXT: [[RESULT:%.*]] = select i1 [[OVER]], i32 -1, i32 [[SUM]]
-  // SANE-NEXT: call noalias noundef nonnull ptr @_Znaj(i32 noundef [[RESULT]])
-  // SANENOT-NEXT: call noundef nonnull ptr @_Znaj(i32 noundef [[RESULT]])
+  // SANE-NEXT: call noalias noundef nonnull ptr @_Znaj(i32 noundef 
[[RESULT]]) [[ATTR:#[0-9]+]]
+  // SANENOT-NEXT: call noundef nonnull ptr @_Znaj(i32 noundef [[RESULT]]) 
[[ATTR:#[0-9]+]]
 }
 
 // ALL: declare noundef nonnull ptr @_Znaj(
+// SANE: attributes [[ATTR]] = { builtin allocsize(0) memory(inaccessiblemem: 
readwrite) }
+// SANENOT: attributes [[ATTR]] = { builtin allocsize(0) }

>From 40202d49054329741d0f574cec4241ca70b956a2 Mon Sep 17 00:00:00 2001
From: Nikita Popov <[email protected]>
Date: Tue, 12 May 2026 16:50:32 +0200
Subject: [PATCH 2/4] Also add errnomem: write effect to match GCC

---
 clang/lib/CodeGen/CGCall.cpp           | 4 +++-
 clang/test/CodeGenCXX/new_hot_cold.cpp | 8 ++++----
 clang/test/CodeGenCXX/operator-new.cpp | 2 +-
 3 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index e0f79433381bc..6ca96ec024b9e 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -2621,7 +2621,9 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,
         if (getCodeGenOpts().AssumeSaneOperatorNew &&
             (Kind == OO_New || Kind == OO_Array_New)) {
           RetAttrs.addAttribute(llvm::Attribute::NoAlias);
-          FuncAttrs.addMemoryAttr(llvm::MemoryEffects::inaccessibleMemOnly());
+          FuncAttrs.addMemoryAttr(
+              llvm::MemoryEffects::inaccessibleOrErrnoMemOnly(
+                  llvm::ModRefInfo::ModRef, llvm::ModRefInfo::Mod));
         }
       }
       const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Fn);
diff --git a/clang/test/CodeGenCXX/new_hot_cold.cpp 
b/clang/test/CodeGenCXX/new_hot_cold.cpp
index 385f6cc07f689..439166ddea566 100644
--- a/clang/test/CodeGenCXX/new_hot_cold.cpp
+++ b/clang/test/CodeGenCXX/new_hot_cold.cpp
@@ -124,7 +124,7 @@ void hot_cold_new_align_nothrow_array() {
 
 // CHECK-DAG: attributes [[ATTR_NOBUILTIN]] = { nobuiltin allocsize(0) {{.*}} }
 // CHECK-DAG: attributes [[ATTR_NOBUILTIN_NOTHROW]] = { nobuiltin nounwind 
allocsize(0) {{.*}} }
-// CHECK-DAG: attributes [[ATTR_NO_BUILTIN_CALL]] = { allocsize(0) 
memory(inaccessiblemem: readwrite) }
-// CHECK-DAG: attributes [[ATTR_BUILTIN_CALL]] = { builtin allocsize(0) 
memory(inaccessiblemem: readwrite) }
-// CHECK-DAG: attributes [[ATTR_NO_BUILTIN_NOTHROW_CALL]] = { nounwind 
allocsize(0) memory(inaccessiblemem: readwrite) }
-// CHECK-DAG: attributes [[ATTR_BUILTIN_NOTHROW_CALL]] = { builtin nounwind 
allocsize(0) memory(inaccessiblemem: readwrite) }
+// CHECK-DAG: attributes [[ATTR_NO_BUILTIN_CALL]] = { allocsize(0) 
memory(inaccessiblemem: readwrite, errnomem: write) }
+// CHECK-DAG: attributes [[ATTR_BUILTIN_CALL]] = { builtin allocsize(0) 
memory(inaccessiblemem: readwrite, errnomem: write) }
+// CHECK-DAG: attributes [[ATTR_NO_BUILTIN_NOTHROW_CALL]] = { nounwind 
allocsize(0) memory(inaccessiblemem: readwrite, errnomem: write) }
+// CHECK-DAG: attributes [[ATTR_BUILTIN_NOTHROW_CALL]] = { builtin nounwind 
allocsize(0) memory(inaccessiblemem: readwrite, errnomem: write) }
diff --git a/clang/test/CodeGenCXX/operator-new.cpp 
b/clang/test/CodeGenCXX/operator-new.cpp
index 8a71bd96e9b97..e741d85fe4cd2 100644
--- a/clang/test/CodeGenCXX/operator-new.cpp
+++ b/clang/test/CodeGenCXX/operator-new.cpp
@@ -27,5 +27,5 @@ void *f2(long N) {
 }
 
 // ALL: declare noundef nonnull ptr @_Znaj(
-// SANE: attributes [[ATTR]] = { builtin allocsize(0) memory(inaccessiblemem: 
readwrite) }
+// SANE: attributes [[ATTR]] = { builtin allocsize(0) memory(inaccessiblemem: 
readwrite, errnomem: write) }
 // SANENOT: attributes [[ATTR]] = { builtin allocsize(0) }

>From 0d6a98ba2e342594c87cf9d34db933d7926a4611 Mon Sep 17 00:00:00 2001
From: Nikita Popov <[email protected]>
Date: Wed, 13 May 2026 09:20:18 +0200
Subject: [PATCH 3/4] Add comment on potential LTO issue

---
 clang/lib/CodeGen/CGCall.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 6ca96ec024b9e..edeb1c361ed9a 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -2621,6 +2621,8 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,
         if (getCodeGenOpts().AssumeSaneOperatorNew &&
             (Kind == OO_New || Kind == OO_Array_New)) {
           RetAttrs.addAttribute(llvm::Attribute::NoAlias);
+          // FIXME: inaccessiblemem could cause issues if LTO makes the
+          // previously inaccessible memory accessible after linking.
           FuncAttrs.addMemoryAttr(
               llvm::MemoryEffects::inaccessibleOrErrnoMemOnly(
                   llvm::ModRefInfo::ModRef, llvm::ModRefInfo::Mod));

>From f7464015fa8befe5d314be1dcf4eb9563967a2fd Mon Sep 17 00:00:00 2001
From: Nikita Popov <[email protected]>
Date: Thu, 28 May 2026 14:58:18 +0200
Subject: [PATCH 4/4] Use isAnyOperatorNew()

---
 clang/lib/CodeGen/CGCall.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index edeb1c361ed9a..f4c6f3a6e96f4 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -2617,9 +2617,8 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,
       if (AttrOnCallSite && Fn->isReplaceableGlobalAllocationFunction()) {
         // A sane operator new returns a non-aliasing pointer and does not
         // read or write accessible memory.
-        auto Kind = Fn->getDeclName().getCXXOverloadedOperator();
         if (getCodeGenOpts().AssumeSaneOperatorNew &&
-            (Kind == OO_New || Kind == OO_Array_New)) {
+            Fn->getDeclName().isAnyOperatorNew()) {
           RetAttrs.addAttribute(llvm::Attribute::NoAlias);
           // FIXME: inaccessiblemem could cause issues if LTO makes the
           // previously inaccessible memory accessible after linking.

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to