[llvm] [FunctionAttrs] Handle nofreeobj (PR #218364)
Nikita Popov via llvm-commits
llvm-commits at lists.llvm.org
Mon Aug 24 02:51:45 PDT 2026
https://github.com/nikic created https://github.com/llvm/llvm-project/pull/218364
This adds handling for nofreeobj (introduced in https://github.com/llvm/llvm-project/pull/206445) in two places:
* Don't infer `nofree` if we already have `nofreeobj`. `nofreeobj` is a stronger property, it's pointless to have both.
* Use `nofreeobj` on call arguments when inferring `nofree`.
>From ae3f76b3db6c6c7bda996f50616e0c6b11013811 Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Mon, 24 Aug 2026 11:45:21 +0200
Subject: [PATCH 1/2] Add test
---
llvm/test/Transforms/FunctionAttrs/nofree.ll | 25 +++++++++++++++++---
1 file changed, 22 insertions(+), 3 deletions(-)
diff --git a/llvm/test/Transforms/FunctionAttrs/nofree.ll b/llvm/test/Transforms/FunctionAttrs/nofree.ll
index c477bf8e00aeb..4cfff49d49f23 100644
--- a/llvm/test/Transforms/FunctionAttrs/nofree.ll
+++ b/llvm/test/Transforms/FunctionAttrs/nofree.ll
@@ -103,7 +103,7 @@ define void @_Z4foo7Pi(ptr %a) local_unnamed_addr #1 {
; CHECK-NEXT: [[ISNULL:%.*]] = icmp eq ptr [[A]], null
; CHECK-NEXT: br i1 [[ISNULL]], label %[[DELETE_END:.*]], label %[[DELETE_NOTNULL:.*]]
; CHECK: [[DELETE_NOTNULL]]:
-; CHECK-NEXT: tail call void @_ZdlPv(ptr [[A]]) #[[ATTR8:[0-9]+]]
+; CHECK-NEXT: tail call void @_ZdlPv(ptr [[A]]) #[[ATTR9:[0-9]+]]
; CHECK-NEXT: br label %[[DELETE_END]]
; CHECK: [[DELETE_END]]:
; CHECK-NEXT: ret void
@@ -130,7 +130,7 @@ define void @_Z4foo8Pi(ptr %a) local_unnamed_addr #1 {
; CHECK-NEXT: [[ISNULL:%.*]] = icmp eq ptr [[A]], null
; CHECK-NEXT: br i1 [[ISNULL]], label %[[DELETE_END:.*]], label %[[DELETE_NOTNULL:.*]]
; CHECK: [[DELETE_NOTNULL]]:
-; CHECK-NEXT: tail call void @_ZdaPv(ptr [[A]]) #[[ATTR8]]
+; CHECK-NEXT: tail call void @_ZdaPv(ptr [[A]]) #[[ATTR9]]
; CHECK-NEXT: br label %[[DELETE_END]]
; CHECK: [[DELETE_END]]:
; CHECK-NEXT: ret void
@@ -252,7 +252,7 @@ define void @passed_to_nofree_fn_maybe_capture_arg(ptr %p) {
define void @passed_to_readonly_fn_nocapture_arg(ptr %p) {
; CHECK-LABEL: define void @passed_to_readonly_fn_nocapture_arg(
; CHECK-SAME: ptr nofree readonly captures(address) [[P:%.*]]) {
-; CHECK-NEXT: call void @takes_ptr(ptr captures(address, read_provenance) [[P]]) #[[ATTR9:[0-9]+]]
+; CHECK-NEXT: call void @takes_ptr(ptr captures(address, read_provenance) [[P]]) #[[ATTR10:[0-9]+]]
; CHECK-NEXT: call void @may_free()
; CHECK-NEXT: ret void
;
@@ -298,6 +298,25 @@ define void @passed_to_unknown_bundle(ptr %p) {
ret void
}
+define void @already_nofreeobj(ptr nofreeobj %p) {
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; CHECK-LABEL: define void @already_nofreeobj(
+; CHECK-SAME: ptr nofree nofreeobj readnone captures(none) [[P:%.*]]) #[[ATTR7:[0-9]+]] {
+; CHECK-NEXT: ret void
+;
+ ret void
+}
+
+define void @call_with_nofreeobj(ptr %p) {
+; CHECK-LABEL: define void @call_with_nofreeobj(
+; CHECK-SAME: ptr captures(none) [[P:%.*]]) {
+; CHECK-NEXT: call void @takes_ptr(ptr nofreeobj captures(none) [[P]])
+; CHECK-NEXT: ret void
+;
+ call void @takes_ptr(ptr nofreeobj captures(none) %p)
+ ret void
+}
+
declare void @takes_ptr(ptr)
declare void @_ZdaPv(ptr) local_unnamed_addr #4
>From 4be06e4afaa38e34eb4e0404484280426a6e1cbf Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Mon, 24 Aug 2026 11:49:04 +0200
Subject: [PATCH 2/2] Handle nofreeobj
---
llvm/lib/Transforms/IPO/FunctionAttrs.cpp | 6 ++++--
llvm/test/Transforms/FunctionAttrs/nofree.ll | 4 ++--
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
index d98d3a1c46309..d2f89150ee663 100644
--- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -952,7 +952,8 @@ determinePointerAccessAttrs(Argument *A,
if (isModSet(ArgMR) && !CB->onlyReadsMemory(UseIndex)) {
Props.IsWrite = true;
if (CB->isArgOperand(U) && !CB->hasFnAttr(Attribute::NoFree) &&
- !CB->paramHasAttr(UseIndex, Attribute::NoFree))
+ !CB->paramHasAttr(UseIndex, Attribute::NoFree) &&
+ !CB->paramHasAttr(UseIndex, Attribute::NoFreeObj))
Props.IsFree = true;
}
} else {
@@ -1075,7 +1076,8 @@ static bool addAccessAttrs(Argument *A, ArgAccessProperties Props) {
assert(A && "Argument must not be null.");
bool Changed = false;
- if (!Props.IsFree && !A->hasAttribute(Attribute::NoFree)) {
+ if (!Props.IsFree && !A->hasAttribute(Attribute::NoFree) &&
+ !A->hasAttribute(Attribute::NoFreeObj)) {
++NumNoFreeArg;
A->addAttr(Attribute::NoFree);
Changed = true;
diff --git a/llvm/test/Transforms/FunctionAttrs/nofree.ll b/llvm/test/Transforms/FunctionAttrs/nofree.ll
index 4cfff49d49f23..e342ed8f4c198 100644
--- a/llvm/test/Transforms/FunctionAttrs/nofree.ll
+++ b/llvm/test/Transforms/FunctionAttrs/nofree.ll
@@ -301,7 +301,7 @@ define void @passed_to_unknown_bundle(ptr %p) {
define void @already_nofreeobj(ptr nofreeobj %p) {
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define void @already_nofreeobj(
-; CHECK-SAME: ptr nofree nofreeobj readnone captures(none) [[P:%.*]]) #[[ATTR7:[0-9]+]] {
+; CHECK-SAME: ptr nofreeobj readnone captures(none) [[P:%.*]]) #[[ATTR7:[0-9]+]] {
; CHECK-NEXT: ret void
;
ret void
@@ -309,7 +309,7 @@ define void @already_nofreeobj(ptr nofreeobj %p) {
define void @call_with_nofreeobj(ptr %p) {
; CHECK-LABEL: define void @call_with_nofreeobj(
-; CHECK-SAME: ptr captures(none) [[P:%.*]]) {
+; CHECK-SAME: ptr nofree captures(none) [[P:%.*]]) {
; CHECK-NEXT: call void @takes_ptr(ptr nofreeobj captures(none) [[P]])
; CHECK-NEXT: ret void
;
More information about the llvm-commits
mailing list