[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 06:25:33 PDT 2026
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 <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