[clang] [CIR][ABI] Add restrict, nonnull, and ReturnsNonNull attributes (PR #188281)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Apr 15 10:52:48 PDT 2026
https://github.com/adams381 updated https://github.com/llvm/llvm-project/pull/188281
>From 3f4cc6fa06636f8cae680c791521c47486d9310d Mon Sep 17 00:00:00 2001
From: Adam Smith <adams at nvidia.com>
Date: Tue, 24 Mar 2026 09:45:34 -0700
Subject: [PATCH 1/5] [CIR][ABI] Add restrict, nonnull, and ReturnsNonNull
attributes
Add noalias for restrict-qualified pointer parameters, nonnull for
__attribute__((nonnull)) pointer parameters, and nonnull on return
values for functions with ReturnsNonNullAttr (e.g. operator new).
Pass targetDecl to constructFunctionArgumentAttributes so it can
access ParmVarDecl for source-level qualifiers and attributes that
are not on the canonical QualType.
Made-with: Cursor
---
clang/lib/CIR/CodeGen/CIRGenCall.cpp | 44 +++++++++++++--
clang/lib/CIR/CodeGen/CIRGenModule.h | 3 +-
clang/test/CIR/CodeGen/coro-task.cpp | 2 +-
clang/test/CIR/CodeGen/new-delete.cpp | 4 +-
clang/test/CIR/CodeGen/new.cpp | 56 +++++++++----------
clang/test/CIR/CodeGen/restrict-nonnull.c | 29 ++++++++++
.../test/CIR/CodeGenBuiltins/builtin-call.cpp | 4 +-
.../CodeGenBuiltins/builtin-new-delete.cpp | 8 +--
.../CIR/CodeGenBuiltins/builtin-printf.cpp | 12 ++--
9 files changed, 112 insertions(+), 50 deletions(-)
create mode 100644 clang/test/CIR/CodeGen/restrict-nonnull.c
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index 10b4528ff2aac..cbbb5827b0adc 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -389,8 +389,13 @@ void CIRGenModule::constructAttributeList(
attrs.set(cir::CIRDialect::getSideEffectAttrName(),
cir::SideEffectAttr::get(&getMLIRContext(), sideEffect));
- // TODO(cir): When doing 'return attrs' we need to cover the Restrict and
- // ReturnsNonNull attributes here.
+ if (targetDecl->hasAttr<ReturnsNonNullAttr>())
+ retAttrs.set(mlir::LLVM::LLVMDialect::getNonNullAttrName(),
+ mlir::UnitAttr::get(&getMLIRContext()));
+
+ // TODO(cir): Add noalias to returns for malloc-like functions
+ // (__attribute__((malloc)) / __declspec(restrict)).
+
if (targetDecl->hasAttr<AnyX86NoCallerSavedRegistersAttr>())
addUnitAttr(cir::CIRDialect::getNoCallerSavedRegsAttrName());
// TODO(cir): Implement 'NoCFCheck' attribute here. This requires
@@ -482,7 +487,7 @@ void CIRGenModule::constructAttributeList(
// TODO(cir): Add loader-replaceable attribute here.
constructFunctionReturnAttributes(info, targetDecl, isThunk, retAttrs);
- constructFunctionArgumentAttributes(info, isThunk, argAttrs);
+ constructFunctionArgumentAttributes(info, targetDecl, isThunk, argAttrs);
}
bool CIRGenModule::hasStrictReturn(QualType retTy, const Decl *targetDecl) {
@@ -618,13 +623,13 @@ void CIRGenModule::constructFunctionReturnAttributes(
}
void CIRGenModule::constructFunctionArgumentAttributes(
- const CIRGenFunctionInfo &info, bool isThunk,
+ const CIRGenFunctionInfo &info, const Decl *targetDecl, bool isThunk,
llvm::MutableArrayRef<mlir::NamedAttrList> argAttrs) {
assert(!cir::MissingFeatures::abiArgInfo());
// TODO(cir): classic codegen does a lot of work here based on the ABIArgInfo
// to set things based on calling convention.
- // At the moment, only nonnull, dereferenceable, align, and noundef are being
- // implemented here, using similar logic to how we do so for return types.
+
+ const auto *fd = dyn_cast_or_null<FunctionDecl>(targetDecl);
if (info.isInstanceMethod() && !isThunk) {
QualType thisPtrTy = info.arguments()[0];
@@ -700,6 +705,33 @@ void CIRGenModule::constructFunctionArgumentAttributes(
builder.getI64IntegerAttr(
getNaturalPointeeTypeAlignment(argType).getQuantity()));
}
+
+ // Source-level parameter attributes (restrict, nonnull). These
+ // require the FunctionDecl to access ParmVarDecl info.
+ if (fd) {
+ unsigned paramIdx = &argAttrList - argAttrs.data();
+ unsigned srcIdx = info.isInstanceMethod() ? paramIdx - 1 : paramIdx;
+ if (srcIdx < fd->getNumParams()) {
+ const ParmVarDecl *pvd = fd->getParamDecl(srcIdx);
+ QualType pvdType = pvd->getType();
+
+ // restrict on pointer parameters → noalias.
+ if (pvdType->isPointerType() && pvdType.isRestrictQualified())
+ argAttrList.set(mlir::LLVM::LLVMDialect::getNoAliasAttrName(),
+ mlir::UnitAttr::get(&getMLIRContext()));
+
+ // __attribute__((nonnull)) on pointer parameters.
+ if (pvdType->isPointerType() && !codeGenOpts.NullPointerIsValid) {
+ bool hasNonnull = pvd->hasAttr<NonNullAttr>();
+ if (!hasNonnull)
+ if (const auto *nna = fd->getAttr<NonNullAttr>())
+ hasNonnull = nna->isNonNull(srcIdx);
+ if (hasNonnull)
+ argAttrList.set(mlir::LLVM::LLVMDialect::getNonNullAttrName(),
+ mlir::UnitAttr::get(&getMLIRContext()));
+ }
+ }
+ }
}
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index ba3f936106d31..206d6c0a99924 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -120,7 +120,8 @@ class CIRGenModule : public CIRGenTypeCache {
mlir::NamedAttrList &retAttrs);
/// A helper for constructAttributeList that handles argument attributes.
void constructFunctionArgumentAttributes(
- const CIRGenFunctionInfo &info, bool isThunk,
+ const CIRGenFunctionInfo &info, const clang::Decl *targetDecl,
+ bool isThunk,
llvm::MutableArrayRef<mlir::NamedAttrList> argAttrs);
/// A helper function for constructAttributeList that determines whether a
/// return value might have been discarded.
diff --git a/clang/test/CIR/CodeGen/coro-task.cpp b/clang/test/CIR/CodeGen/coro-task.cpp
index 568dedf2c7921..2ec7406a5768d 100644
--- a/clang/test/CIR/CodeGen/coro-task.cpp
+++ b/clang/test/CIR/CodeGen/coro-task.cpp
@@ -176,7 +176,7 @@ VoidTask silly_task() {
// CIR: cir.store{{.*}} %[[NullPtr]], %[[SavedFrameAddr]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
// CIR: cir.if %[[ShouldAlloc]] {
// CIR: %[[CoroSize:.*]] = cir.call @__builtin_coro_size() : () -> (!u64i {llvm.noundef})
-// CIR: %[[AllocAddr:.*]] = cir.call @_Znwm(%[[CoroSize]]) {allocsize = array<i32: 0>} : (!u64i {llvm.noundef}) -> (!cir.ptr<!void> {llvm.noundef})
+// CIR: %[[AllocAddr:.*]] = cir.call @_Znwm(%[[CoroSize]]) {allocsize = array<i32: 0>} : (!u64i {llvm.noundef}) -> (!cir.ptr<!void> {llvm.nonnull, llvm.noundef})
// CIR: cir.store{{.*}} %[[AllocAddr]], %[[SavedFrameAddr]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
// CIR: }
// CIR: %[[Load0:.*]] = cir.load{{.*}} %[[SavedFrameAddr]] : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
diff --git a/clang/test/CIR/CodeGen/new-delete.cpp b/clang/test/CIR/CodeGen/new-delete.cpp
index d0c8c7d851c70..c0cb843d3b68b 100644
--- a/clang/test/CIR/CodeGen/new-delete.cpp
+++ b/clang/test/CIR/CodeGen/new-delete.cpp
@@ -31,7 +31,7 @@ A *a() {
// LLVM: define {{.*}} ptr @_Z1av() {{.*}} personality ptr @__gxx_personality_v0 {
// LLVM: %[[RETVAL:.*]] = alloca ptr
// LLVM: %[[NEW_RESULT:.*]] = alloca ptr
-// LLVM: %[[PTR:.*]] = call ptr @_Znwm(i64 8) #[[ATTR_BUILTIN_NEW:.*]]
+// LLVM: %[[PTR:.*]] = call nonnull ptr @_Znwm(i64 8) #[[ATTR_BUILTIN_NEW:.*]]
// LLVM: br label %[[EH_SCOPE:.*]]
// LLVM: [[EH_SCOPE]]:
// LLVM: store ptr %[[PTR]], ptr %[[NEW_RESULT]]
@@ -106,7 +106,7 @@ A *b() {
// LLVM: define {{.*}} ptr @_Z1bv() {{.*}} personality ptr @__gxx_personality_v0 {
// LLVM: %[[RETVAL:.*]] = alloca ptr
// LLVM: %[[NEW_RESULT:.*]] = alloca ptr
-// LLVM: %[[PTR:.*]] = call ptr @_Znwm(i64 8) #[[ATTR_BUILTIN_NEW]]
+// LLVM: %[[PTR:.*]] = call nonnull ptr @_Znwm(i64 8) #[[ATTR_BUILTIN_NEW]]
// LLVM: br label %[[EH_SCOPE:.*]]
// LLVM: [[EH_SCOPE]]:
// LLVM: store ptr %[[PTR]], ptr %[[NEW_RESULT]]
diff --git a/clang/test/CIR/CodeGen/new.cpp b/clang/test/CIR/CodeGen/new.cpp
index 503b395244bd8..452d7247f66dc 100644
--- a/clang/test/CIR/CodeGen/new.cpp
+++ b/clang/test/CIR/CodeGen/new.cpp
@@ -163,7 +163,7 @@ void test_new_with_complex_type() {
// CHECK: cir.func{{.*}} @_Z26test_new_with_complex_typev
// CHECK: %[[A_ADDR:.*]] = cir.alloca !cir.ptr<!cir.complex<!cir.float>>, !cir.ptr<!cir.ptr<!cir.complex<!cir.float>>>, ["a", init]
// CHECK: %[[COMPLEX_SIZE:.*]] = cir.const #cir.int<8> : !u64i
-// CHECK: %[[NEW_COMPLEX:.*]] = cir.call @_Znwm(%[[COMPLEX_SIZE]]) {allocsize = array<i32: 0>, builtin} : (!u64i {llvm.noundef}) -> (!cir.ptr<!void> {llvm.noundef})
+// CHECK: %[[NEW_COMPLEX:.*]] = cir.call @_Znwm(%[[COMPLEX_SIZE]]) {allocsize = array<i32: 0>, builtin} : (!u64i {llvm.noundef}) -> (!cir.ptr<!void> {llvm.nonnull, llvm.noundef})
// CHECK: %[[COMPLEX_PTR:.*]] = cir.cast bitcast %[[NEW_COMPLEX]] : !cir.ptr<!void> -> !cir.ptr<!cir.complex<!cir.float>>
// CHECK: %[[COMPLEX_VAL:.*]] = cir.const #cir.const_complex<#cir.fp<1.000000e+00> : !cir.float, #cir.fp<2.000000e+00> : !cir.float> : !cir.complex<!cir.float>
// CHECK: cir.store{{.*}} %[[COMPLEX_VAL]], %[[COMPLEX_PTR]] : !cir.complex<!cir.float>, !cir.ptr<!cir.complex<!cir.float>>
@@ -171,7 +171,7 @@ void test_new_with_complex_type() {
// LLVM: define{{.*}} void @_Z26test_new_with_complex_typev
// LLVM: %[[A_ADDR:.*]] = alloca ptr, i64 1, align 8
-// LLVM: %[[NEW_COMPLEX:.*]] = call noundef ptr @_Znwm(i64 noundef 8)
+// LLVM: %[[NEW_COMPLEX:.*]] = call noundef nonnull ptr @_Znwm(i64 noundef 8)
// LLVM: store { float, float } { float 1.000000e+00, float 2.000000e+00 }, ptr %[[NEW_COMPLEX]], align 8
// LLVM: store ptr %[[NEW_COMPLEX]], ptr %[[A_ADDR]], align 8
@@ -191,7 +191,7 @@ void t_new_constant_size() {
// CHECK: cir.func{{.*}} @_Z19t_new_constant_sizev()
// CHECK: %[[P_ADDR:.*]] = cir.alloca !cir.ptr<!cir.double>, !cir.ptr<!cir.ptr<!cir.double>>, ["p", init] {alignment = 8 : i64}
// CHECK: %[[ALLOCATION_SIZE:.*]] = cir.const #cir.int<128> : !u64i
-// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) {allocsize = array<i32: 0>, builtin} : (!u64i {llvm.noundef}) -> (!cir.ptr<!void> {llvm.noundef})
+// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) {allocsize = array<i32: 0>, builtin} : (!u64i {llvm.noundef}) -> (!cir.ptr<!void> {llvm.nonnull, llvm.noundef})
// CHECK: %[[TYPED_PTR:.*]] = cir.cast bitcast %[[RAW_PTR]] : !cir.ptr<!void> -> !cir.ptr<!cir.double>
// CHECK: cir.store align(8) %[[TYPED_PTR]], %[[P_ADDR]] : !cir.ptr<!cir.double>, !cir.ptr<!cir.ptr<!cir.double>>
// CHECK: cir.return
@@ -199,7 +199,7 @@ void t_new_constant_size() {
// LLVM: define{{.*}} void @_Z19t_new_constant_sizev
// LLVM: %[[P_ADDR:.*]] = alloca ptr, i64 1, align 8
-// LLVM: %[[CALL:.*]] = call noundef ptr @_Znam(i64 noundef 128)
+// LLVM: %[[CALL:.*]] = call noundef nonnull ptr @_Znam(i64 noundef 128)
// LLVM: store ptr %[[CALL]], ptr %[[P_ADDR]], align 8
// OGCG: define{{.*}} void @_Z19t_new_constant_sizev
@@ -220,7 +220,7 @@ void t_constant_size_nontrivial() {
// CHECK: %[[P_ADDR:.*]] = cir.alloca !cir.ptr<!rec_C>, !cir.ptr<!cir.ptr<!rec_C>>, ["p", init] {alignment = 8 : i64}
// CHECK: %[[NUM_ELEMENTS:.*]] = cir.const #cir.int<3> : !u64i
// CHECK: %[[ALLOCATION_SIZE:.*]] = cir.const #cir.int<11> : !u64i
-// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) {allocsize = array<i32: 0>, builtin} : (!u64i {llvm.noundef}) -> (!cir.ptr<!void> {llvm.noundef})
+// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) {allocsize = array<i32: 0>, builtin} : (!u64i {llvm.noundef}) -> (!cir.ptr<!void> {llvm.nonnull, llvm.noundef})
// CHECK: %[[COOKIE_PTR_BASE:.*]] = cir.cast bitcast %[[RAW_PTR]] : !cir.ptr<!void> -> !cir.ptr<!cir.ptr<!u8i>>
// CHECK: %[[COOKIE_PTR:.*]] = cir.cast bitcast %[[COOKIE_PTR_BASE]] : !cir.ptr<!cir.ptr<!u8i>> -> !cir.ptr<!u64i>
// CHECK: cir.store align(8) %[[NUM_ELEMENTS]], %[[COOKIE_PTR]] : !u64i, !cir.ptr<!u64i>
@@ -234,7 +234,7 @@ void t_constant_size_nontrivial() {
// LLVM: @_Z26t_constant_size_nontrivialv()
// LLVM: %[[ALLOCA:.*]] = alloca ptr, i64 1, align 8
-// LLVM: %[[COOKIE_PTR:.*]] = call noundef ptr @_Znam(i64 noundef 11)
+// LLVM: %[[COOKIE_PTR:.*]] = call noundef nonnull ptr @_Znam(i64 noundef 11)
// LLVM: store i64 3, ptr %[[COOKIE_PTR]], align 8
// LLVM: %[[ALLOCATED_PTR:.*]] = getelementptr ptr, ptr %[[COOKIE_PTR]], i64 8
// LLVM: store ptr %[[ALLOCATED_PTR]], ptr %[[ALLOCA]], align 8
@@ -260,7 +260,7 @@ void t_constant_size_nontrivial2() {
// CHECK: %[[P_ADDR:.*]] = cir.alloca !cir.ptr<!rec_D>, !cir.ptr<!cir.ptr<!rec_D>>, ["p", init] {alignment = 8 : i64}
// CHECK: %[[NUM_ELEMENTS:.*]] = cir.const #cir.int<3> : !u64i
// CHECK: %[[ALLOCATION_SIZE:.*]] = cir.const #cir.int<20> : !u64i
-// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) {allocsize = array<i32: 0>, builtin} : (!u64i {llvm.noundef}) -> (!cir.ptr<!void> {llvm.noundef})
+// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) {allocsize = array<i32: 0>, builtin} : (!u64i {llvm.noundef}) -> (!cir.ptr<!void> {llvm.nonnull, llvm.noundef})
// CHECK: %[[COOKIE_PTR_BASE:.*]] = cir.cast bitcast %[[RAW_PTR]] : !cir.ptr<!void> -> !cir.ptr<!cir.ptr<!u8i>>
// CHECK: %[[COOKIE_PTR:.*]] = cir.cast bitcast %[[COOKIE_PTR_BASE]] : !cir.ptr<!cir.ptr<!u8i>> -> !cir.ptr<!u64i>
// CHECK: cir.store align(8) %[[NUM_ELEMENTS]], %[[COOKIE_PTR]] : !u64i, !cir.ptr<!u64i>
@@ -274,7 +274,7 @@ void t_constant_size_nontrivial2() {
// LLVM: @_Z27t_constant_size_nontrivial2v()
// LLVM: %[[ALLOCA:.*]] = alloca ptr, i64 1, align 8
-// LLVM: %[[COOKIE_PTR:.*]] = call noundef ptr @_Znam(i64 noundef 20)
+// LLVM: %[[COOKIE_PTR:.*]] = call noundef nonnull ptr @_Znam(i64 noundef 20)
// LLVM: store i64 3, ptr %[[COOKIE_PTR]], align 8
// LLVM: %[[ALLOCATED_PTR:.*]] = getelementptr ptr, ptr %[[COOKIE_PTR]], i64 8
// LLVM: store ptr %[[ALLOCATED_PTR]], ptr %[[ALLOCA]], align 8
@@ -292,7 +292,7 @@ void t_align16_nontrivial() {
// CHECK: %[[P_ADDR:.*]] = cir.alloca !cir.ptr<!rec_E>, !cir.ptr<!cir.ptr<!rec_E>>, ["p", init] {alignment = 8 : i64}
// CHECK: %[[NUM_ELEMENTS:.*]] = cir.const #cir.int<2> : !u64i
// CHECK: %[[ALLOCATION_SIZE:.*]] = cir.const #cir.int<48> : !u64i
-// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) {allocsize = array<i32: 0>, builtin} : (!u64i {llvm.noundef}) -> (!cir.ptr<!void> {llvm.noundef})
+// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) {allocsize = array<i32: 0>, builtin} : (!u64i {llvm.noundef}) -> (!cir.ptr<!void> {llvm.nonnull, llvm.noundef})
// CHECK: %[[COOKIE_PTR_BASE:.*]] = cir.cast bitcast %[[RAW_PTR]] : !cir.ptr<!void> -> !cir.ptr<!cir.ptr<!u8i>>
// CHECK: %[[COOKIE_OFFSET:.*]] = cir.const #cir.int<8> : !s32i
// CHECK: %[[COOKIE_PTR_RAW:.*]] = cir.ptr_stride %[[COOKIE_PTR_BASE]], %[[COOKIE_OFFSET]] : (!cir.ptr<!cir.ptr<!u8i>>, !s32i) -> !cir.ptr<!cir.ptr<!u8i>>
@@ -308,7 +308,7 @@ void t_align16_nontrivial() {
// LLVM: @_Z20t_align16_nontrivialv()
// LLVM: %[[ALLOCA:.*]] = alloca ptr, i64 1, align 8
-// LLVM: %[[RAW_PTR:.*]] = call noundef ptr @_Znam(i64 noundef 48)
+// LLVM: %[[RAW_PTR:.*]] = call noundef nonnull ptr @_Znam(i64 noundef 48)
// LLVM: %[[COOKIE_PTR:.*]] = getelementptr ptr, ptr %[[RAW_PTR]], i64 8
// LLVM: store i64 2, ptr %[[COOKIE_PTR]], align 8
// LLVM: %[[ALLOCATED_PTR:.*]] = getelementptr ptr, ptr %[[RAW_PTR]], i64 16
@@ -330,14 +330,14 @@ void t_new_multidim_constant_size() {
// CHECK: cir.func{{.*}} @_Z28t_new_multidim_constant_sizev()
// CHECK: %[[P_ADDR:.*]] = cir.alloca !cir.ptr<!cir.array<!cir.array<!cir.double x 4> x 3>>, !cir.ptr<!cir.ptr<!cir.array<!cir.array<!cir.double x 4> x 3>>>, ["p", init] {alignment = 8 : i64}
// CHECK: %[[ALLOCATION_SIZE:.*]] = cir.const #cir.int<192> : !u64i
-// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) {allocsize = array<i32: 0>, builtin} : (!u64i {llvm.noundef}) -> (!cir.ptr<!void> {llvm.noundef})
+// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) {allocsize = array<i32: 0>, builtin} : (!u64i {llvm.noundef}) -> (!cir.ptr<!void> {llvm.nonnull, llvm.noundef})
// CHECK: %[[TYPED_PTR:.*]] = cir.cast bitcast %[[RAW_PTR]] : !cir.ptr<!void> -> !cir.ptr<!cir.array<!cir.array<!cir.double x 4> x 3>>
// CHECK: cir.store align(8) %[[TYPED_PTR]], %[[P_ADDR]] : !cir.ptr<!cir.array<!cir.array<!cir.double x 4> x 3>>, !cir.ptr<!cir.ptr<!cir.array<!cir.array<!cir.double x 4> x 3>>>
// CHECK: }
// LLVM: define{{.*}} void @_Z28t_new_multidim_constant_sizev
// LLVM: %[[P_ADDR:.*]] = alloca ptr, i64 1, align 8
-// LLVM: %[[CALL:.*]] = call noundef ptr @_Znam(i64 noundef 192)
+// LLVM: %[[CALL:.*]] = call noundef nonnull ptr @_Znam(i64 noundef 192)
// LLVM: store ptr %[[CALL]], ptr %[[P_ADDR]], align 8
// OGCG: define{{.*}} void @_Z28t_new_multidim_constant_sizev
@@ -351,14 +351,14 @@ void t_constant_size_memset_init() {
// CHECK: cir.func {{.*}} @_Z27t_constant_size_memset_initv()
// CHECK: %[[ALLOCATION_SIZE:.*]] = cir.const #cir.int<64> : !u64i
-// CHECK: %[[ALLOC_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) {allocsize = array<i32: 0>, builtin} : (!u64i {llvm.noundef}) -> (!cir.ptr<!void> {llvm.noundef})
+// CHECK: %[[ALLOC_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) {allocsize = array<i32: 0>, builtin} : (!u64i {llvm.noundef}) -> (!cir.ptr<!void> {llvm.nonnull, llvm.noundef})
// CHECK: %[[ELEM_PTR:.*]] = cir.cast bitcast %[[ALLOC_PTR]] : !cir.ptr<!void> -> !cir.ptr<!s32i>
// CHECK: %[[VOID_PTR:.*]] = cir.cast bitcast %[[ELEM_PTR]] : !cir.ptr<!s32i> -> !cir.ptr<!void>
// CHECK: %[[ZERO:.*]] = cir.const #cir.int<0> : !u8i
// CHECK: cir.libc.memset %[[ALLOCATION_SIZE]] bytes at %[[VOID_PTR]] to %[[ZERO]] : !cir.ptr<!void>, !u8i, !u64i
// LLVM: define {{.*}} void @_Z27t_constant_size_memset_initv()
-// LLVM: %[[P:.*]] = call noundef ptr @_Znam(i64 noundef 64)
+// LLVM: %[[P:.*]] = call noundef nonnull ptr @_Znam(i64 noundef 64)
// LLVM: call void @llvm.memset.p0.i64(ptr %[[P]], i8 0, i64 64, i1 false)
// OGCG: define {{.*}} void @_Z27t_constant_size_memset_initv()
@@ -371,7 +371,7 @@ void t_constant_size_full_init() {
// CHECK: cir.func {{.*}} @_Z25t_constant_size_full_initv()
// CHECK: %[[ALLOCATION_SIZE:.*]] = cir.const #cir.int<16> : !u64i
-// CHECK: %[[ALLOC_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) {allocsize = array<i32: 0>, builtin} : (!u64i {llvm.noundef}) -> (!cir.ptr<!void> {llvm.noundef})
+// CHECK: %[[ALLOC_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) {allocsize = array<i32: 0>, builtin} : (!u64i {llvm.noundef}) -> (!cir.ptr<!void> {llvm.nonnull, llvm.noundef})
// CHECK: %[[ELEM_0_PTR:.*]] = cir.cast bitcast %[[ALLOC_PTR]] : !cir.ptr<!void> -> !cir.ptr<!s32i>
// CHECK: %[[CONST_ONE:.*]] = cir.const #cir.int<1> : !s32i
// CHECK: cir.store{{.*}} %[[CONST_ONE]], %[[ELEM_0_PTR]] : !s32i, !cir.ptr<!s32i>
@@ -389,7 +389,7 @@ void t_constant_size_full_init() {
// CHECK: cir.store{{.*}} %[[CONST_FOUR]], %[[ELEM_3_PTR]] : !s32i, !cir.ptr<!s32i>
// LLVM: define {{.*}} void @_Z25t_constant_size_full_initv()
-// LLVM: %[[P:.*]] = call noundef ptr @_Znam(i64 noundef 16)
+// LLVM: %[[P:.*]] = call noundef nonnull ptr @_Znam(i64 noundef 16)
// LLVM: store i32 1, ptr %[[CALL]]
// LLVM: %[[ELEM_1:.*]] = getelementptr i32, ptr %[[P]], i64 1
// LLVM: store i32 2, ptr %[[ELEM_1]]
@@ -414,7 +414,7 @@ void t_constant_size_partial_init() {
// CHECK: cir.func {{.*}} @_Z28t_constant_size_partial_initv()
// CHECK: %[[ALLOCATION_SIZE:.*]] = cir.const #cir.int<64> : !u64i
-// CHECK: %[[ALLOC_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) {allocsize = array<i32: 0>, builtin} : (!u64i {llvm.noundef}) -> (!cir.ptr<!void> {llvm.noundef})
+// CHECK: %[[ALLOC_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) {allocsize = array<i32: 0>, builtin} : (!u64i {llvm.noundef}) -> (!cir.ptr<!void> {llvm.nonnull, llvm.noundef})
// CHECK: %[[ELEM_0_PTR:.*]] = cir.cast bitcast %[[ALLOC_PTR]] : !cir.ptr<!void> -> !cir.ptr<!s32i>
// CHECK: %[[CONST_ONE:.*]] = cir.const #cir.int<1> : !s32i
// CHECK: cir.store{{.*}} %[[CONST_ONE]], %[[ELEM_0_PTR]] : !s32i, !cir.ptr<!s32i>
@@ -435,7 +435,7 @@ void t_constant_size_partial_init() {
// CHECK: cir.libc.memset %[[REMAINING_SIZE]] bytes at %[[VOID_PTR]] to %[[ZERO]] : !cir.ptr<!void>, !u8i, !u64i
// LLVM: define {{.*}} void @_Z28t_constant_size_partial_initv()
-// LLVM: %[[P:.*]] = call noundef ptr @_Znam(i64 {{.*}} 64)
+// LLVM: %[[P:.*]] = call noundef nonnull ptr @_Znam(i64 {{.*}} 64)
// LLVM: store i32 1, ptr %[[P]]
// LLVM: %[[ELEM_1:.*]] = getelementptr i32, ptr %[[P]], i64 1
// LLVM: store i32 2, ptr %[[ELEM_1]]
@@ -464,7 +464,7 @@ void t_new_var_size(size_t n) {
// LLVM: define{{.*}} void @_Z14t_new_var_sizem
// LLVM: %[[N:.*]] = load i64, ptr %{{.+}}
-// LLVM: %[[PTR:.*]] = call noundef ptr @_Znam(i64 {{.*}} %[[N]])
+// LLVM: %[[PTR:.*]] = call noundef nonnull ptr @_Znam(i64 {{.*}} %[[N]])
// OGCG: define{{.*}} void @_Z14t_new_var_sizem
// OGCG: %[[N:.*]] = load i64, ptr %{{.+}}
@@ -482,7 +482,7 @@ void t_new_var_size2(int n) {
// LLVM: define{{.*}} void @_Z15t_new_var_size2i
// LLVM: %[[N:.*]] = load i32, ptr %{{.+}}
// LLVM: %[[N_SIZE_T:.*]] = sext i32 %[[N]] to i64
-// LLVM: %[[PTR:.*]] = call noundef ptr @_Znam(i64 {{.*}} %[[N_SIZE_T]])
+// LLVM: %[[PTR:.*]] = call noundef nonnull ptr @_Znam(i64 {{.*}} %[[N_SIZE_T]])
// OGCG: define{{.*}} void @_Z15t_new_var_size2i
// OGCG: %[[N:.*]] = load i32, ptr %{{.+}}
@@ -507,7 +507,7 @@ void t_new_var_size3(size_t n) {
// LLVM: %[[ELEMENT_SIZE:.*]] = extractvalue { i64, i1 } %[[MUL_OVERFLOW]], 0
// LLVM: %[[OVERFLOW:.*]] = extractvalue { i64, i1 } %[[MUL_OVERFLOW]], 1
// LLVM: %[[ALLOC_SIZE:.*]] = select i1 %[[OVERFLOW]], i64 -1, i64 %[[ELEMENT_SIZE]]
-// LLVM: %[[RESULT:.*]] = call noundef ptr @_Znam(i64 {{.*}} %[[ALLOC_SIZE]])
+// LLVM: %[[RESULT:.*]] = call noundef nonnull ptr @_Znam(i64 {{.*}} %[[ALLOC_SIZE]])
// OGCG: define{{.*}} void @_Z15t_new_var_size3m
// OGCG: %[[N:.*]] = load i64, ptr %{{.+}}
@@ -537,7 +537,7 @@ void t_new_var_size4(int n) {
// LLVM: %[[ELEMENT_SIZE:.*]] = extractvalue { i64, i1 } %[[MUL_OVERFLOW]], 0
// LLVM: %[[OVERFLOW:.*]] = extractvalue { i64, i1 } %[[MUL_OVERFLOW]], 1
// LLVM: %[[ALLOC_SIZE:.*]] = select i1 %[[OVERFLOW]], i64 -1, i64 %[[ELEMENT_SIZE]]
-// LLVM: %[[PTR:.*]] = call noundef ptr @_Znam(i64 {{.*}} %[[ALLOC_SIZE]])
+// LLVM: %[[PTR:.*]] = call noundef nonnull ptr @_Znam(i64 {{.*}} %[[ALLOC_SIZE]])
// OGCG: define{{.*}} void @_Z15t_new_var_size4i
// OGCG: %[[N:.*]] = load i32, ptr %{{.+}}
@@ -571,7 +571,7 @@ void t_new_var_size5(int n) {
// LLVM: %[[ELEMENT_SIZE:.*]] = extractvalue { i64, i1 } %[[MUL_OVERFLOW]], 0
// LLVM: %[[OVERFLOW:.*]] = extractvalue { i64, i1 } %[[MUL_OVERFLOW]], 1
// LLVM: %[[ALLOC_SIZE:.*]] = select i1 %[[OVERFLOW]], i64 -1, i64 %[[ELEMENT_SIZE]]
-// LLVM: %[[PTR:.*]] = call noundef ptr @_Znam(i64 {{.*}} %[[ALLOC_SIZE]])
+// LLVM: %[[PTR:.*]] = call noundef nonnull ptr @_Znam(i64 {{.*}} %[[ALLOC_SIZE]])
// OGCG: define{{.*}} void @_Z15t_new_var_size5i
// OGCG: %[[N:.*]] = load i32, ptr %{{.+}}
@@ -625,7 +625,7 @@ void t_new_var_size6(int n) {
// LLVM: %[[OVERFLOW:.*]] = extractvalue { i64, i1 } %[[MUL_OVERFLOW]], 1
// LLVM: %[[ANY_OVERFLOW:.*]] = or i1 %[[LT_MIN_SIZE]], %[[OVERFLOW]]
// LLVM: %[[ALLOC_SIZE:.*]] = select i1 %[[ANY_OVERFLOW]], i64 -1, i64 %[[ELEMENT_SIZE]]
-// LLVM: %[[PTR:.*]] = call noundef ptr @_Znam(i64 {{.*}} %[[ALLOC_SIZE]])
+// LLVM: %[[PTR:.*]] = call noundef nonnull ptr @_Znam(i64 {{.*}} %[[ALLOC_SIZE]])
// LLVM: store double 1.000000e+00, ptr %[[PTR]], align 8
// LLVM: %[[ELEM_1:.*]] = getelementptr double, ptr %[[PTR]], i64 1
// LLVM: store double 2.000000e+00, ptr %[[ELEM_1]], align 8
@@ -674,7 +674,7 @@ void t_new_var_size7(__int128 n) {
// LLVM: %[[ELEMENT_SIZE:.*]] = extractvalue { i64, i1 } %[[MUL_OVERFLOW]], 0
// LLVM: %[[OVERFLOW:.*]] = extractvalue { i64, i1 } %[[MUL_OVERFLOW]], 1
// LLVM: %[[ALLOC_SIZE:.*]] = select i1 %[[OVERFLOW]], i64 -1, i64 %[[ELEMENT_SIZE]]
-// LLVM: %[[PTR:.*]] = call noundef ptr @_Znam(i64 {{.*}} %[[ALLOC_SIZE]])
+// LLVM: %[[PTR:.*]] = call noundef nonnull ptr @_Znam(i64 {{.*}} %[[ALLOC_SIZE]])
// OGCG: define{{.*}} void @_Z15t_new_var_size7n
// OGCG: %[[N:.*]] = load i128, ptr %{{.+}}
@@ -710,7 +710,7 @@ void t_new_var_size_nontrivial(size_t n) {
// LLVM: %[[OVERFLOW_ADD:.*]] = extractvalue { i64, i1 } %[[ADD_OVERFLOW]], 1
// LLVM: %[[ANY_OVERFLOW:.*]] = or i1 %[[OVERFLOW]], %[[OVERFLOW_ADD]]
// LLVM: %[[ALLOC_SIZE:.*]] = select i1 %[[ANY_OVERFLOW]], i64 -1, i64 %[[ELEMENT_SIZE]]
-// LLVM: %[[PTR:.*]] = call noundef ptr @_Znam(i64 {{.*}} %[[ALLOC_SIZE]])
+// LLVM: %[[PTR:.*]] = call noundef nonnull ptr @_Znam(i64 {{.*}} %[[ALLOC_SIZE]])
// OGCG: define{{.*}} void @_Z25t_new_var_size_nontrivialm
// OGCG: %[[N:.*]] = load i64, ptr %{{.+}}
@@ -774,7 +774,7 @@ void test_array_new_with_ctor_init() {
// LLVM: define{{.*}} void @_Z29test_array_new_with_ctor_initv
// LLVM: %[[P_ADDR:.*]] = alloca ptr, i64 1, align 8
-// LLVM: %[[RAW_PTR:.*]] = call noundef ptr @_Znam(i64 noundef 3)
+// LLVM: %[[RAW_PTR:.*]] = call noundef nonnull ptr @_Znam(i64 noundef 3)
// LLVM: %[[BEGIN:.*]] = getelementptr %class.F, ptr %[[RAW_PTR]], i32 0
// LLVM: %[[ARRAY_END:.*]] = getelementptr %class.F, ptr %[[BEGIN]], i64 3
// LLVM: %[[IDX_ADDR:.*]] = alloca ptr, i64 1, align 1
@@ -1164,7 +1164,7 @@ void test_array_new_with_ctor_partial_init_list() {
// LLVM: define{{.*}} void @_Z42test_array_new_with_ctor_partial_init_listv
// LLVM: %[[P_ADDR:.*]] = alloca ptr, i64 1, align 8
-// LLVM: %[[RAW_PTR:.*]] = call noundef ptr @_Znam(i64 noundef 8)
+// LLVM: %[[RAW_PTR:.*]] = call noundef nonnull ptr @_Znam(i64 noundef 8)
// LLVM: call void @_ZN1GC1Ei(ptr noundef nonnull align 1 dereferenceable(1) %[[RAW_PTR]], i32 noundef 1)
// LLVM: %[[SECOND:.*]] = getelementptr %class.G, ptr %[[RAW_PTR]], i64 1
// LLVM: call void @_ZN1GC1Ei(ptr noundef nonnull align 1 dereferenceable(1) %[[SECOND]], i32 noundef 2)
diff --git a/clang/test/CIR/CodeGen/restrict-nonnull.c b/clang/test/CIR/CodeGen/restrict-nonnull.c
new file mode 100644
index 0000000000000..d391ac1b36809
--- /dev/null
+++ b/clang/test/CIR/CodeGen/restrict-nonnull.c
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -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: 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: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+
+// restrict on pointer params adds noalias.
+void take_restrict(int *restrict p) { *p = 42; }
+// CIR: cir.func {{.*}} @take_restrict(%arg0: !cir.ptr<!s32i>
+// CIR-SAME: {llvm.noalias, llvm.noundef}
+// LLVM: define {{.*}} void @take_restrict(ptr noalias noundef %{{.*}})
+// OGCG: define {{.*}} void @take_restrict(ptr noalias noundef %{{.*}})
+
+// __attribute__((nonnull)) on individual parameter.
+void take_nonnull(int *p) __attribute__((nonnull(1)));
+void take_nonnull(int *p) { *p = 42; }
+// CIR: cir.func {{.*}} @take_nonnull(%arg0: !cir.ptr<!s32i>
+// CIR-SAME: {llvm.nonnull, llvm.noundef}
+// LLVM: define {{.*}} void @take_nonnull(ptr noundef nonnull %{{.*}})
+// OGCG: define {{.*}} void @take_nonnull(ptr noundef nonnull %{{.*}})
+
+// restrict + nonnull together.
+void take_both(int *restrict p) __attribute__((nonnull(1)));
+void take_both(int *restrict p) { *p = 42; }
+// CIR: cir.func {{.*}} @take_both(%arg0: !cir.ptr<!s32i>
+// CIR-SAME: {llvm.noalias, llvm.nonnull, llvm.noundef}
+// LLVM: define {{.*}} void @take_both(ptr noalias noundef nonnull %{{.*}})
+// OGCG: define {{.*}} void @take_both(ptr noalias noundef nonnull %{{.*}})
diff --git a/clang/test/CIR/CodeGenBuiltins/builtin-call.cpp b/clang/test/CIR/CodeGenBuiltins/builtin-call.cpp
index d6f72b115f183..0efb6c7ae78ef 100644
--- a/clang/test/CIR/CodeGenBuiltins/builtin-call.cpp
+++ b/clang/test/CIR/CodeGenBuiltins/builtin-call.cpp
@@ -85,11 +85,11 @@ void library_builtins() {
// CIR: cir.func{{.*}} @_Z16library_builtinsv()
// CIR: %[[NULL:.+]] = cir.const #cir.ptr<null> : !cir.ptr<!s8i>
-// CIR: cir.call @printf(%[[NULL]]) nothrow : (!cir.ptr<!s8i> {llvm.noundef}) -> !s32i
+// CIR: cir.call @printf(%[[NULL]]) nothrow : (!cir.ptr<!s8i> {llvm.noalias, llvm.noundef}) -> !s32i
// CIR: cir.call @abort() nothrow {noreturn} : () -> ()
// LLVM: define{{.*}} void @_Z16library_builtinsv()
-// LLVM: call i32 (ptr, ...) @printf(ptr noundef null)
+// LLVM: call i32 (ptr, ...) @printf(ptr noalias noundef null)
// LLVM: call void @abort()
// OGCG: define{{.*}} void @_Z16library_builtinsv()
diff --git a/clang/test/CIR/CodeGenBuiltins/builtin-new-delete.cpp b/clang/test/CIR/CodeGenBuiltins/builtin-new-delete.cpp
index fdb4250af06d6..3ddf165e767c2 100644
--- a/clang/test/CIR/CodeGenBuiltins/builtin-new-delete.cpp
+++ b/clang/test/CIR/CodeGenBuiltins/builtin-new-delete.cpp
@@ -9,12 +9,12 @@
void test_builtins_basic() {
__builtin_operator_delete(__builtin_operator_new(4));
// CIR-LABEL: test_builtins_basic
- // CIR: [[P:%.*]] = cir.call @_Znwm({{%.*}}) {allocsize = array<i32: 0>, builtin} : (!u64i {llvm.noundef}) -> (!cir.ptr<!void> {llvm.noundef})
+ // CIR: [[P:%.*]] = cir.call @_Znwm({{%.*}}) {allocsize = array<i32: 0>, builtin} : (!u64i {llvm.noundef}) -> (!cir.ptr<!void> {llvm.nonnull, llvm.noundef})
// CIR: cir.call @_ZdlPv([[P]]) {{.*}}builtin{{.*}} : (!cir.ptr<!void> {llvm.noundef}) -> ()
// CIR: cir.return
// LLVM-LABEL: test_builtins_basic
- // LLVM: [[P:%.*]] = call noundef ptr @_Znwm(i64 {{.*}} 4) #[[ATTR_BUILTIN_NEW:.*]]
+ // LLVM: [[P:%.*]] = call noundef nonnull ptr @_Znwm(i64 {{.*}} 4) #[[ATTR_BUILTIN_NEW:.*]]
// LLVM: call void @_ZdlPv(ptr {{.*}} [[P]]) #[[ATTR_BUILTIN_DEL:.*]]
// LLVM: ret void
@@ -28,12 +28,12 @@ void test_sized_delete() {
__builtin_operator_delete(__builtin_operator_new(4), 4);
// CIR-LABEL: test_sized_delete
- // CIR: [[P:%.*]] = cir.call @_Znwm({{%.*}}) {allocsize = array<i32: 0>, builtin} : (!u64i {llvm.noundef}) -> (!cir.ptr<!void> {llvm.noundef})
+ // CIR: [[P:%.*]] = cir.call @_Znwm({{%.*}}) {allocsize = array<i32: 0>, builtin} : (!u64i {llvm.noundef}) -> (!cir.ptr<!void> {llvm.nonnull, llvm.noundef})
// CIR: cir.call @_ZdlPvm([[P]], {{%.*}}) {{.*}}builtin{{.*}} : (!cir.ptr<!void> {llvm.noundef}, !u64i {llvm.noundef}) -> ()
// CIR: cir.return
// LLVM-LABEL: test_sized_delete
- // LLVM: [[P:%.*]] = call noundef ptr @_Znwm(i64 {{.*}} 4) #[[ATTR_BUILTIN_NEW]]
+ // LLVM: [[P:%.*]] = call noundef nonnull ptr @_Znwm(i64 {{.*}} 4) #[[ATTR_BUILTIN_NEW]]
// LLVM: call void @_ZdlPvm(ptr {{.*}} [[P]], i64 {{.*}} 4) #[[ATTR_BUILTIN_DEL]]
// LLVM: ret void
diff --git a/clang/test/CIR/CodeGenBuiltins/builtin-printf.cpp b/clang/test/CIR/CodeGenBuiltins/builtin-printf.cpp
index 2a0f5c4196a8d..5c2ee802f7076 100644
--- a/clang/test/CIR/CodeGenBuiltins/builtin-printf.cpp
+++ b/clang/test/CIR/CodeGenBuiltins/builtin-printf.cpp
@@ -26,16 +26,16 @@ void func(char const * const str, int i) {
// CIR: cir.store %[[arg0]], %[[str_ptr]] : !cir.ptr<!s8i>, !cir.ptr<!cir.ptr<!s8i>>
// CIR: cir.store %[[arg1]], %[[i_ptr]] : !s32i, !cir.ptr<!s32i>
// CIR: %[[null_ptr:.+]] = cir.const #cir.ptr<null> : !cir.ptr<!s8i>
-// CIR: %[[printf_result1:.+]] = cir.call @printf(%[[null_ptr]]) nothrow : (!cir.ptr<!s8i> {llvm.noundef}) -> !s32i
+// CIR: %[[printf_result1:.+]] = cir.call @printf(%[[null_ptr]]) nothrow : (!cir.ptr<!s8i> {llvm.noalias, llvm.noundef}) -> !s32i
// CIR: %[[str_fmt_global:.+]] = cir.get_global @".str" : !cir.ptr<!cir.array<!s8i x 3>>
// CIR: %[[str_fmt_ptr:.+]] = cir.cast array_to_ptrdecay %[[str_fmt_global]] : !cir.ptr<!cir.array<!s8i x 3>> -> !cir.ptr<!s8i>
// CIR: %[[str_val:.+]] = cir.load{{.*}} %[[str_ptr]] : !cir.ptr<!cir.ptr<!s8i>>, !cir.ptr<!s8i>
-// CIR: %[[printf_result2:.+]] = cir.call @printf(%[[str_fmt_ptr]], %[[str_val]]) nothrow : (!cir.ptr<!s8i> {llvm.noundef}, !cir.ptr<!s8i> {llvm.noundef}) -> !s32i
+// CIR: %[[printf_result2:.+]] = cir.call @printf(%[[str_fmt_ptr]], %[[str_val]]) nothrow : (!cir.ptr<!s8i> {llvm.noalias, llvm.noundef}, !cir.ptr<!s8i> {llvm.noundef}) -> !s32i
// CIR: %[[full_fmt_global:.+]] = cir.get_global @".str.1" : !cir.ptr<!cir.array<!s8i x 7>>
// CIR: %[[full_fmt_ptr:.+]] = cir.cast array_to_ptrdecay %[[full_fmt_global]] : !cir.ptr<!cir.array<!s8i x 7>> -> !cir.ptr<!s8i>
// CIR: %[[str_val2:.+]] = cir.load{{.*}} %[[str_ptr]] : !cir.ptr<!cir.ptr<!s8i>>, !cir.ptr<!s8i>
// CIR: %[[i_val:.+]] = cir.load{{.*}} %[[i_ptr]] : !cir.ptr<!s32i>, !s32i
-// CIR: %[[printf_result3:.+]] = cir.call @printf(%[[full_fmt_ptr]], %[[str_val2]], %[[i_val]]) nothrow : (!cir.ptr<!s8i> {llvm.noundef}, !cir.ptr<!s8i> {llvm.noundef}, !s32i {llvm.noundef}) -> !s32i
+// CIR: %[[printf_result3:.+]] = cir.call @printf(%[[full_fmt_ptr]], %[[str_val2]], %[[i_val]]) nothrow : (!cir.ptr<!s8i> {llvm.noalias, llvm.noundef}, !cir.ptr<!s8i> {llvm.noundef}, !s32i {llvm.noundef}) -> !s32i
// CIR: cir.return
// LLVM: define{{.*}} void @_Z4funcPKci(ptr noundef %[[arg0:.+]], i32 noundef %[[arg1:.+]])
@@ -43,12 +43,12 @@ void func(char const * const str, int i) {
// LLVM: %[[i_ptr:.+]] = alloca i32
// LLVM: store ptr %[[arg0]], ptr %[[str_ptr]]{{.*}}
// LLVM: store i32 %[[arg1]], ptr %[[i_ptr]]{{.*}}
-// LLVM: %[[printf_result1:.+]] = call i32 (ptr, ...) @printf(ptr noundef null)
+// LLVM: %[[printf_result1:.+]] = call i32 (ptr, ...) @printf(ptr noalias noundef null)
// LLVM: %[[str_val:.+]] = load ptr, ptr %[[str_ptr]]{{.*}}
-// LLVM: %[[printf_result2:.+]] = call i32 (ptr, ...) @printf(ptr noundef @.str, ptr noundef %[[str_val]])
+// LLVM: %[[printf_result2:.+]] = call i32 (ptr, ...) @printf(ptr noalias noundef @.str, ptr noundef %[[str_val]])
// LLVM: %[[str_val2:.+]] = load ptr, ptr %[[str_ptr]]{{.*}}
// LLVM: %[[i_val:.+]] = load i32, ptr %[[i_ptr]]{{.*}}
-// LLVM: %[[printf_result3:.+]] = call i32 (ptr, ...) @printf(ptr noundef @.str.1, ptr noundef %[[str_val2]], i32 noundef %[[i_val]])
+// LLVM: %[[printf_result3:.+]] = call i32 (ptr, ...) @printf(ptr noalias noundef @.str.1, ptr noundef %[[str_val2]], i32 noundef %[[i_val]])
// LLVM: ret void
// OGCG: define{{.*}} void @_Z4funcPKci(ptr noundef %[[arg0:.+]], i32 noundef %[[arg1:.+]])
>From 834ac66d58573051f511b52491443aeda7752249 Mon Sep 17 00:00:00 2001
From: Adam Smith <adams at nvidia.com>
Date: Tue, 24 Mar 2026 11:12:52 -0700
Subject: [PATCH 2/5] [NFC] Fix clang-format whitespace
Made-with: Cursor
---
clang/lib/CIR/CodeGen/CIRGenModule.h | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 206d6c0a99924..0973b46999a1d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -121,8 +121,7 @@ class CIRGenModule : public CIRGenTypeCache {
/// A helper for constructAttributeList that handles argument attributes.
void constructFunctionArgumentAttributes(
const CIRGenFunctionInfo &info, const clang::Decl *targetDecl,
- bool isThunk,
- llvm::MutableArrayRef<mlir::NamedAttrList> argAttrs);
+ bool isThunk, llvm::MutableArrayRef<mlir::NamedAttrList> argAttrs);
/// A helper function for constructAttributeList that determines whether a
/// return value might have been discarded.
bool mayDropFunctionReturn(const ASTContext &context, QualType retTy);
>From 21a9becfc468393f9a281f1466d7c32301b2d09b Mon Sep 17 00:00:00 2001
From: Adam Smith <adams at nvidia.com>
Date: Mon, 6 Apr 2026 10:57:40 -0700
Subject: [PATCH 3/5] [CIR][ABI] Address PR #188281 review feedback
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Move ReturnsNonNullAttr to constructFunctionReturnAttributes
with NullPointerIsValid guard
- Skip restrict→noalias for builtins (OGCG doesn't apply it)
- Update operator new[] call checks in new.cpp with nonnull
- Add OGCG checks for operator new calls in new-delete.cpp
Made-with: Cursor
---
clang/lib/CIR/CodeGen/CIRGenCall.cpp | 16 ++++++++++------
clang/test/CIR/CodeGen/new-delete.cpp | 4 ++--
clang/test/CIR/CodeGen/new.cpp | 8 ++++----
clang/test/CIR/CodeGenBuiltins/builtin-call.cpp | 4 ++--
.../test/CIR/CodeGenBuiltins/builtin-printf.cpp | 12 ++++++------
5 files changed, 24 insertions(+), 20 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index cbbb5827b0adc..6cdf759b222e1 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -389,10 +389,6 @@ void CIRGenModule::constructAttributeList(
attrs.set(cir::CIRDialect::getSideEffectAttrName(),
cir::SideEffectAttr::get(&getMLIRContext(), sideEffect));
- if (targetDecl->hasAttr<ReturnsNonNullAttr>())
- retAttrs.set(mlir::LLVM::LLVMDialect::getNonNullAttrName(),
- mlir::UnitAttr::get(&getMLIRContext()));
-
// TODO(cir): Add noalias to returns for malloc-like functions
// (__attribute__((malloc)) / __declspec(restrict)).
@@ -620,6 +616,11 @@ void CIRGenModule::constructFunctionReturnAttributes(
getNaturalPointeeTypeAlignment(retTy).getQuantity()));
}
}
+
+ if (targetDecl && targetDecl->hasAttr<ReturnsNonNullAttr>() &&
+ !codeGenOpts.NullPointerIsValid)
+ retAttrs.set(mlir::LLVM::LLVMDialect::getNonNullAttrName(),
+ mlir::UnitAttr::get(&getMLIRContext()));
}
void CIRGenModule::constructFunctionArgumentAttributes(
@@ -715,8 +716,11 @@ void CIRGenModule::constructFunctionArgumentAttributes(
const ParmVarDecl *pvd = fd->getParamDecl(srcIdx);
QualType pvdType = pvd->getType();
- // restrict on pointer parameters → noalias.
- if (pvdType->isPointerType() && pvdType.isRestrictQualified())
+ // restrict on pointer parameters → noalias. Skip builtins:
+ // OGCG only applies restrict→noalias through calling convention
+ // lowering, which builtins bypass.
+ if (pvdType->isPointerType() && pvdType.isRestrictQualified() &&
+ !fd->getBuiltinID())
argAttrList.set(mlir::LLVM::LLVMDialect::getNoAliasAttrName(),
mlir::UnitAttr::get(&getMLIRContext()));
diff --git a/clang/test/CIR/CodeGen/new-delete.cpp b/clang/test/CIR/CodeGen/new-delete.cpp
index c0cb843d3b68b..3c9a8a6212a72 100644
--- a/clang/test/CIR/CodeGen/new-delete.cpp
+++ b/clang/test/CIR/CodeGen/new-delete.cpp
@@ -61,7 +61,7 @@ A *a() {
// OGCG: define {{.*}} ptr @_Z1av() {{.*}} personality ptr @__gxx_personality_v0 {
// OGCG: %[[EXN_SLOT:.*]] = alloca ptr
// OGCG: %[[EHSELECTOR_SLOT:.*]] = alloca i32
-// OGCG: %[[PTR:.*]] = call {{.*}} ptr @_Znwm(i64 8) #[[OGCG_ATTR_BUILTIN_NEW:.*]]
+// OGCG: %[[PTR:.*]] = call noalias nonnull ptr @_Znwm(i64 8) #[[OGCG_ATTR_BUILTIN_NEW:.*]]
// OGCG: invoke void @_ZN1AC1Ei(ptr {{.*}} %[[PTR]], i32 5)
// OGCG: to label %[[INVOKE_CONT:.*]] unwind label %[[UNWIND:.*]]
// OGCG: [[INVOKE_CONT]]:
@@ -139,7 +139,7 @@ A *b() {
// OGCG: define {{.*}} ptr @_Z1bv() {{.*}} personality ptr @__gxx_personality_v0 {
// OGCG: %[[EXN_SLOT:.*]] = alloca ptr
// OGCG: %[[EHSELECTOR_SLOT:.*]] = alloca i32
-// OGCG: %[[PTR:.*]] = call {{.*}} ptr @_Znwm(i64 8) #[[OGCG_ATTR_BUILTIN_NEW]]
+// OGCG: %[[PTR:.*]] = call noalias nonnull ptr @_Znwm(i64 8) #[[OGCG_ATTR_BUILTIN_NEW]]
// OGCG: %[[FOO:.*]] = invoke i32 @_Z3foov()
// OGCG: to label %[[INVOKE_CONT:.*]] unwind label %[[UNWIND:.*]]
// OGCG: [[INVOKE_CONT]]:
diff --git a/clang/test/CIR/CodeGen/new.cpp b/clang/test/CIR/CodeGen/new.cpp
index 452d7247f66dc..ad984ad3dc83e 100644
--- a/clang/test/CIR/CodeGen/new.cpp
+++ b/clang/test/CIR/CodeGen/new.cpp
@@ -831,7 +831,7 @@ void test_array_new_var_sized_with_ctor_init(int size) {
// LLVM: define{{.*}} void @_Z39test_array_new_var_sized_with_ctor_initi
// LLVM: %[[N:.*]] = load i32, ptr %{{.+}}
// LLVM: %[[N64:.*]] = sext i32 %[[N]] to i64
-// LLVM: %[[RAW:.*]] = call noundef ptr @_Znam(i64 {{.*}} %[[N64]])
+// LLVM: %[[RAW:.*]] = call noundef nonnull ptr @_Znam(i64 {{.*}} %[[N64]])
// LLVM: %[[CMP:.*]] = icmp ne i64 %[[N64]], 0
// LLVM: br i1 %[[CMP]]
@@ -888,7 +888,7 @@ void test_const_array_new_value_init() {
// rather than a bulk llvm.memset over the whole allocation.
//
// LLVM: define{{.*}} void @_Z31test_const_array_new_value_initv
-// LLVM: %[[RAW:.*]] = call noundef ptr @_Znam(i64 noundef 3)
+// LLVM: %[[RAW:.*]] = call noundef nonnull ptr @_Znam(i64 noundef 3)
// LLVM: %[[BEGIN:.*]] = getelementptr %class.OuterZero, ptr %[[RAW]], i32 0
// LLVM: %[[END:.*]] = getelementptr %class.OuterZero, ptr %[[BEGIN]], i64 3
// LLVM: store ptr %[[BEGIN]], ptr %[[IDX:.*]], align 8
@@ -1006,7 +1006,7 @@ void test_multidim_array_new_with_ctor() {
// CHECK: }
// LLVM: define{{.*}} void @_Z33test_multidim_array_new_with_ctorv
-// LLVM: %[[RAW:.*]] = call noundef ptr @_Znam(i64 noundef 6)
+// LLVM: %[[RAW:.*]] = call noundef nonnull ptr @_Znam(i64 noundef 6)
// LLVM: %[[BEGIN:.*]] = getelementptr %class.F, ptr %[[RAW]], i32 0
// LLVM: %[[END:.*]] = getelementptr %class.F, ptr %[[BEGIN]], i64 6
// LLVM: store ptr %[[BEGIN]], ptr %[[IDX:.*]], align 8
@@ -1079,7 +1079,7 @@ void test_multidim_var_array_new_with_ctor(int n) {
// LLVM: %[[N64:.*]] = sext i32 %[[N]] to i64
// LLVM: %[[MUL:.*]] = call { i64, i1 } @llvm.umul.with.overflow.i64(i64 %[[N64]], i64 3)
// LLVM: %[[TOTAL:.*]] = extractvalue { i64, i1 } %[[MUL]], 0
-// LLVM: %[[RAW:.*]] = call noundef ptr @_Znam(i64 noundef %{{.*}})
+// LLVM: %[[RAW:.*]] = call noundef nonnull ptr @_Znam(i64 noundef %{{.*}})
// LLVM: %[[END:.*]] = getelementptr %class.F, ptr %[[RAW]], i64 %[[TOTAL]]
// LLVM: %[[CMP:.*]] = icmp ne i64 %[[TOTAL]], 0
// LLVM: br i1 %[[CMP]]
diff --git a/clang/test/CIR/CodeGenBuiltins/builtin-call.cpp b/clang/test/CIR/CodeGenBuiltins/builtin-call.cpp
index 0efb6c7ae78ef..d6f72b115f183 100644
--- a/clang/test/CIR/CodeGenBuiltins/builtin-call.cpp
+++ b/clang/test/CIR/CodeGenBuiltins/builtin-call.cpp
@@ -85,11 +85,11 @@ void library_builtins() {
// CIR: cir.func{{.*}} @_Z16library_builtinsv()
// CIR: %[[NULL:.+]] = cir.const #cir.ptr<null> : !cir.ptr<!s8i>
-// CIR: cir.call @printf(%[[NULL]]) nothrow : (!cir.ptr<!s8i> {llvm.noalias, llvm.noundef}) -> !s32i
+// CIR: cir.call @printf(%[[NULL]]) nothrow : (!cir.ptr<!s8i> {llvm.noundef}) -> !s32i
// CIR: cir.call @abort() nothrow {noreturn} : () -> ()
// LLVM: define{{.*}} void @_Z16library_builtinsv()
-// LLVM: call i32 (ptr, ...) @printf(ptr noalias noundef null)
+// LLVM: call i32 (ptr, ...) @printf(ptr noundef null)
// LLVM: call void @abort()
// OGCG: define{{.*}} void @_Z16library_builtinsv()
diff --git a/clang/test/CIR/CodeGenBuiltins/builtin-printf.cpp b/clang/test/CIR/CodeGenBuiltins/builtin-printf.cpp
index 5c2ee802f7076..2a0f5c4196a8d 100644
--- a/clang/test/CIR/CodeGenBuiltins/builtin-printf.cpp
+++ b/clang/test/CIR/CodeGenBuiltins/builtin-printf.cpp
@@ -26,16 +26,16 @@ void func(char const * const str, int i) {
// CIR: cir.store %[[arg0]], %[[str_ptr]] : !cir.ptr<!s8i>, !cir.ptr<!cir.ptr<!s8i>>
// CIR: cir.store %[[arg1]], %[[i_ptr]] : !s32i, !cir.ptr<!s32i>
// CIR: %[[null_ptr:.+]] = cir.const #cir.ptr<null> : !cir.ptr<!s8i>
-// CIR: %[[printf_result1:.+]] = cir.call @printf(%[[null_ptr]]) nothrow : (!cir.ptr<!s8i> {llvm.noalias, llvm.noundef}) -> !s32i
+// CIR: %[[printf_result1:.+]] = cir.call @printf(%[[null_ptr]]) nothrow : (!cir.ptr<!s8i> {llvm.noundef}) -> !s32i
// CIR: %[[str_fmt_global:.+]] = cir.get_global @".str" : !cir.ptr<!cir.array<!s8i x 3>>
// CIR: %[[str_fmt_ptr:.+]] = cir.cast array_to_ptrdecay %[[str_fmt_global]] : !cir.ptr<!cir.array<!s8i x 3>> -> !cir.ptr<!s8i>
// CIR: %[[str_val:.+]] = cir.load{{.*}} %[[str_ptr]] : !cir.ptr<!cir.ptr<!s8i>>, !cir.ptr<!s8i>
-// CIR: %[[printf_result2:.+]] = cir.call @printf(%[[str_fmt_ptr]], %[[str_val]]) nothrow : (!cir.ptr<!s8i> {llvm.noalias, llvm.noundef}, !cir.ptr<!s8i> {llvm.noundef}) -> !s32i
+// CIR: %[[printf_result2:.+]] = cir.call @printf(%[[str_fmt_ptr]], %[[str_val]]) nothrow : (!cir.ptr<!s8i> {llvm.noundef}, !cir.ptr<!s8i> {llvm.noundef}) -> !s32i
// CIR: %[[full_fmt_global:.+]] = cir.get_global @".str.1" : !cir.ptr<!cir.array<!s8i x 7>>
// CIR: %[[full_fmt_ptr:.+]] = cir.cast array_to_ptrdecay %[[full_fmt_global]] : !cir.ptr<!cir.array<!s8i x 7>> -> !cir.ptr<!s8i>
// CIR: %[[str_val2:.+]] = cir.load{{.*}} %[[str_ptr]] : !cir.ptr<!cir.ptr<!s8i>>, !cir.ptr<!s8i>
// CIR: %[[i_val:.+]] = cir.load{{.*}} %[[i_ptr]] : !cir.ptr<!s32i>, !s32i
-// CIR: %[[printf_result3:.+]] = cir.call @printf(%[[full_fmt_ptr]], %[[str_val2]], %[[i_val]]) nothrow : (!cir.ptr<!s8i> {llvm.noalias, llvm.noundef}, !cir.ptr<!s8i> {llvm.noundef}, !s32i {llvm.noundef}) -> !s32i
+// CIR: %[[printf_result3:.+]] = cir.call @printf(%[[full_fmt_ptr]], %[[str_val2]], %[[i_val]]) nothrow : (!cir.ptr<!s8i> {llvm.noundef}, !cir.ptr<!s8i> {llvm.noundef}, !s32i {llvm.noundef}) -> !s32i
// CIR: cir.return
// LLVM: define{{.*}} void @_Z4funcPKci(ptr noundef %[[arg0:.+]], i32 noundef %[[arg1:.+]])
@@ -43,12 +43,12 @@ void func(char const * const str, int i) {
// LLVM: %[[i_ptr:.+]] = alloca i32
// LLVM: store ptr %[[arg0]], ptr %[[str_ptr]]{{.*}}
// LLVM: store i32 %[[arg1]], ptr %[[i_ptr]]{{.*}}
-// LLVM: %[[printf_result1:.+]] = call i32 (ptr, ...) @printf(ptr noalias noundef null)
+// LLVM: %[[printf_result1:.+]] = call i32 (ptr, ...) @printf(ptr noundef null)
// LLVM: %[[str_val:.+]] = load ptr, ptr %[[str_ptr]]{{.*}}
-// LLVM: %[[printf_result2:.+]] = call i32 (ptr, ...) @printf(ptr noalias noundef @.str, ptr noundef %[[str_val]])
+// LLVM: %[[printf_result2:.+]] = call i32 (ptr, ...) @printf(ptr noundef @.str, ptr noundef %[[str_val]])
// LLVM: %[[str_val2:.+]] = load ptr, ptr %[[str_ptr]]{{.*}}
// LLVM: %[[i_val:.+]] = load i32, ptr %[[i_ptr]]{{.*}}
-// LLVM: %[[printf_result3:.+]] = call i32 (ptr, ...) @printf(ptr noalias noundef @.str.1, ptr noundef %[[str_val2]], i32 noundef %[[i_val]])
+// LLVM: %[[printf_result3:.+]] = call i32 (ptr, ...) @printf(ptr noundef @.str.1, ptr noundef %[[str_val2]], i32 noundef %[[i_val]])
// LLVM: ret void
// OGCG: define{{.*}} void @_Z4funcPKci(ptr noundef %[[arg0:.+]], i32 noundef %[[arg1:.+]])
>From bbec0cc4d592649a3838f3ea8e0e8e1a8ca49c12 Mon Sep 17 00:00:00 2001
From: Adam Smith <adams at nvidia.com>
Date: Mon, 13 Apr 2026 14:07:29 -0700
Subject: [PATCH 4/5] [CIR] Address review feedback on restrict/nonnull
attributes
Rework constructFunctionArgumentAttributes to address Erich's
feedback:
- Move FunctionDecl cast closer to usage
- Replace pointer arithmetic with argNo counter
- Simplify nonnull check with short-circuiting expression
- Hoist isPointerType() check to outer condition
Made-with: Cursor
---
clang/lib/CIR/CodeGen/CIRGenCall.cpp | 39 ++++++++++++----------------
1 file changed, 17 insertions(+), 22 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index 6cdf759b222e1..79ab5520b817a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -630,8 +630,6 @@ void CIRGenModule::constructFunctionArgumentAttributes(
// TODO(cir): classic codegen does a lot of work here based on the ABIArgInfo
// to set things based on calling convention.
- const auto *fd = dyn_cast_or_null<FunctionDecl>(targetDecl);
-
if (info.isInstanceMethod() && !isThunk) {
QualType thisPtrTy = info.arguments()[0];
// Member allocation functions are instance methods, but setting attributes
@@ -674,6 +672,8 @@ void CIRGenModule::constructFunctionArgumentAttributes(
// that seems risky at the moment. At one point we should evaluate if at least
// dereferenceable, nonnull, and align can be combined.
const cir::CIRDataLayout &layout = getDataLayout();
+ const auto *fd = dyn_cast_or_null<FunctionDecl>(targetDecl);
+ unsigned argNo = 0;
for (const auto &[argAttrList, argCanType] :
llvm::zip_equal(argAttrs, info.arguments())) {
assert(!cir::MissingFeatures::abiArgInfo());
@@ -707,35 +707,30 @@ void CIRGenModule::constructFunctionArgumentAttributes(
getNaturalPointeeTypeAlignment(argType).getQuantity()));
}
- // Source-level parameter attributes (restrict, nonnull). These
- // require the FunctionDecl to access ParmVarDecl info.
- if (fd) {
- unsigned paramIdx = &argAttrList - argAttrs.data();
- unsigned srcIdx = info.isInstanceMethod() ? paramIdx - 1 : paramIdx;
+ // Source-level parameter attributes (restrict, nonnull) require
+ // ParmVarDecl access since canonical types strip restrict.
+ if (fd && argType->isPointerType()) {
+ unsigned srcIdx = info.isInstanceMethod() ? argNo - 1 : argNo;
if (srcIdx < fd->getNumParams()) {
const ParmVarDecl *pvd = fd->getParamDecl(srcIdx);
- QualType pvdType = pvd->getType();
- // restrict on pointer parameters → noalias. Skip builtins:
- // OGCG only applies restrict→noalias through calling convention
- // lowering, which builtins bypass.
- if (pvdType->isPointerType() && pvdType.isRestrictQualified() &&
- !fd->getBuiltinID())
+ // restrict -> noalias. Skip builtins: OGCG only applies
+ // restrict->noalias through calling convention lowering.
+ if (pvd->getType().isRestrictQualified() && !fd->getBuiltinID())
argAttrList.set(mlir::LLVM::LLVMDialect::getNoAliasAttrName(),
mlir::UnitAttr::get(&getMLIRContext()));
// __attribute__((nonnull)) on pointer parameters.
- if (pvdType->isPointerType() && !codeGenOpts.NullPointerIsValid) {
- bool hasNonnull = pvd->hasAttr<NonNullAttr>();
- if (!hasNonnull)
- if (const auto *nna = fd->getAttr<NonNullAttr>())
- hasNonnull = nna->isNonNull(srcIdx);
- if (hasNonnull)
- argAttrList.set(mlir::LLVM::LLVMDialect::getNonNullAttrName(),
- mlir::UnitAttr::get(&getMLIRContext()));
- }
+ if (!codeGenOpts.NullPointerIsValid &&
+ (pvd->hasAttr<NonNullAttr>() ||
+ (fd->getAttr<NonNullAttr>() &&
+ fd->getAttr<NonNullAttr>()->isNonNull(srcIdx))))
+ argAttrList.set(mlir::LLVM::LLVMDialect::getNonNullAttrName(),
+ mlir::UnitAttr::get(&getMLIRContext()));
}
}
+
+ ++argNo;
}
}
>From 107e5d920afc44d3823add6cfa4105d970ae842e Mon Sep 17 00:00:00 2001
From: Adam Smith <adams at nvidia.com>
Date: Wed, 15 Apr 2026 10:22:19 -0700
Subject: [PATCH 5/5] Address review: use ParmVarDecl in zip loop, remove index
arithmetic
Build a parallel SmallVector<const ParmVarDecl *> aligned with
argAttrs (nullptr for 'this' and extra slots), then add it to the
zip_equal loop. Eliminates manual srcIdx computation for restrict
and nonnull checks.
Update new-delete-deactivation.cpp LLVM checks for nonnull on
operator new calls.
Made-with: Cursor
---
clang/lib/CIR/CodeGen/CIRGenCall.cpp | 53 +++++++++----------
.../CIR/CodeGen/new-delete-deactivation.cpp | 10 ++--
2 files changed, 29 insertions(+), 34 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index 79ab5520b817a..542034e0a9847 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -673,9 +673,19 @@ void CIRGenModule::constructFunctionArgumentAttributes(
// dereferenceable, nonnull, and align can be combined.
const cir::CIRDataLayout &layout = getDataLayout();
const auto *fd = dyn_cast_or_null<FunctionDecl>(targetDecl);
- unsigned argNo = 0;
- for (const auto &[argAttrList, argCanType] :
- llvm::zip_equal(argAttrs, info.arguments())) {
+
+ // Build a parallel vector of ParmVarDecls aligned with argAttrs.
+ // Instance methods have a leading 'this' slot with no ParmVarDecl.
+ SmallVector<const ParmVarDecl *> parmDecls(argAttrs.size(), nullptr);
+ if (fd) {
+ unsigned offset = info.isInstanceMethod() ? 1 : 0;
+ for (unsigned i = 0;
+ i < fd->getNumParams() && i + offset < parmDecls.size(); ++i)
+ parmDecls[i + offset] = fd->getParamDecl(i);
+ }
+
+ for (const auto &[argAttrList, argCanType, pvd] :
+ llvm::zip_equal(argAttrs, info.arguments(), parmDecls)) {
assert(!cir::MissingFeatures::abiArgInfo());
QualType argType = argCanType;
const cir::ABIArgInfo argInfo = cir::ABIArgInfo::getDirect();
@@ -686,9 +696,6 @@ void CIRGenModule::constructFunctionArgumentAttributes(
mlir::UnitAttr::get(&getMLIRContext()));
assert(!cir::MissingFeatures::abiArgInfo());
- // TODO(cir): there is plenty of other attributes here added due to ABI
- // decisions. While these probably won't end up here, we note that the
- // classic codegen does it here and perhaps we should pay attention to that.
if (const auto *refTy = argType->getAs<ReferenceType>()) {
QualType pointeeTy = refTy->getPointeeType();
@@ -707,30 +714,18 @@ void CIRGenModule::constructFunctionArgumentAttributes(
getNaturalPointeeTypeAlignment(argType).getQuantity()));
}
- // Source-level parameter attributes (restrict, nonnull) require
- // ParmVarDecl access since canonical types strip restrict.
- if (fd && argType->isPointerType()) {
- unsigned srcIdx = info.isInstanceMethod() ? argNo - 1 : argNo;
- if (srcIdx < fd->getNumParams()) {
- const ParmVarDecl *pvd = fd->getParamDecl(srcIdx);
-
- // restrict -> noalias. Skip builtins: OGCG only applies
- // restrict->noalias through calling convention lowering.
- if (pvd->getType().isRestrictQualified() && !fd->getBuiltinID())
- argAttrList.set(mlir::LLVM::LLVMDialect::getNoAliasAttrName(),
- mlir::UnitAttr::get(&getMLIRContext()));
-
- // __attribute__((nonnull)) on pointer parameters.
- if (!codeGenOpts.NullPointerIsValid &&
- (pvd->hasAttr<NonNullAttr>() ||
- (fd->getAttr<NonNullAttr>() &&
- fd->getAttr<NonNullAttr>()->isNonNull(srcIdx))))
- argAttrList.set(mlir::LLVM::LLVMDialect::getNonNullAttrName(),
- mlir::UnitAttr::get(&getMLIRContext()));
- }
- }
+ if (pvd && argType->isPointerType()) {
+ if (pvd->getType().isRestrictQualified() && !fd->getBuiltinID())
+ argAttrList.set(mlir::LLVM::LLVMDialect::getNoAliasAttrName(),
+ mlir::UnitAttr::get(&getMLIRContext()));
- ++argNo;
+ if (!codeGenOpts.NullPointerIsValid &&
+ (pvd->hasAttr<NonNullAttr>() ||
+ (fd->getAttr<NonNullAttr>() && fd->getAttr<NonNullAttr>()->isNonNull(
+ pvd->getFunctionScopeIndex()))))
+ argAttrList.set(mlir::LLVM::LLVMDialect::getNonNullAttrName(),
+ mlir::UnitAttr::get(&getMLIRContext()));
+ }
}
}
diff --git a/clang/test/CIR/CodeGen/new-delete-deactivation.cpp b/clang/test/CIR/CodeGen/new-delete-deactivation.cpp
index f5ad9b56cd772..e70aaa1ce86aa 100644
--- a/clang/test/CIR/CodeGen/new-delete-deactivation.cpp
+++ b/clang/test/CIR/CodeGen/new-delete-deactivation.cpp
@@ -51,7 +51,7 @@ A *deact_simple() { return new A(makeB()); }
// LLVM-LABEL: define dso_local ptr @_Z12deact_simplev() {{.*}} personality ptr @__gxx_personality_v0 {
// LLVM: %[[TMP:.*]] = alloca %struct.B
// LLVM: %[[ACTIVE:.*]] = alloca i8
-// LLVM: %[[PTR:.*]] = call ptr @_Znwm(i64 1) #[[ATTR_BUILTIN_NEW:.*]]
+// LLVM: %[[PTR:.*]] = call nonnull ptr @_Znwm(i64 1) #[[ATTR_BUILTIN_NEW:.*]]
// LLVM: store i8 1, ptr %[[ACTIVE]]
// LLVM: %[[MAKEB:.*]] = invoke %struct.B @_Z5makeBv()
// LLVM: to label %[[INVOKE_CONT:.*]] unwind label %[[UNWIND_OUTER:.*]]
@@ -128,7 +128,7 @@ A *deact_if(bool cond) {
// LLVM-LABEL: define dso_local ptr @_Z8deact_ifb(i1 %0) {{.*}} personality ptr @__gxx_personality_v0 {
// LLVM: br i1 %{{.*}}, label %[[THEN:.*]], label %[[END:.*]]
// LLVM: [[THEN]]:
-// LLVM: %[[PTR:.*]] = call ptr @_Znwm(i64 1) #[[ATTR_BUILTIN_NEW]]
+// LLVM: %[[PTR:.*]] = call nonnull ptr @_Znwm(i64 1) #[[ATTR_BUILTIN_NEW]]
// LLVM: store i8 1, ptr %[[ACTIVE:.*]]
// LLVM: invoke void @_ZN1AC1Ei(ptr {{.*}} %[[PTR]], i32 {{.*}})
// LLVM: to label %[[CONT:.*]] unwind label %[[UNWIND_INNER:.*]]
@@ -183,7 +183,7 @@ A *deact_ternary(bool cond) { return (new A(makeB()), cond) ? nullptr : nullptr;
// CIR: }
// LLVM-LABEL: define dso_local ptr @_Z13deact_ternaryb(i1 %0) {{.*}} personality ptr @__gxx_personality_v0 {
-// LLVM: %[[PTR:.*]] = call ptr @_Znwm(i64 1) #[[ATTR_BUILTIN_NEW]]
+// LLVM: %[[PTR:.*]] = call nonnull ptr @_Znwm(i64 1) #[[ATTR_BUILTIN_NEW]]
// LLVM: store i8 1, ptr %[[ACTIVE:.*]]
// LLVM: invoke void @_ZN1AC1Ei(ptr {{.*}} %[[PTR]], i32 {{.*}})
// LLVM: to label %[[CONT:.*]] unwind label %[[UNWIND_INNER:.*]]
@@ -246,7 +246,7 @@ A *deact_while_cond(int n) {
// LLVM: %[[ACTIVE:.*]] = alloca i8
// LLVM: br label %[[WHILE_COND:.*]]
// LLVM: [[WHILE_COND]]:
-// LLVM: %[[PTR:.*]] = call ptr @_Znwm(i64 1) #[[ATTR_BUILTIN_NEW]]
+// LLVM: %[[PTR:.*]] = call nonnull ptr @_Znwm(i64 1) #[[ATTR_BUILTIN_NEW]]
// LLVM: store i8 1, ptr %[[ACTIVE]]
// LLVM: invoke %struct.B @_Z5makeBv()
// LLVM: to label %[[INVOKE_CONT:.*]] unwind label %[[UNWIND_OUTER:.*]]
@@ -328,7 +328,7 @@ A *deact_switch(int kind) {
// LLVM: i32 1, label %[[CASE1:.*]]
// LLVM: ]
// LLVM: [[CASE1]]:
-// LLVM: %[[PTR:.*]] = call ptr @_Znwm(i64 1) #[[ATTR_BUILTIN_NEW]]
+// LLVM: %[[PTR:.*]] = call nonnull ptr @_Znwm(i64 1) #[[ATTR_BUILTIN_NEW]]
// LLVM: store i8 1, ptr %[[ACTIVE:.*]]
// LLVM: invoke void @_ZN1AC1Ei(ptr {{.*}} %[[PTR]], i32 {{.*}})
// LLVM: to label %[[CONT:.*]] unwind label %[[UNWIND_INNER:.*]]
More information about the cfe-commits
mailing list