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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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:.*]]

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to