[clang] [CIR] Handle __declspec(noalias) as argmem side effects (PR #221819)
David Rivera via cfe-commits
cfe-commits at lists.llvm.org
Mon Sep 7 13:03:45 PDT 2026
https://github.com/RiverDave created https://github.com/llvm/llvm-project/pull/221819
## Summary
- Map `__declspec(noalias)` (`NoAliasAttr`) to `SideEffect::ArgMem`.
- Lower that to `nounwind memory(argmem: readwrite, inaccessiblemem: readwrite)`, matching classic codegen (no `willreturn`).
- New CIR lit `ms-declspec-noalias.c` (Linux triple + `-fms-extensions`; Windows ABI is still NYI in CIR) plus `call.cir` parse coverage.
Soft overlap with the operator-new PR: both add a `SideEffect` case in `CIRAttrs.td` and a switch arm in `LowerToLLVM.cpp`. Whichever lands second should rebase those two files.
## Test plan
- [x] `llvm-lit clang/test/CIR/CodeGen/ms-declspec-noalias.c clang/test/CIR/IR/call.cir clang/test/CIR/CodeGen/side-effect.cpp`
- [ ] Pre-merge Clang CIR checks
Made with [Cursor](https://cursor.com)
>From fed6cdc99254f86936cf6fe2853cb9fe6eddb836 Mon Sep 17 00:00:00 2001
From: David Rivera <davidriverg at gmail.com>
Date: Mon, 7 Sep 2026 16:02:50 -0400
Subject: [PATCH] [CIR] Handle __declspec(noalias) as argmem side effects
Match classic codegen: NoAliasAttr becomes nounwind plus
memory(argmem: readwrite, inaccessiblemem: readwrite).
---
.../include/clang/CIR/Dialect/IR/CIRAttrs.td | 8 ++++-
clang/lib/CIR/CodeGen/CIRGenCall.cpp | 4 +++
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 23 ++++++++++++++
clang/test/CIR/CodeGen/ms-declspec-noalias.c | 30 +++++++++++++++++++
clang/test/CIR/IR/call.cir | 2 ++
5 files changed, 66 insertions(+), 1 deletion(-)
create mode 100644 clang/test/CIR/CodeGen/ms-declspec-noalias.c
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index fa90ceba97115..dbe311fffb67a 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -1875,7 +1875,8 @@ def CIR_SideEffect : CIR_I32EnumAttr<
"SideEffect", "allowed side effects of a function", [
I32EnumAttrCase<"All", 0, "all">,
I32EnumAttrCase<"Pure", 1, "pure">,
- I32EnumAttrCase<"Const", 2, "const">
+ I32EnumAttrCase<"Const", 2, "const">,
+ I32EnumAttrCase<"ArgMem", 4, "argmem">
]> {
let description = [{
The side effect attribute specifies the possible side effects of a function
@@ -1889,6 +1890,10 @@ def CIR_SideEffect : CIR_I32EnumAttr<
`__attribute__((pure))`.
- const: The function or callee may not read or write data from memory. This
has the same effect as the GNU C/C++ attribute `__attribute__((const))`.
+ - argmem: The function or callee may only access argument memory and
+ inaccessible memory. This matches LLVM
+ `memory(argmem: readwrite, inaccessiblemem: readwrite)` used for
+ `__declspec(noalias)`.
Examples:
@@ -1896,6 +1901,7 @@ def CIR_SideEffect : CIR_I32EnumAttr<
%2 = cir.call @add(%0, %1) : (!s32i, !s32i) -> !s32i
%2 = cir.call @add(%0, %1) : (!s32i, !s32i) -> !s32i side_effect(pure)
%2 = cir.call @add(%0, %1) : (!s32i, !s32i) -> !s32i side_effect(const)
+ %2 = cir.call @f(%0) : (!cir.ptr<!s32i>) -> () side_effect(argmem)
```
}];
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index 3a4b7cecf2e08..a8644c03c1ba7 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -398,6 +398,10 @@ void CIRGenModule::constructAttributeList(
} else if (targetDecl->hasAttr<PureAttr>()) {
// gcc specifies that 'pure' functions cannot have infinite loops.
sideEffect = cir::SideEffect::Pure;
+ } else if (targetDecl->hasAttr<NoAliasAttr>()) {
+ // __declspec(noalias): inaccessible-or-arg memory only, and nounwind.
+ sideEffect = cir::SideEffect::ArgMem;
+ addUnitAttr(cir::CIRDialect::getNoThrowAttrName());
}
attrs.set(cir::CIRDialect::getSideEffectAttrName(),
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 6f7509c363fd3..894c308c3ab40 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -457,6 +457,18 @@ void convertSideEffectForCall(mlir::Operation *callOp, bool isNothrow,
noUnwind = true;
willReturn = true;
break;
+
+ case cir::SideEffect::ArgMem:
+ memoryEffect = mlir::LLVM::MemoryEffectsAttr::get(
+ callOp->getContext(), /*other=*/ModRefInfo::NoModRef,
+ /*argMem=*/ModRefInfo::ModRef,
+ /*inaccessibleMem=*/ModRefInfo::ModRef,
+ /*errnoMem=*/ModRefInfo::NoModRef,
+ /*targetMem0=*/ModRefInfo::NoModRef,
+ /*targetMem1=*/ModRefInfo::NoModRef);
+ noUnwind = true;
+ willReturn = false;
+ break;
}
noReturn = callOp->hasAttr(CIRDialect::getNoReturnAttrName());
@@ -2824,6 +2836,17 @@ mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewrite(
fn.setNoUnwind(true);
fn.setWillReturn(true);
break;
+ case cir::SideEffect::ArgMem:
+ fn.setMemoryEffectsAttr(mlir::LLVM::MemoryEffectsAttr::get(
+ fn.getContext(),
+ /*other=*/mlir::LLVM::ModRefInfo::NoModRef,
+ /*argMem=*/mlir::LLVM::ModRefInfo::ModRef,
+ /*inaccessibleMem=*/mlir::LLVM::ModRefInfo::ModRef,
+ /*errnoMem=*/mlir::LLVM::ModRefInfo::NoModRef,
+ /*targetMem0=*/mlir::LLVM::ModRefInfo::NoModRef,
+ /*targetMem1=*/mlir::LLVM::ModRefInfo::NoModRef));
+ fn.setNoUnwind(true);
+ break;
}
}
diff --git a/clang/test/CIR/CodeGen/ms-declspec-noalias.c b/clang/test/CIR/CodeGen/ms-declspec-noalias.c
new file mode 100644
index 0000000000000..fd0e8276d4fdc
--- /dev/null
+++ b/clang/test/CIR/CodeGen/ms-declspec-noalias.c
@@ -0,0 +1,30 @@
+// CIR does not yet implement the Windows C++ ABI, so exercise __declspec(noalias)
+// on a Linux triple with -fms-extensions.
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fms-extensions -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 -fms-extensions -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 -fms-extensions -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+
+__declspec(noalias) void noalias_callee(int *x);
+void noalias_caller(int *x) { noalias_callee(x); }
+
+// CIR: cir.func {{.*}} @noalias_caller
+// CIR: cir.call @noalias_callee(%{{.*}}) nothrow side_effect(argmem)
+// CIR: cir.func {{.*}}@noalias_callee(!cir.ptr<!s32i> {{.*}}) side_effect(argmem)
+
+// LLVM: call void @noalias_callee({{.*}}) [[NA:#[0-9]+]]
+// OGCG: call void @noalias_callee({{.*}}) [[NA:#[0-9]+]]
+
+__declspec(noalias) void noalias_def(int *x) {}
+
+// CIR: cir.func {{.*}} @noalias_def(%{{.*}}) side_effect(argmem)
+
+// LLVM: define dso_local void @noalias_def({{.*}}) [[NA_DEF:#[0-9]+]]
+// OGCG: define dso_local void @noalias_def({{.*}}) [[NA_DEF:#[0-9]+]]
+
+// LLVM-DAG: attributes [[NA]] = { nounwind memory(argmem: readwrite, inaccessiblemem: readwrite)
+// OGCG-DAG: attributes [[NA]] = { nounwind memory(argmem: readwrite, inaccessiblemem: readwrite)
+// LLVM-DAG: attributes [[NA_DEF]] = { {{.*}}nounwind{{.*}}memory(argmem: readwrite, inaccessiblemem: readwrite)
+// OGCG-DAG: attributes [[NA_DEF]] = { {{.*}}nounwind{{.*}}memory(argmem: readwrite, inaccessiblemem: readwrite)
diff --git a/clang/test/CIR/IR/call.cir b/clang/test/CIR/IR/call.cir
index 59f28be36846f..58fe1dc767307 100644
--- a/clang/test/CIR/IR/call.cir
+++ b/clang/test/CIR/IR/call.cir
@@ -10,6 +10,7 @@ cir.func @f2() {
cir.call @f1() : () -> ()
cir.call @f1() side_effect(pure) : () -> ()
cir.call @f1() side_effect(const) : () -> ()
+ cir.call @f1() side_effect(argmem) : () -> ()
cir.return
}
@@ -17,6 +18,7 @@ cir.func @f2() {
// CHECK-NEXT: cir.call @f1() : () -> ()
// CHECK-NEXT: cir.call @f1() side_effect(pure) : () -> ()
// CHECK-NEXT: cir.call @f1() side_effect(const) : () -> ()
+// CHECK-NEXT: cir.call @f1() side_effect(argmem) : () -> ()
// CHECK-NEXT: cir.return
// CHECK-NEXT: }
More information about the cfe-commits
mailing list