[llvm-branch-commits] [llvm] [Transforms] Infer attributes for posix_memalign (PR #218681)

Tomas Matheson via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Tue Aug 25 07:07:46 PDT 2026


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

>From cdc20f801857b11d0a45bfeda1ab00933caf5431 Mon Sep 17 00:00:00 2001
From: Tomas Matheson <tomas.matheson at arm.com>
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}



More information about the llvm-branch-commits mailing list