https://github.com/adams381 created https://github.com/llvm/llvm-project/pull/191483
Map `restrict`-qualified pointer parameters to `noalias`, skipping builtins (e.g. `printf`) where OGCG doesn't apply it. OGCG only adds `noalias` from `restrict` through its calling-convention lowering path, which builtins bypass, so the `!fd->getBuiltinID()` guard keeps CIR in line. `constructFunctionArgumentAttributes` now takes `targetDecl` so it can look up `ParmVarDecl` qualifiers. Updated `asm-label-inline-builtins.c` checks to reflect the new `noalias` on the non-builtin `__vfprintf_chkieee128` call. New test: `restrict-noalias.c` (CIR / LLVM / OGCG). Made with [Cursor](https://cursor.com) >From c01652da4b5da0e4c34cfe635583eecde3961758 Mon Sep 17 00:00:00 2001 From: Adam Smith <[email protected]> Date: Fri, 10 Apr 2026 11:09:42 -0700 Subject: [PATCH] =?UTF-8?q?[CIR]=20Add=20restrict=E2=86=92noalias=20and=20?= =?UTF-8?q?skip=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 %{{.*}}) _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
