Skip to content

Commit 3e43205

Browse files
authored
[CIR] Add restrict→noalias on non-builtin pointer params (#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)
1 parent 6d67286 commit 3e43205

3 files changed

Lines changed: 67 additions & 8 deletions

File tree

clang/lib/CIR/CodeGen/CIRGenCall.cpp

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,8 @@ void CIRGenModule::constructAttributeList(
496496
// TODO(cir): Add loader-replaceable attribute here.
497497

498498
constructFunctionReturnAttributes(info, targetDecl, isThunk, retAttrs);
499-
constructFunctionArgumentAttributes(info, isThunk, argAttrs);
499+
constructFunctionArgumentAttributes(info, targetDecl, isThunk, attrOnCallSite,
500+
argAttrs);
500501
}
501502

502503
bool CIRGenModule::hasStrictReturn(QualType retTy, const Decl *targetDecl) {
@@ -643,13 +644,11 @@ void CIRGenModule::constructFunctionReturnAttributes(
643644
}
644645

645646
void CIRGenModule::constructFunctionArgumentAttributes(
646-
const CIRGenFunctionInfo &info, bool isThunk,
647-
llvm::MutableArrayRef<mlir::NamedAttrList> argAttrs) {
647+
const CIRGenFunctionInfo &info, const Decl *targetDecl, bool isThunk,
648+
bool attrOnCallSite, llvm::MutableArrayRef<mlir::NamedAttrList> argAttrs) {
648649
assert(!cir::MissingFeatures::abiArgInfo());
649650
// TODO(cir): classic codegen does a lot of work here based on the ABIArgInfo
650651
// to set things based on calling convention.
651-
// At the moment, only nonnull, dereferenceable, align, and noundef are being
652-
// implemented here, using similar logic to how we do so for return types.
653652

654653
if (info.isInstanceMethod() && !isThunk) {
655654
QualType thisPtrTy = info.arguments()[0];
@@ -693,8 +692,22 @@ void CIRGenModule::constructFunctionArgumentAttributes(
693692
// that seems risky at the moment. At one point we should evaluate if at least
694693
// dereferenceable, nonnull, and align can be combined.
695694
const cir::CIRDataLayout &layout = getDataLayout();
696-
for (const auto &[argAttrList, argCanType] :
697-
llvm::zip_equal(argAttrs, info.arguments())) {
695+
const auto *fd = dyn_cast_or_null<FunctionDecl>(targetDecl);
696+
697+
// Build a parallel array of ParmVarDecls aligned with argAttrs so we can
698+
// access parameter-level qualifiers (e.g. restrict) without manual index
699+
// arithmetic in the loop.
700+
SmallVector<const ParmVarDecl *> parmDecls;
701+
parmDecls.reserve(argAttrs.size());
702+
if (fd) {
703+
if (info.isInstanceMethod())
704+
parmDecls.push_back(nullptr);
705+
parmDecls.insert(parmDecls.end(), fd->param_begin(), fd->param_end());
706+
}
707+
parmDecls.resize(argAttrs.size(), nullptr);
708+
709+
for (const auto &[argAttrList, argCanType, pvd] :
710+
llvm::zip_equal(argAttrs, info.arguments(), parmDecls)) {
698711
assert(!cir::MissingFeatures::abiArgInfo());
699712
QualType argType = argCanType;
700713
const cir::ABIArgInfo argInfo = cir::ABIArgInfo::getDirect();
@@ -730,6 +743,13 @@ void CIRGenModule::constructFunctionArgumentAttributes(
730743
if (unsigned mask = getNoFPClassTestMask(getLangOpts()))
731744
argAttrList.set(mlir::LLVM::LLVMDialect::getNoFPClassAttrName(),
732745
builder.getI64IntegerAttr(mask));
746+
747+
// restrict -> noalias on definitions only (not call sites). Skip
748+
// builtins: OGCG applies restrict->noalias in EmitFunctionProlog.
749+
if (!attrOnCallSite && pvd && pvd->getType()->isPointerType() &&
750+
pvd->getType().isRestrictQualified() && !fd->getBuiltinID())
751+
argAttrList.set(mlir::LLVM::LLVMDialect::getNoAliasAttrName(),
752+
mlir::UnitAttr::get(&getMLIRContext()));
733753
}
734754
}
735755

clang/lib/CIR/CodeGen/CIRGenModule.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ class CIRGenModule : public CIRGenTypeCache {
120120
mlir::NamedAttrList &retAttrs);
121121
/// A helper for constructAttributeList that handles argument attributes.
122122
void constructFunctionArgumentAttributes(
123-
const CIRGenFunctionInfo &info, bool isThunk,
123+
const CIRGenFunctionInfo &info, const clang::Decl *targetDecl,
124+
bool isThunk, bool attrOnCallSite,
124125
llvm::MutableArrayRef<mlir::NamedAttrList> argAttrs);
125126
/// A helper function for constructAttributeList that determines whether a
126127
/// return value might have been discarded.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
3+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
4+
// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
5+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
6+
// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
7+
8+
void user_func(int *__restrict p);
9+
10+
void test_user(int *__restrict p) {
11+
user_func(p);
12+
}
13+
14+
// CIR: cir.func private @user_func(!cir.ptr<!s32i> {llvm.noalias, llvm.noundef})
15+
// CIR: cir.func {{.*}} @test_user(%arg0: !cir.ptr<!s32i> {llvm.noalias, llvm.noundef}
16+
// CIR: cir.call @user_func(%{{.*}}) : (!cir.ptr<!s32i> {llvm.noundef}) -> ()
17+
18+
// LLVM: define dso_local void @test_user(ptr noalias noundef %{{.*}})
19+
// LLVM: call void @user_func(ptr noundef %{{.*}})
20+
21+
// OGCG: define dso_local void @test_user(ptr noalias noundef %{{.*}})
22+
// OGCG: call void @user_func(ptr noundef %{{.*}})
23+
24+
int printf(const char *__restrict fmt, ...);
25+
26+
void test_builtin(const char *__restrict fmt) {
27+
printf(fmt);
28+
}
29+
30+
// Builtins must NOT get noalias from restrict (matching OGCG behavior).
31+
// CIR: cir.func {{.*}} @test_builtin(%arg0: !cir.ptr<!s8i> {llvm.noalias, llvm.noundef}
32+
// CIR: cir.call @printf(%{{.*}}) : (!cir.ptr<!s8i> {llvm.noundef}) -> !s32i
33+
34+
// LLVM: define dso_local void @test_builtin(ptr noalias noundef %{{.*}})
35+
// LLVM: call i32 (ptr, ...) @printf(ptr noundef %{{.*}})
36+
37+
// OGCG: define dso_local void @test_builtin(ptr noalias noundef %{{.*}})
38+
// OGCG: call i32 (ptr, ...) @printf(ptr noundef %{{.*}})

0 commit comments

Comments
 (0)