[clang] [CIR] Add restrict→noalias on non-builtin pointer params (PR #191483)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Apr 13 14:50:39 PDT 2026
https://github.com/adams381 updated https://github.com/llvm/llvm-project/pull/191483
>From c01652da4b5da0e4c34cfe635583eecde3961758 Mon Sep 17 00:00:00 2001
From: Adam Smith <adams at nvidia.com>
Date: Fri, 10 Apr 2026 11:09:42 -0700
Subject: [PATCH 1/2] =?UTF-8?q?[CIR]=20Add=20restrict=E2=86=92noalias=20an?=
=?UTF-8?q?d=20skip=20builtins?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add noalias attribute for restrict-qualified pointer parameters on
non-builtin functions. Builtins like printf are skipped because OGCG
applies restrict→noalias through calling convention lowering, which
builtins bypass.
Pass targetDecl to constructFunctionArgumentAttributes so it can
access ParmVarDecl for source-level qualifiers.
Made-with: Cursor
---
clang/lib/CIR/CodeGen/CIRGenCall.cpp | 25 ++++++++++--
clang/lib/CIR/CodeGen/CIRGenModule.h | 4 +-
.../CIR/CodeGen/asm-label-inline-builtins.c | 4 +-
clang/test/CIR/CodeGen/restrict-noalias.c | 38 +++++++++++++++++++
4 files changed, 63 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 876fef687b477..efcb80545d493 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -480,7 +480,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) {
@@ -616,13 +616,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];
@@ -698,6 +698,23 @@ void CIRGenModule::constructFunctionArgumentAttributes(
builder.getI64IntegerAttr(
getNaturalPointeeTypeAlignment(argType).getQuantity()));
}
+
+ // restrict on pointer parameters -> noalias. Skip builtins: OGCG only
+ // applies restrict->noalias through calling convention lowering, which
+ // builtins bypass.
+ 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();
+
+ if (pvdType->isPointerType() && pvdType.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 266510de84fd0..8156276c71c96 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -117,8 +117,8 @@ class CIRGenModule : public CIRGenTypeCache {
mlir::NamedAttrList &retAttrs);
/// A helper for constructAttributeList that handles argument attributes.
void constructFunctionArgumentAttributes(
- const CIRGenFunctionInfo &info, bool isThunk,
- llvm::MutableArrayRef<mlir::NamedAttrList> argAttrs);
+ 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.
bool mayDropFunctionReturn(const ASTContext &context, QualType retTy);
diff --git a/clang/test/CIR/CodeGen/asm-label-inline-builtins.c b/clang/test/CIR/CodeGen/asm-label-inline-builtins.c
index f3ff4c5a0c2ba..2d11712812dd6 100644
--- a/clang/test/CIR/CodeGen/asm-label-inline-builtins.c
+++ b/clang/test/CIR/CodeGen/asm-label-inline-builtins.c
@@ -32,14 +32,14 @@ void test(const char *fmt, __builtin_va_list ap) {
}
// CIR: cir.func always_inline internal private @__vprintfieee128.inline({{.*}}) -> !s32i
-// CIR: cir.call @__vfprintf_chkieee128(%{{.*}}, %{{.*}}, %{{.*}}, %{{.*}})
+// CIR: cir.call @__vfprintf_chkieee128({{.*}}) : (!cir.ptr<!rec__IO_FILE> {llvm.noalias, llvm.noundef}, !s32i {llvm.noundef}, !cir.ptr<!s8i> {llvm.noalias, llvm.noundef}, !cir.ptr<!rec___va_list_tag> {llvm.noundef}) -> !s32i
//
// CIR: cir.func {{.*}} @test({{.*}})
// CIR: cir.call @__vprintfieee128.inline(%{{.*}}, %{{.*}})
// LLVM: define internal i32 @__vprintfieee128.inline({{.*}}) #[[ALWAYS_INLINE_ATTR:.*]] {
-// LLVM: call i32 @__vfprintf_chkieee128(ptr {{.*}} %{{.*}}, i32 {{.*}} 1, ptr {{.*}} %{{.*}}, ptr {{.*}} %{{.*}})
+// LLVM: call i32 @__vfprintf_chkieee128(ptr noalias noundef %{{.*}}, i32 noundef 1, ptr noalias noundef %{{.*}}, ptr noundef %{{.*}})
//
// LLVM: define {{.*}} void @test{{.*}}
// LLVM: call i32 @__vprintfieee128.inline(ptr {{.*}} %{{.*}}, ptr {{.*}} %{{.*}})
diff --git a/clang/test/CIR/CodeGen/restrict-noalias.c b/clang/test/CIR/CodeGen/restrict-noalias.c
new file mode 100644
index 0000000000000..06513688d16dc
--- /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.noalias, llvm.noundef}) -> ()
+
+// LLVM: define dso_local void @test_user(ptr noalias noundef %{{.*}})
+// LLVM: call void @user_func(ptr noalias 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 %{{.*}})
>From e47340a0b591a6b3c7608d6f957899c81e88f4b6 Mon Sep 17 00:00:00 2001
From: Adam Smith <adams at nvidia.com>
Date: Mon, 13 Apr 2026 14:50:04 -0700
Subject: [PATCH 2/2] [CIR] Address review feedback on restrict->noalias
- Replace pointer arithmetic with argNo counter
- Move FunctionDecl cast closer to usage
- Add attrOnCallSite guard to skip restrict->noalias at call
sites, matching OGCG behavior (classic codegen applies it in
EmitFunctionProlog, not constructAttributeList)
- Update test to match OGCG: no noalias on call sites
Made-with: Cursor
---
clang/lib/CIR/CodeGen/CIRGenCall.cpp | 35 +++++++++++------------
clang/lib/CIR/CodeGen/CIRGenModule.h | 3 +-
clang/test/CIR/CodeGen/restrict-noalias.c | 4 +--
3 files changed, 20 insertions(+), 22 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index efcb80545d493..277b6c8da2af9 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -480,7 +480,8 @@ void CIRGenModule::constructAttributeList(
// TODO(cir): Add loader-replaceable attribute here.
constructFunctionReturnAttributes(info, targetDecl, isThunk, retAttrs);
- constructFunctionArgumentAttributes(info, targetDecl, isThunk, argAttrs);
+ constructFunctionArgumentAttributes(info, targetDecl, isThunk, attrOnCallSite,
+ argAttrs);
}
bool CIRGenModule::hasStrictReturn(QualType retTy, const Decl *targetDecl) {
@@ -617,13 +618,11 @@ void CIRGenModule::constructFunctionReturnAttributes(
void CIRGenModule::constructFunctionArgumentAttributes(
const CIRGenFunctionInfo &info, const Decl *targetDecl, bool isThunk,
- llvm::MutableArrayRef<mlir::NamedAttrList> argAttrs) {
+ 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.
- 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
@@ -666,6 +665,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());
@@ -699,22 +700,18 @@ void CIRGenModule::constructFunctionArgumentAttributes(
getNaturalPointeeTypeAlignment(argType).getQuantity()));
}
- // restrict on pointer parameters -> noalias. Skip builtins: OGCG only
- // applies restrict->noalias through calling convention lowering, which
- // builtins bypass.
- 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();
-
- if (pvdType->isPointerType() && pvdType.isRestrictQualified() &&
- !fd->getBuiltinID())
- argAttrList.set(mlir::LLVM::LLVMDialect::getNoAliasAttrName(),
- mlir::UnitAttr::get(&getMLIRContext()));
- }
+ // restrict -> noalias on definitions only (not call sites). Skip
+ // builtins: OGCG applies restrict->noalias in EmitFunctionProlog.
+ if (!attrOnCallSite && fd && argType->isPointerType()) {
+ unsigned srcIdx = info.isInstanceMethod() ? argNo - 1 : argNo;
+ if (srcIdx < fd->getNumParams() &&
+ fd->getParamDecl(srcIdx)->getType().isRestrictQualified() &&
+ !fd->getBuiltinID())
+ argAttrList.set(mlir::LLVM::LLVMDialect::getNoAliasAttrName(),
+ mlir::UnitAttr::get(&getMLIRContext()));
}
+
+ ++argNo;
}
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 8156276c71c96..caed18460bbbb 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -118,7 +118,8 @@ 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, bool attrOnCallSite,
+ 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);
diff --git a/clang/test/CIR/CodeGen/restrict-noalias.c b/clang/test/CIR/CodeGen/restrict-noalias.c
index 06513688d16dc..9ba14ce69e9e9 100644
--- a/clang/test/CIR/CodeGen/restrict-noalias.c
+++ b/clang/test/CIR/CodeGen/restrict-noalias.c
@@ -13,10 +13,10 @@ void test_user(int *__restrict 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.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 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 %{{.*}})
More information about the cfe-commits
mailing list