[clang] [CIR] Add noalias on malloc-like function returns (PR #221817)
David Rivera via cfe-commits
cfe-commits at lists.llvm.org
Mon Sep 7 13:58:56 PDT 2026
https://github.com/RiverDave updated https://github.com/llvm/llvm-project/pull/221817
>From 4a41d7f5c44453dc43e61f7d5a7bdf521f1f8f5c Mon Sep 17 00:00:00 2001
From: David Rivera <davidriverg at gmail.com>
Date: Mon, 7 Sep 2026 15:47:41 -0400
Subject: [PATCH 1/2] [CIR] Add noalias on malloc-like function returns
Mirror classic RestrictAttr handling so __attribute__((malloc)) and
__declspec(restrict) stamp llvm.noalias on the return, matching OGCG.
---
clang/lib/CIR/CodeGen/CIRGenCall.cpp | 12 +++--
clang/test/CIR/CodeGen/restrict-noalias.c | 62 +++++++++++++++++++++--
2 files changed, 68 insertions(+), 6 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index 3a4b7cecf2e08..db17709050018 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -403,9 +403,6 @@ void CIRGenModule::constructAttributeList(
attrs.set(cir::CIRDialect::getSideEffectAttrName(),
cir::SideEffectAttr::get(&getMLIRContext(), sideEffect));
- // TODO(cir): Add noalias to returns for malloc-like functions
- // (__attribute__((malloc)) / __declspec(restrict)).
-
if (targetDecl->hasAttr<ReturnsNonNullAttr>() &&
!codeGenOpts.NullPointerIsValid)
retAttrs.set(mlir::LLVM::LLVMDialect::getNonNullAttrName(),
@@ -665,6 +662,15 @@ void CIRGenModule::constructFunctionReturnAttributes(
getNaturalPointeeTypeAlignment(retTy).getQuantity()));
}
}
+
+ // __attribute__((malloc)) / __declspec(restrict) -> noalias on the return
+ // value. Classic skips the attribute when a deallocator is specified
+ // (malloc(dealloc) / malloc(dealloc, N)).
+ if (const auto *restrictAttr =
+ targetDecl ? targetDecl->getAttr<RestrictAttr>() : nullptr;
+ restrictAttr && restrictAttr->getDeallocator() == nullptr)
+ retAttrs.set(mlir::LLVM::LLVMDialect::getNoAliasAttrName(),
+ mlir::UnitAttr::get(&getMLIRContext()));
}
void CIRGenModule::constructFunctionArgumentAttributes(
diff --git a/clang/test/CIR/CodeGen/restrict-noalias.c b/clang/test/CIR/CodeGen/restrict-noalias.c
index b69de97b07864..2ad1dc391be9c 100644
--- a/clang/test/CIR/CodeGen/restrict-noalias.c
+++ b/clang/test/CIR/CodeGen/restrict-noalias.c
@@ -1,8 +1,8 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-ignored-attributes -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-ignored-attributes -fclangir -emit-llvm %s -o %t-cir.ll
// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-ignored-attributes -emit-llvm %s -o %t.ll
// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
void user_func(int *__restrict p);
@@ -36,3 +36,59 @@ void test_builtin(const char *__restrict fmt) {
// OGCG: define dso_local void @test_builtin(ptr noalias noundef %{{.*}})
// OGCG: call i32 (ptr, ...) @printf(ptr noundef %{{.*}})
+
+__attribute__((malloc)) void *my_malloc(unsigned long n);
+void *test_ret(unsigned long n) { return my_malloc(n); }
+
+// CIR: cir.func {{.*}} @test_ret(%{{.*}}: !u64i {llvm.noundef}
+// CIR: cir.call @my_malloc(%{{.*}}) : (!u64i {llvm.noundef}) -> (!cir.ptr<!void> {llvm.noalias{{.*}}})
+// CIR: cir.func {{.*}} @my_malloc(!u64i {llvm.noundef}) -> (!cir.ptr<!void> {llvm.noalias{{.*}}})
+
+// LLVM: define dso_local {{.*}}ptr @test_ret
+// LLVM: call noalias {{.*}}ptr @my_malloc
+// LLVM: declare noalias {{.*}}ptr @my_malloc
+
+// OGCG: define dso_local {{.*}}ptr @test_ret
+// OGCG: call noalias {{.*}}ptr @my_malloc
+// OGCG: declare noalias {{.*}}ptr @my_malloc
+
+__attribute__((malloc)) void *my_malloc_def(unsigned long n) { return 0; }
+
+// CIR: cir.func {{.*}} @my_malloc_def(%{{.*}}: !u64i {llvm.noundef}
+// CIR-SAME: -> (!cir.ptr<!void> {llvm.noalias{{.*}}})
+
+// LLVM: define dso_local noalias {{.*}}ptr @my_malloc_def
+// OGCG: define dso_local noalias {{.*}}ptr @my_malloc_def
+
+int *Mem;
+void dealloc(int *);
+__attribute__((malloc(dealloc))) int *malloc_with_dealloc(void) { return Mem; }
+__attribute__((malloc(dealloc, 1))) int *malloc_with_dealloc_idx(void) {
+ return Mem;
+}
+
+int *test_malloc_with_dealloc(void) { return malloc_with_dealloc(); }
+
+// CIR-LABEL: cir.func {{.*}} @malloc_with_dealloc
+// CIR-NOT: llvm.noalias
+// CIR-LABEL: cir.func {{.*}} @malloc_with_dealloc_idx
+// CIR-NOT: llvm.noalias
+// CIR-LABEL: cir.func {{.*}} @test_malloc_with_dealloc
+// CIR: cir.call @malloc_with_dealloc() : () -> !cir.ptr<!s32i>
+// CIR-NOT: llvm.noalias
+
+// LLVM: define dso_local {{.*}}ptr @malloc_with_dealloc()
+// LLVM-NOT: noalias
+// LLVM: define dso_local {{.*}}ptr @malloc_with_dealloc_idx()
+// LLVM-NOT: noalias
+// LLVM: define dso_local {{.*}}ptr @test_malloc_with_dealloc()
+// LLVM: call {{.*}}ptr @malloc_with_dealloc()
+// LLVM-NOT: call noalias
+
+// OGCG: define dso_local {{.*}}ptr @malloc_with_dealloc()
+// OGCG-NOT: noalias
+// OGCG: define dso_local {{.*}}ptr @malloc_with_dealloc_idx()
+// OGCG-NOT: noalias
+// OGCG: define dso_local {{.*}}ptr @test_malloc_with_dealloc()
+// OGCG: call {{.*}}ptr @malloc_with_dealloc()
+// OGCG-NOT: call noalias
>From a8894e31bca8775891ed403725f7db1773d5422a Mon Sep 17 00:00:00 2001
From: David Rivera <davidriverg at gmail.com>
Date: Mon, 7 Sep 2026 16:58:43 -0400
Subject: [PATCH 2/2] [CIR] Tighten malloc(dealloc) noalias FileCheck negatives
Unrestricted {{.*}} before ptr can swallow an incorrect return noalias,
so the following -NOT is never tested. Bracket the return-attribute
region instead.
---
clang/test/CIR/CodeGen/restrict-noalias.c | 28 +++++++++++++++--------
1 file changed, 18 insertions(+), 10 deletions(-)
diff --git a/clang/test/CIR/CodeGen/restrict-noalias.c b/clang/test/CIR/CodeGen/restrict-noalias.c
index 2ad1dc391be9c..5e661a2b7e087 100644
--- a/clang/test/CIR/CodeGen/restrict-noalias.c
+++ b/clang/test/CIR/CodeGen/restrict-noalias.c
@@ -77,18 +77,26 @@ int *test_malloc_with_dealloc(void) { return malloc_with_dealloc(); }
// CIR: cir.call @malloc_with_dealloc() : () -> !cir.ptr<!s32i>
// CIR-NOT: llvm.noalias
-// LLVM: define dso_local {{.*}}ptr @malloc_with_dealloc()
+// Bracket the return-attribute region. An unrestricted {{.*}} before ptr would
+// let FileCheck consume an incorrect noalias and still satisfy the -NOT.
+// LLVM: define dso_local
// LLVM-NOT: noalias
-// LLVM: define dso_local {{.*}}ptr @malloc_with_dealloc_idx()
+// LLVM-SAME: ptr @malloc_with_dealloc()
+// LLVM: define dso_local
// LLVM-NOT: noalias
-// LLVM: define dso_local {{.*}}ptr @test_malloc_with_dealloc()
-// LLVM: call {{.*}}ptr @malloc_with_dealloc()
-// LLVM-NOT: call noalias
+// LLVM-SAME: ptr @malloc_with_dealloc_idx()
+// LLVM: define {{.*}} @test_malloc_with_dealloc()
+// LLVM: call
+// LLVM-NOT: noalias
+// LLVM-SAME: ptr @malloc_with_dealloc()
-// OGCG: define dso_local {{.*}}ptr @malloc_with_dealloc()
+// OGCG: define dso_local
+// OGCG-NOT: noalias
+// OGCG-SAME: ptr @malloc_with_dealloc()
+// OGCG: define dso_local
// OGCG-NOT: noalias
-// OGCG: define dso_local {{.*}}ptr @malloc_with_dealloc_idx()
+// OGCG-SAME: ptr @malloc_with_dealloc_idx()
+// OGCG: define {{.*}} @test_malloc_with_dealloc()
+// OGCG: call
// OGCG-NOT: noalias
-// OGCG: define dso_local {{.*}}ptr @test_malloc_with_dealloc()
-// OGCG: call {{.*}}ptr @malloc_with_dealloc()
-// OGCG-NOT: call noalias
+// OGCG-SAME: ptr @malloc_with_dealloc()
More information about the cfe-commits
mailing list