Skip to content

[IR] Only require nofree in canBeFreed()#200191

Merged
nikic merged 1 commit into
llvm:mainfrom
nikic:nofree-without-nosync
May 29, 2026
Merged

[IR] Only require nofree in canBeFreed()#200191
nikic merged 1 commit into
llvm:mainfrom
nikic:nofree-without-nosync

Conversation

@nikic

@nikic nikic commented May 28, 2026

Copy link
Copy Markdown
Contributor

After #195658 we only need the function to be nofree, as this also precludes freeing via synchronization now. Being nosync is no longer required.

After llvm#195658 we only
need the function to be nofree, as this also precludes freeing
via synchronization now. Being nosync is no longer required.
@llvmorg-github-actions llvmorg-github-actions Bot added llvm:ir llvm:analysis Includes value tracking, cost tables and constant folding labels May 28, 2026
@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-llvm-ir

Author: Nikita Popov (nikic)

Changes

After #195658 we only need the function to be nofree, as this also precludes freeing via synchronization now. Being nosync is no longer required.


Full diff: https://github.com/llvm/llvm-project/pull/200191.diff

2 Files Affected:

  • (modified) llvm/lib/IR/Value.cpp (+5-6)
  • (modified) llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll (+2-4)
diff --git a/llvm/lib/IR/Value.cpp b/llvm/lib/IR/Value.cpp
index 7246adf7ec651..074d42d728c95 100644
--- a/llvm/lib/IR/Value.cpp
+++ b/llvm/lib/IR/Value.cpp
@@ -834,13 +834,12 @@ bool Value::canBeFreed() const {
   if (auto *A = dyn_cast<Argument>(this)) {
     if (A->hasPointeeInMemoryValueAttr())
       return false;
-    // A pointer to an object in a function which neither frees, nor can arrange
-    // for another thread to free on its behalf, can not be freed in the scope
-    // of the function.  Note that this logic is restricted to memory
-    // allocations in existance before the call; a nofree function *is* allowed
-    // to free memory it allocated.
+    // A nofree function can not free (including via synchronization) any
+    // allocations that existed prior to the call, but may free allocations
+    // created inside the function. This logic is limited to argument pointers,
+    // as they definitely exist prior to the call.
     const Function *F = A->getParent();
-    if (F->doesNotFreeMemory() && F->hasNoSync())
+    if (F->doesNotFreeMemory())
       return false;
   }
 
diff --git a/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll b/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll
index fc40c68624f2d..43f2c156b2ddc 100644
--- a/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll
+++ b/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll
@@ -248,16 +248,14 @@ define void @negative(ptr dereferenceable(8) %p) {
 
 ; CHECK-LABEL: 'infer_func_attrs1'
 ; CHECK: %p
-define void @infer_func_attrs1(ptr dereferenceable(8) %p) nofree nosync {
+define void @infer_func_attrs1(ptr dereferenceable(8) %p) nofree {
   call void @mayfree()
   %v = load i32, ptr %p
   ret void
 }
 
 ; CHECK-LABEL: 'infer_func_attrs2'
-; GLOBAL: %p
-; POINT-NOT: %p
-; FIXME: Can be inferred from attributes
+; CHECK: %p
 define void @infer_func_attrs2(ptr dereferenceable(8) %p) readonly {
   call void @mayfree()
   %v = load i32, ptr %p

@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-llvm-analysis

Author: Nikita Popov (nikic)

Changes

After #195658 we only need the function to be nofree, as this also precludes freeing via synchronization now. Being nosync is no longer required.


Full diff: https://github.com/llvm/llvm-project/pull/200191.diff

2 Files Affected:

  • (modified) llvm/lib/IR/Value.cpp (+5-6)
  • (modified) llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll (+2-4)
diff --git a/llvm/lib/IR/Value.cpp b/llvm/lib/IR/Value.cpp
index 7246adf7ec651..074d42d728c95 100644
--- a/llvm/lib/IR/Value.cpp
+++ b/llvm/lib/IR/Value.cpp
@@ -834,13 +834,12 @@ bool Value::canBeFreed() const {
   if (auto *A = dyn_cast<Argument>(this)) {
     if (A->hasPointeeInMemoryValueAttr())
       return false;
-    // A pointer to an object in a function which neither frees, nor can arrange
-    // for another thread to free on its behalf, can not be freed in the scope
-    // of the function.  Note that this logic is restricted to memory
-    // allocations in existance before the call; a nofree function *is* allowed
-    // to free memory it allocated.
+    // A nofree function can not free (including via synchronization) any
+    // allocations that existed prior to the call, but may free allocations
+    // created inside the function. This logic is limited to argument pointers,
+    // as they definitely exist prior to the call.
     const Function *F = A->getParent();
-    if (F->doesNotFreeMemory() && F->hasNoSync())
+    if (F->doesNotFreeMemory())
       return false;
   }
 
diff --git a/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll b/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll
index fc40c68624f2d..43f2c156b2ddc 100644
--- a/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll
+++ b/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll
@@ -248,16 +248,14 @@ define void @negative(ptr dereferenceable(8) %p) {
 
 ; CHECK-LABEL: 'infer_func_attrs1'
 ; CHECK: %p
-define void @infer_func_attrs1(ptr dereferenceable(8) %p) nofree nosync {
+define void @infer_func_attrs1(ptr dereferenceable(8) %p) nofree {
   call void @mayfree()
   %v = load i32, ptr %p
   ret void
 }
 
 ; CHECK-LABEL: 'infer_func_attrs2'
-; GLOBAL: %p
-; POINT-NOT: %p
-; FIXME: Can be inferred from attributes
+; CHECK: %p
 define void @infer_func_attrs2(ptr dereferenceable(8) %p) readonly {
   call void @mayfree()
   %v = load i32, ptr %p

@efriedma-quic efriedma-quic left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@antoniofrighetto antoniofrighetto left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LG. Should willNotFreeBetween() require an update too?

// Make sure the current function cannot arrange for another thread to free on
// its behalf.
if (!CtxI->getFunction()->hasNoSync())
return false;

@nikic

nikic commented May 28, 2026

Copy link
Copy Markdown
Contributor Author

@antoniofrighetto Yes, see #199002 for some related work.

@antoniofrighetto

Copy link
Copy Markdown
Contributor

@antoniofrighetto Yes, see #199002 for some related work.

Thanks, I missed that.

@nikic
nikic merged commit b835d5a into llvm:main May 29, 2026
13 checks passed
@nikic
nikic deleted the nofree-without-nosync branch May 29, 2026 06:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

llvm:analysis Includes value tracking, cost tables and constant folding llvm:ir

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants