https://github.com/RiverDave created https://github.com/llvm/llvm-project/pull/221817
## Summary - Stamp `llvm.noalias` on function returns when the callee has `RestrictAttr` (`__attribute__((malloc))` / `__declspec(restrict)`) and no deallocator. - Matches classic codegen in `CGCall.cpp`: return `noalias` on both `define` and `call`, and skip `malloc(dealloc)`. - Extends `restrict-noalias.c` with a malloc call, a malloc definition, and a `malloc(dealloc)` negative. ## Test plan - [x] `llvm-lit clang/test/CIR/CodeGen/restrict-noalias.c` - [ ] Pre-merge Clang CIR checks Made with [Cursor](https://cursor.com) >From 4a41d7f5c44453dc43e61f7d5a7bdf521f1f8f5c Mon Sep 17 00:00:00 2001 From: David Rivera <[email protected]> Date: Mon, 7 Sep 2026 15:47:41 -0400 Subject: [PATCH] [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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
