https://github.com/adams381 updated https://github.com/llvm/llvm-project/pull/191483
>From a2442e5314eaff940def53cac6dd8faf7ab21284 Mon Sep 17 00:00:00 2001 From: Adam Smith <[email protected]> Date: Fri, 10 Apr 2026 11:09:42 -0700 Subject: [PATCH 1/3] =?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 ca59f56366822..42d1b5c308b7d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -120,8 +120,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 706699eb8c493854984501f769336ac35b78ec58 Mon Sep 17 00:00:00 2001 From: Adam Smith <[email protected]> Date: Mon, 13 Apr 2026 14:50:04 -0700 Subject: [PATCH 2/3] [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 42d1b5c308b7d..f76fd0e8663e4 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -121,7 +121,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 %{{.*}}) >From b53cf838e5f01d764b2485b9b1f8f95b8f860503 Mon Sep 17 00:00:00 2001 From: Adam Smith <[email protected]> Date: Tue, 14 Apr 2026 11:45:07 -0700 Subject: [PATCH 3/3] Rework restrict->noalias to use ParmVarDecl in zip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Build a parallel SmallVector of ParmVarDecl pointers aligned with the argAttrs array, then include it in the zip_equal loop. This replaces manual index arithmetic (argNo counter with instance-method offset) with direct ParmVarDecl access, addressing review feedback about sidesteping the function's organizational structure. Also fix stale CIR and LLVM CHECK lines in asm-label-inline-builtins.c that expected noalias on call sites — call sites do not carry noalias, only declarations do. Made-with: Cursor --- clang/lib/CIR/CodeGen/CIRGenCall.cpp | 30 +++++++++++-------- .../CIR/CodeGen/asm-label-inline-builtins.c | 4 +-- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index 277b6c8da2af9..ecc66bbcd90be 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -666,9 +666,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 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(); @@ -702,16 +712,10 @@ void CIRGenModule::constructFunctionArgumentAttributes( // 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; + 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/test/CIR/CodeGen/asm-label-inline-builtins.c b/clang/test/CIR/CodeGen/asm-label-inline-builtins.c index 2d11712812dd6..bee255fb149f9 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.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.call @__vfprintf_chkieee128({{.*}}) : (!cir.ptr<!rec__IO_FILE> {llvm.noundef}, !s32i {llvm.noundef}, !cir.ptr<!s8i> {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 noalias noundef %{{.*}}, i32 noundef 1, ptr noalias noundef %{{.*}}, ptr noundef %{{.*}}) +// LLVM: call i32 @__vfprintf_chkieee128(ptr noundef %{{.*}}, i32 noundef 1, ptr noundef %{{.*}}, ptr noundef %{{.*}}) // // LLVM: define {{.*}} void @test{{.*}} // LLVM: call i32 @__vprintfieee128.inline(ptr {{.*}} %{{.*}}, ptr {{.*}} %{{.*}}) _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
