[llvm] fa3d170 - [FunctionAttrs] Infer noalias through a null check. (#226956)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 29 02:53:43 PDT 2026
Author: Florian Hahn
Date: 2026-09-29T09:53:35Z
New Revision: fa3d17075284bf746f4030ad3371fe5c05b06653
URL: https://github.com/llvm/llvm-project/commit/fa3d17075284bf746f4030ad3371fe5c05b06653
DIFF: https://github.com/llvm/llvm-project/commit/fa3d17075284bf746f4030ad3371fe5c05b06653.diff
LOG: [FunctionAttrs] Infer noalias through a null check. (#226956)
Only comparing against null does not captures provenance and should not
impact whether a pointer is noalias or not.
This enables noalias inference in a number of cases for malloc-like
functions:
https://github.com/dtcxzyw/llvm-opt-benchmark-nightly/pull/1459.
PR: https://github.com/llvm/llvm-project/pull/226956
Added:
Modified:
llvm/lib/Transforms/IPO/FunctionAttrs.cpp
llvm/test/Transforms/FunctionAttrs/noalias.ll
Removed:
################################################################################
diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
index a713ead683476..8e8f9f8c12b5e 100644
--- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -1485,7 +1485,11 @@ static bool isFunctionMallocLike(Function *F, const SCCNodeSet &SCCNodes) {
return false; // Did not come from an allocation.
}
- if (PointerMayBeCaptured(RetVal, /*ReturnCaptures=*/false))
+ // Only capturing the provenance of the result prevents it from being
+ // noalias.
+ if (capturesAnything(
+ PointerMayBeCaptured(RetVal, CaptureComponents::Provenance)
+ .WithoutRet))
return false;
}
diff --git a/llvm/test/Transforms/FunctionAttrs/noalias.ll b/llvm/test/Transforms/FunctionAttrs/noalias.ll
index 25c701bf33f76..198b8e788b58c 100644
--- a/llvm/test/Transforms/FunctionAttrs/noalias.ll
+++ b/llvm/test/Transforms/FunctionAttrs/noalias.ll
@@ -243,3 +243,195 @@ define ptr @return_unknown_noalias_call(ptr %fn) {
%a = call noalias ptr %fn()
ret ptr %a
}
+
+; A null check does not capture the provenance of the result.
+define ptr @return_malloc_null_checked(i64 %size) {
+; CHECK-LABEL: define noalias ptr @return_malloc_null_checked(
+; CHECK-SAME: i64 [[SIZE:%.*]]) {
+; CHECK-NEXT: [[A:%.*]] = call ptr @malloc(i64 [[SIZE]])
+; CHECK-NEXT: [[C:%.*]] = icmp eq ptr [[A]], null
+; CHECK-NEXT: br i1 [[C]], label %[[THEN:.*]], label %[[ELSE:.*]]
+; CHECK: [[THEN]]:
+; CHECK-NEXT: ret ptr null
+; CHECK: [[ELSE]]:
+; CHECK-NEXT: ret ptr [[A]]
+;
+ %a = call ptr @malloc(i64 %size)
+ %c = icmp eq ptr %a, null
+ br i1 %c, label %then, label %else
+
+then:
+ ret ptr null
+
+else:
+ ret ptr %a
+}
+
+; A null check does not hide the capture by the store to @g.
+define ptr @return_malloc_null_checked_captured(i64 %size) {
+; CHECK-LABEL: define ptr @return_malloc_null_checked_captured(
+; CHECK-SAME: i64 [[SIZE:%.*]]) {
+; CHECK-NEXT: [[A:%.*]] = call ptr @malloc(i64 [[SIZE]])
+; CHECK-NEXT: [[C:%.*]] = icmp eq ptr [[A]], null
+; CHECK-NEXT: store ptr [[A]], ptr @g, align 8
+; CHECK-NEXT: br i1 [[C]], label %[[THEN:.*]], label %[[ELSE:.*]]
+; CHECK: [[THEN]]:
+; CHECK-NEXT: ret ptr null
+; CHECK: [[ELSE]]:
+; CHECK-NEXT: ret ptr [[A]]
+;
+ %a = call ptr @malloc(i64 %size)
+ %c = icmp eq ptr %a, null
+ store ptr %a, ptr @g
+ br i1 %c, label %then, label %else
+
+then:
+ ret ptr null
+
+else:
+ ret ptr %a
+}
+
+declare void @capture_address_is_null(ptr captures(address_is_null))
+declare void @capture_provenance(ptr captures(provenance))
+declare void @capture_address_is_null_provenance(ptr captures(address_is_null, provenance))
+declare void @capture_read_provenance(ptr captures(read_provenance))
+
+; Only whether the result is null is captured.
+define ptr @return_malloc_capture_address_is_null(i64 %size) {
+; CHECK-LABEL: define noalias ptr @return_malloc_capture_address_is_null(
+; CHECK-SAME: i64 [[SIZE:%.*]]) {
+; CHECK-NEXT: [[A:%.*]] = call ptr @malloc(i64 [[SIZE]])
+; CHECK-NEXT: call void @capture_address_is_null(ptr [[A]])
+; CHECK-NEXT: ret ptr [[A]]
+;
+ %a = call ptr @malloc(i64 %size)
+ call void @capture_address_is_null(ptr %a)
+ ret ptr %a
+}
+
+; The provenance of the result is captured in addition to the null check.
+define ptr @return_malloc_null_checked_capture_provenance(i64 %size) {
+; CHECK-LABEL: define ptr @return_malloc_null_checked_capture_provenance(
+; CHECK-SAME: i64 [[SIZE:%.*]]) {
+; CHECK-NEXT: [[A:%.*]] = call ptr @malloc(i64 [[SIZE]])
+; CHECK-NEXT: [[C:%.*]] = icmp eq ptr [[A]], null
+; CHECK-NEXT: br i1 [[C]], label %[[THEN:.*]], label %[[ELSE:.*]]
+; CHECK: [[THEN]]:
+; CHECK-NEXT: ret ptr null
+; CHECK: [[ELSE]]:
+; CHECK-NEXT: call void @capture_provenance(ptr [[A]])
+; CHECK-NEXT: ret ptr [[A]]
+;
+ %a = call ptr @malloc(i64 %size)
+ %c = icmp eq ptr %a, null
+ br i1 %c, label %then, label %else
+
+then:
+ ret ptr null
+
+else:
+ call void @capture_provenance(ptr %a)
+ ret ptr %a
+}
+
+; The provenance of the result is captured together with the null check.
+define ptr @return_malloc_capture_address_is_null_provenance(i64 %size) {
+; CHECK-LABEL: define ptr @return_malloc_capture_address_is_null_provenance(
+; CHECK-SAME: i64 [[SIZE:%.*]]) {
+; CHECK-NEXT: [[A:%.*]] = call ptr @malloc(i64 [[SIZE]])
+; CHECK-NEXT: call void @capture_address_is_null_provenance(ptr [[A]])
+; CHECK-NEXT: ret ptr [[A]]
+;
+ %a = call ptr @malloc(i64 %size)
+ call void @capture_address_is_null_provenance(ptr %a)
+ ret ptr %a
+}
+
+; The read provenance of the result is captured in addition to the null check.
+define ptr @return_malloc_null_checked_capture_read_provenance(i64 %size) {
+; CHECK-LABEL: define ptr @return_malloc_null_checked_capture_read_provenance(
+; CHECK-SAME: i64 [[SIZE:%.*]]) {
+; CHECK-NEXT: [[A:%.*]] = call ptr @malloc(i64 [[SIZE]])
+; CHECK-NEXT: [[C:%.*]] = icmp eq ptr [[A]], null
+; CHECK-NEXT: br i1 [[C]], label %[[THEN:.*]], label %[[ELSE:.*]]
+; CHECK: [[THEN]]:
+; CHECK-NEXT: ret ptr null
+; CHECK: [[ELSE]]:
+; CHECK-NEXT: call void @capture_read_provenance(ptr [[A]])
+; CHECK-NEXT: ret ptr [[A]]
+;
+ %a = call ptr @malloc(i64 %size)
+ %c = icmp eq ptr %a, null
+ br i1 %c, label %then, label %else
+
+then:
+ ret ptr null
+
+else:
+ call void @capture_read_provenance(ptr %a)
+ ret ptr %a
+}
+
+; A null check of a GEP does not capture the provenance of the result.
+define ptr @return_malloc_gep_null_checked(i64 %size) {
+; CHECK-LABEL: define noalias ptr @return_malloc_gep_null_checked(
+; CHECK-SAME: i64 [[SIZE:%.*]]) {
+; CHECK-NEXT: [[A:%.*]] = call ptr @malloc(i64 [[SIZE]])
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[A]], i64 8
+; CHECK-NEXT: [[C:%.*]] = icmp eq ptr [[GEP]], null
+; CHECK-NEXT: br i1 [[C]], label %[[THEN:.*]], label %[[ELSE:.*]]
+; CHECK: [[THEN]]:
+; CHECK-NEXT: ret ptr null
+; CHECK: [[ELSE]]:
+; CHECK-NEXT: ret ptr [[A]]
+;
+ %a = call ptr @malloc(i64 %size)
+ %gep = getelementptr i8, ptr %a, i64 8
+ %c = icmp eq ptr %gep, null
+ br i1 %c, label %then, label %else
+
+then:
+ ret ptr null
+
+else:
+ ret ptr %a
+}
+
+; Comparing the result against another pointer only captures its address.
+define ptr @return_malloc_compared(i64 %size, ptr %p) {
+; CHECK-LABEL: define noalias ptr @return_malloc_compared(
+; CHECK-SAME: i64 [[SIZE:%.*]], ptr nofree readnone captures(address) [[P:%.*]]) {
+; CHECK-NEXT: [[A:%.*]] = call ptr @malloc(i64 [[SIZE]])
+; CHECK-NEXT: [[C:%.*]] = icmp eq ptr [[A]], [[P]]
+; CHECK-NEXT: br i1 [[C]], label %[[THEN:.*]], label %[[ELSE:.*]]
+; CHECK: [[THEN]]:
+; CHECK-NEXT: ret ptr null
+; CHECK: [[ELSE]]:
+; CHECK-NEXT: ret ptr [[A]]
+;
+ %a = call ptr @malloc(i64 %size)
+ %c = icmp eq ptr %a, %p
+ br i1 %c, label %then, label %else
+
+then:
+ ret ptr null
+
+else:
+ ret ptr %a
+}
+
+declare void @capture_address(ptr captures(address))
+
+; Only the address of the result is captured.
+define ptr @return_malloc_capture_address(i64 %size) {
+; CHECK-LABEL: define noalias ptr @return_malloc_capture_address(
+; CHECK-SAME: i64 [[SIZE:%.*]]) {
+; CHECK-NEXT: [[A:%.*]] = call ptr @malloc(i64 [[SIZE]])
+; CHECK-NEXT: call void @capture_address(ptr [[A]])
+; CHECK-NEXT: ret ptr [[A]]
+;
+ %a = call ptr @malloc(i64 %size)
+ call void @capture_address(ptr %a)
+ ret ptr %a
+}
More information about the llvm-commits
mailing list