[clang] [CIR] Add restrict→noalias on non-builtin pointer params (PR #191483)

via cfe-commits cfe-commits at lists.llvm.org
Thu Apr 16 12:04:09 PDT 2026


https://github.com/adams381 updated https://github.com/llvm/llvm-project/pull/191483

>From 7ad65b03d0ff08e8f2e5909c5cfebb7e57b6ae18 Mon Sep 17 00:00:00 2001
From: Adam Smith <adams at nvidia.com>
Date: Thu, 16 Apr 2026 12:00:45 -0700
Subject: [PATCH] [CIR] Add restrict->noalias on non-builtin pointer params

Set noalias on function definitions (not call sites) when a
pointer parameter has the restrict qualifier.  Skip builtins
to match OGCG, which applies restrict->noalias in
EmitFunctionProlog rather than in attribute construction.

Thread targetDecl and attrOnCallSite into
constructFunctionArgumentAttributes and build a parallel
ParmVarDecl array in the zip_equal loop to avoid manual
index arithmetic.

Made-with: Cursor
---
 clang/lib/CIR/CodeGen/CIRGenCall.cpp      | 32 ++++++++++++++-----
 clang/lib/CIR/CodeGen/CIRGenModule.h      |  3 +-
 clang/test/CIR/CodeGen/restrict-noalias.c | 38 +++++++++++++++++++++++
 3 files changed, 65 insertions(+), 8 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/restrict-noalias.c

diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index 19bb30aa1b656..9070407115a68 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -483,7 +483,8 @@ void CIRGenModule::constructAttributeList(
   // TODO(cir): Add loader-replaceable attribute here.
 
   constructFunctionReturnAttributes(info, targetDecl, isThunk, retAttrs);
-  constructFunctionArgumentAttributes(info, isThunk, argAttrs);
+  constructFunctionArgumentAttributes(info, targetDecl, isThunk, attrOnCallSite,
+                                      argAttrs);
 }
 
 bool CIRGenModule::hasStrictReturn(QualType retTy, const Decl *targetDecl) {
@@ -630,13 +631,11 @@ void CIRGenModule::constructFunctionReturnAttributes(
 }
 
 void CIRGenModule::constructFunctionArgumentAttributes(
-    const CIRGenFunctionInfo &info, bool isThunk,
-    llvm::MutableArrayRef<mlir::NamedAttrList> argAttrs) {
+    const CIRGenFunctionInfo &info, const Decl *targetDecl, bool isThunk,
+    bool attrOnCallSite, 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.
 
   if (info.isInstanceMethod() && !isThunk) {
     QualType thisPtrTy = info.arguments()[0];
@@ -680,8 +679,20 @@ 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();
-  for (const auto &[argAttrList, argCanType] :
-       llvm::zip_equal(argAttrs, info.arguments())) {
+  const auto *fd = dyn_cast_or_null<FunctionDecl>(targetDecl);
+
+  // Build a parallel array of ParmVarDecls aligned with argAttrs so we can
+  // access parameter-level qualifiers (e.g. restrict) without manual index
+  // arithmetic in the loop.
+  SmallVector<const ParmVarDecl *> parmDecls(argAttrs.size(), nullptr);
+  if (fd) {
+    unsigned offset = info.isInstanceMethod() ? 1 : 0;
+    for (unsigned i = 0; i < fd->getNumParams(); ++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();
@@ -717,6 +728,13 @@ void CIRGenModule::constructFunctionArgumentAttributes(
       if (unsigned mask = getNoFPClassTestMask(getLangOpts()))
         argAttrList.set(mlir::LLVM::LLVMDialect::getNoFPClassAttrName(),
                         builder.getI64IntegerAttr(mask));
+
+    // restrict -> noalias on definitions only (not call sites).  Skip
+    // builtins: OGCG applies restrict->noalias in EmitFunctionProlog.
+    if (!attrOnCallSite && pvd && pvd->getType()->isPointerType() &&
+        pvd->getType().isRestrictQualified() && !fd->getBuiltinID())
+      argAttrList.set(mlir::LLVM::LLVMDialect::getNoAliasAttrName(),
+                      mlir::UnitAttr::get(&getMLIRContext()));
   }
 }
 
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index ba3f936106d31..0360ec640810f 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, bool attrOnCallSite,
       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/restrict-noalias.c b/clang/test/CIR/CodeGen/restrict-noalias.c
new file mode 100644
index 0000000000000..9ba14ce69e9e9
--- /dev/null
+++ b/clang/test/CIR/CodeGen/restrict-noalias.c
@@ -0,0 +1,38 @@
+// 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
+
+void user_func(int *__restrict p);
+
+void test_user(int *__restrict p) {
+  user_func(p);
+}
+
+// CIR: cir.func private @user_func(!cir.ptr<!s32i> {llvm.noalias, llvm.noundef})
+// CIR: cir.func {{.*}} @test_user(%arg0: !cir.ptr<!s32i> {llvm.noalias, llvm.noundef}
+// CIR:   cir.call @user_func(%{{.*}}) : (!cir.ptr<!s32i> {llvm.noundef}) -> ()
+
+// LLVM: define dso_local void @test_user(ptr noalias noundef %{{.*}})
+// LLVM:   call void @user_func(ptr noundef %{{.*}})
+
+// OGCG: define dso_local void @test_user(ptr noalias noundef %{{.*}})
+// OGCG:   call void @user_func(ptr noundef %{{.*}})
+
+int printf(const char *__restrict fmt, ...);
+
+void test_builtin(const char *__restrict fmt) {
+  printf(fmt);
+}
+
+// Builtins must NOT get noalias from restrict (matching OGCG behavior).
+// CIR: cir.func {{.*}} @test_builtin(%arg0: !cir.ptr<!s8i> {llvm.noalias, llvm.noundef}
+// CIR:   cir.call @printf(%{{.*}}) : (!cir.ptr<!s8i> {llvm.noundef}) -> !s32i
+
+// LLVM: define dso_local void @test_builtin(ptr noalias noundef %{{.*}})
+// LLVM:   call i32 (ptr, ...) @printf(ptr noundef %{{.*}})
+
+// OGCG: define dso_local void @test_builtin(ptr noalias noundef %{{.*}})
+// OGCG:   call i32 (ptr, ...) @printf(ptr noundef %{{.*}})



More information about the cfe-commits mailing list