https://github.com/tommat01 created 
https://github.com/llvm/llvm-project/pull/218681

posix_memalign writes the allocated pointer through its first argument but does 
not retain the address of that output slot. Model the contract with noundef, 
nounwind, willreturn, captures(none), writeonly, and arg/inaccessible/errno 
memory effects.

The missing capture information caused Attributor to treat an alloca passed to 
posix_memalign as escaped, preventing noalias inference at a later callback 
call site. Test both the inferred declaration attributes and the OpenMP 
callback-lifetime case that exposed the missing model.

>From f8ef4a4ed2d1814b20b103f6f32f246e93ddbd67 Mon Sep 17 00:00:00 2001
From: Tomas Matheson <[email protected]>
Date: Mon, 24 Aug 2026 11:07:35 +0100
Subject: [PATCH] [Transforms] Infer attributes for posix_memalign

posix_memalign writes the allocated pointer through its first argument but does 
not retain the address of that output slot. Model the contract with noundef, 
nounwind, willreturn, captures(none), writeonly, and arg/inaccessible/errno 
memory effects.

The missing capture information caused Attributor to treat an alloca passed to 
posix_memalign as escaped, preventing noalias inference at a later callback 
call site. Test both the inferred declaration attributes and the OpenMP 
callback-lifetime case that exposed the missing model.
---
 llvm/lib/Transforms/Utils/BuildLibCalls.cpp   |  8 +++
 .../Transforms/InferFunctionAttrs/annotate.ll |  3 +-
 .../OpenMP/callback-capture-posix-memalign.ll | 63 +++++++++++++++++++
 3 files changed, 73 insertions(+), 1 deletion(-)
 create mode 100644 
llvm/test/Transforms/OpenMP/callback-capture-posix-memalign.ll

diff --git a/llvm/lib/Transforms/Utils/BuildLibCalls.cpp 
b/llvm/lib/Transforms/Utils/BuildLibCalls.cpp
index 0afcc9df40070..fd5ebc1c7d37a 100644
--- a/llvm/lib/Transforms/Utils/BuildLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/BuildLibCalls.cpp
@@ -534,6 +534,14 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
     Changed |= setDoesNotCapture(F, 0);
     Changed |= setOnlyReadsMemory(F, 0);
     break;
+  case LibFunc_posix_memalign:
+    Changed |= setRetAndArgsNoUndef(F);
+    Changed |= setDoesNotThrow(F);
+    Changed |= setWillReturn(F);
+    Changed |= setOnlyAccessesInaccessibleMemOrArgMemOrErrnoMem(F);
+    Changed |= setDoesNotCapture(F, 0);
+    Changed |= setOnlyWritesMemory(F, 0);
+    break;
   case LibFunc_aligned_alloc:
     Changed |= setAlignedAllocParam(F, 0);
     Changed |= setAllocSize(F, 1, std::nullopt);
diff --git a/llvm/test/Transforms/InferFunctionAttrs/annotate.ll 
b/llvm/test/Transforms/InferFunctionAttrs/annotate.ll
index 44b76640cf97f..d0c29faee8257 100644
--- a/llvm/test/Transforms/InferFunctionAttrs/annotate.ll
+++ b/llvm/test/Transforms/InferFunctionAttrs/annotate.ll
@@ -817,7 +817,7 @@ declare void @perror(ptr)
 ; CHECK: declare noalias noundef ptr @popen(ptr noundef readonly 
captures(none), ptr noundef readonly captures(none)) [[NOFREE_NOUNWIND]]
 declare ptr @popen(ptr, ptr)
 
-; CHECK: declare i32 @posix_memalign(ptr, i64, i64) [[NOFREE]]
+; CHECK: declare noundef i32 @posix_memalign(ptr noundef writeonly 
captures(none), i64 noundef, i64 noundef) 
[[INACCESSIBLEMEMORARGMEMORERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN:#[0-9]+]]
 declare i32 @posix_memalign(ptr, i64, i64)
 
 ; CHECK: declare double @pow(double, double) 
[[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
@@ -1250,6 +1250,7 @@ declare void @memset_pattern16(ptr, ptr, i64)
 ; CHECK-DAG: attributes 
[[INACCESSIBLEMEMORARGMEMORERRNOMEMONLY_NOUNWIND_WILLRETURN_ALLOCKIND_REALLOC_ALLOCSIZE1_FAMILY_MALLOC]]
 = { mustprogress nounwind willreturn allockind("realloc") allocsize(1) 
memory(argmem: readwrite, inaccessiblemem: readwrite, errnomem: write) 
"alloc-family"="malloc" }
 ; CHECK-DAG: attributes 
[[INACCESSIBLEMEMORARGMEMORERRNOMEMONLY_NOUNWIND_WILLRETURN_ALLOCKIND_REALLOC_ALLOCSIZE12_FAMILY_MALLOC]]
 = { mustprogress nounwind willreturn allockind("realloc") allocsize(1,2) 
memory(argmem: readwrite, inaccessiblemem: readwrite, errnomem: write) 
"alloc-family"="malloc" }
 ; CHECK-DAG: attributes 
[[INACCESSIBLEMEMORARGMEMORERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN_FAMILY_MALLOC]]
 = { mustprogress nofree nounwind willreturn memory(argmem: readwrite, 
inaccessiblemem: readwrite, errnomem: write) "alloc-family"="malloc" }
+; CHECK-DAG: attributes 
[[INACCESSIBLEMEMORARGMEMORERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]] = { 
mustprogress nofree nounwind willreturn memory(argmem: readwrite, 
inaccessiblemem: readwrite, errnomem: write) }
 ; CHECK-DAG: attributes [[NOFREE_COLD_NORETURN_NOUNWIND]] = { cold nofree 
noreturn nounwind }
 ; CHECK-DAG: attributes [[NOFREE_COLD_NORETURN]] = { cold nofree noreturn }
 ; CHECK-DAG: attributes [[COLD_NORETURN]] = { cold noreturn }
diff --git a/llvm/test/Transforms/OpenMP/callback-capture-posix-memalign.ll 
b/llvm/test/Transforms/OpenMP/callback-capture-posix-memalign.ll
new file mode 100644
index 0000000000000..710852959b302
--- /dev/null
+++ b/llvm/test/Transforms/OpenMP/callback-capture-posix-memalign.ll
@@ -0,0 +1,63 @@
+; RUN: opt -passes='inferattrs,openmp-opt,function(loop-mssa(licm))' -S < %s | 
FileCheck %s
+
+target triple = "aarch64-unknown-linux-gnu"
+
+; posix_memalign writes through its output slot but does not capture the slot.
+; That earlier use must not prevent the callback capture from becoming noalias
+; or the load from being hoisted across an unknown call.
+; CHECK: call i32 @posix_memalign(ptr writeonly captures(none) %slot, i64 64, 
i64 %size)
+; CHECK-LABEL: define internal void @outlined(
+; CHECK-SAME: ptr noalias readonly align 8 captures(none) dereferenceable(8) 
%capture)
+; CHECK: call void @opaque()
+; CHECK-NEXT: [[VALUES:%.*]] = load ptr, ptr %capture, align 8
+; CHECK: loop:
+; CHECK-NOT: load ptr, ptr %capture
+; CHECK: if.then:
+; CHECK-NEXT: [[ELEMENT:%.*]] = getelementptr double, ptr [[VALUES]], i64 
[[I:%.*]]
+
+define i32 @run(i64 %size) {
+entry:
+  %slot = alloca ptr, align 8
+  %status = call i32 @posix_memalign(ptr %slot, i64 64, i64 %size)
+  call void (ptr, i32, ptr, ...) @__kmpc_fork_call(
+      ptr null, i32 1, ptr @outlined, ptr %slot)
+  ret i32 %status
+}
+
+define internal void @outlined(
+    ptr %global_tid, ptr %bound_tid,
+    ptr align 8 dereferenceable(8) %capture) {
+entry:
+  call void @opaque()
+  br label %loop
+
+loop:
+  %i = phi i64 [ 0, %entry ], [ %next, %latch ]
+  %enabled = icmp eq i64 %i, 7
+  br i1 %enabled, label %if.then, label %latch
+
+if.then:
+  %values = load ptr, ptr %capture, align 8
+  %element = getelementptr double, ptr %values, i64 %i
+  %value = load double, ptr %element, align 8
+  call void @use(double %value)
+  br label %latch
+
+latch:
+  %next = add nuw nsw i64 %i, 1
+  %done = icmp eq i64 %next, 64
+  br i1 %done, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+declare i32 @posix_memalign(ptr, i64, i64)
+declare !callback !0 void @__kmpc_fork_call(ptr, i32, ptr, ...)
+declare void @opaque()
+declare void @use(double) memory(none)
+
+!0 = !{!1}
+!1 = !{i64 2, i64 -1, i64 -1, i1 true}
+!llvm.module.flags = !{!2}
+!2 = !{i32 7, !"openmp", i32 51}

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

Reply via email to