[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