[clang] [CIR] Disallow mem2reg for volatile/atomic loads and stores (PR #212262)
Sirui Mu via cfe-commits
cfe-commits at lists.llvm.org
Mon Jul 27 07:40:26 PDT 2026
https://github.com/Lancern created https://github.com/llvm/llvm-project/pull/212262
Current mem2reg implementation for `cir.load` and `cir.store` does not check whether the load/store is volatile or atomic. Fix this.
>From 35d23ed82794da7b0709db9af3d45e88b2dd11a8 Mon Sep 17 00:00:00 2001
From: Sirui Mu <msrlancern at gmail.com>
Date: Mon, 27 Jul 2026 22:36:51 +0800
Subject: [PATCH] [CIR] Disallow mem2reg for volatile/atomic loads and stores
Current mem2reg implementation for `cir.load` and `cir.store` does not check
whether the load/store is volatile or atomic. Fix this.
---
clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp | 14 +++++-
clang/test/CIR/Transforms/mem2reg.cir | 55 ++++++++++++++++++++--
2 files changed, 63 insertions(+), 6 deletions(-)
diff --git a/clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp b/clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp
index 73b35c7f00c2d..d6de6b6e80799 100644
--- a/clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp
@@ -72,6 +72,11 @@ bool cir::LoadOp::canUsesBeRemoved(
const DataLayout &dataLayout) {
if (blockingUses.size() != 1)
return false;
+
+ // Volatile load or atomic load should not be removed.
+ if (getIsVolatile() || getMemOrder().has_value())
+ return false;
+
Value blockingUse = (*blockingUses.begin())->get();
return blockingUse == slot.ptr && getAddr() == slot.ptr &&
getType() == slot.elemType;
@@ -106,6 +111,11 @@ bool cir::StoreOp::canUsesBeRemoved(
const DataLayout &dataLayout) {
if (blockingUses.size() != 1)
return false;
+
+ // Volatile store or atomic store should not be removed.
+ if (getIsVolatile() || getMemOrder().has_value())
+ return false;
+
Value blockingUse = (*blockingUses.begin())->get();
return blockingUse == slot.ptr && getAddr() == slot.ptr &&
getValue() != slot.ptr && slot.elemType == getValue().getType();
@@ -141,8 +151,8 @@ DeletionKind cir::CopyOp::removeBlockingUses(
const DataLayout &dataLayout) {
if (loadsFrom(slot))
cir::StoreOp::create(builder, getLoc(), reachingDefinition, getDst(),
- /*isVolatile=*/false,
- /*isNontemporal=*/false,
+ /*is_volatile=*/false,
+ /*is_nontemporal=*/false,
/*alignment=*/mlir::IntegerAttr{},
/*sync_scope=*/cir::SyncScopeKindAttr(),
/*mem-order=*/cir::MemOrderAttr());
diff --git a/clang/test/CIR/Transforms/mem2reg.cir b/clang/test/CIR/Transforms/mem2reg.cir
index 2319de275700c..84f83595f1c6c 100644
--- a/clang/test/CIR/Transforms/mem2reg.cir
+++ b/clang/test/CIR/Transforms/mem2reg.cir
@@ -1,7 +1,4 @@
-// RUN: cir-opt %s -cir-flatten-cfg -mem2reg -o - | FileCheck %s \
-// RUN: --implicit-check-not=cir.alloca \
-// RUN: --implicit-check-not=cir.load \
-// RUN: --implicit-check-not=cir.store
+// RUN: cir-opt %s -cir-flatten-cfg -mem2reg -o - | FileCheck %s
!s32i = !cir.int<s, 32>
@@ -20,4 +17,54 @@ module {
%4 = cir.load %0 : !cir.ptr<!s32i>, !s32i
cir.return %4 : !s32i
}
+
+ // CHECK-LABEL: cir.func @do_not_promote_volatile
+ cir.func @do_not_promote_volatile_load() -> !s32i {
+ // CHECK: %[[ALLOCA:.*]] = cir.alloca "y" align(4) init : !cir.ptr<!s32i>
+ // CHECK: %[[RET:.+]] = cir.load volatile %[[ALLOCA]] : !cir.ptr<!s32i>, !s32i
+ // CHECK: cir.return %[[RET]] : !s32i
+ %0 = cir.alloca "y" align(4) init : !cir.ptr<!s32i>
+ %1 = cir.const #cir.int<42> : !s32i
+ cir.store %1, %0 : !s32i, !cir.ptr<!s32i>
+ %2 = cir.load volatile %0 : !cir.ptr<!s32i>, !s32i
+ cir.return %2 : !s32i
+ }
+
+ // CHECK-LABEL: cir.func @do_not_promote_volatile_store
+ cir.func @do_not_promote_volatile_store() -> !s32i {
+ // CHECK: %[[ALLOCA:.*]] = cir.alloca "y" align(4) init : !cir.ptr<!s32i>
+ // CHECK: cir.store volatile %{{.+}}, %[[ALLOCA]] : !s32i, !cir.ptr<!s32i>
+ // CHECK: %[[RET:.+]] = cir.load %[[ALLOCA]] : !cir.ptr<!s32i>, !s32i
+ // CHECK: cir.return %[[RET]] : !s32i
+ %0 = cir.alloca "y" align(4) init : !cir.ptr<!s32i>
+ %1 = cir.const #cir.int<42> : !s32i
+ cir.store volatile %1, %0 : !s32i, !cir.ptr<!s32i>
+ %2 = cir.load %0 : !cir.ptr<!s32i>, !s32i
+ cir.return %2 : !s32i
+ }
+
+ // CHECK-LABEL: cir.func @do_not_promote_atomic_load
+ cir.func @do_not_promote_atomic_load() -> !s32i {
+ // CHECK: %[[ALLOCA:.*]] = cir.alloca "y" align(4) init : !cir.ptr<!s32i>
+ // CHECK: %[[RET:.+]] = cir.load atomic(acquire) %[[ALLOCA]] : !cir.ptr<!s32i>, !s32i
+ // CHECK: cir.return %[[RET]] : !s32i
+ %0 = cir.alloca "y" align(4) init : !cir.ptr<!s32i>
+ %1 = cir.const #cir.int<42> : !s32i
+ cir.store %1, %0 : !s32i, !cir.ptr<!s32i>
+ %2 = cir.load atomic(acquire) %0 : !cir.ptr<!s32i>, !s32i
+ cir.return %2 : !s32i
+ }
+
+ // CHECK-LABEL: cir.func @do_not_promote_atomic_store
+ cir.func @do_not_promote_atomic_store() -> !s32i {
+ // CHECK: %[[ALLOCA:.*]] = cir.alloca "y" align(4) init : !cir.ptr<!s32i>
+ // CHECK: cir.store atomic(release) %{{.+}}, %[[ALLOCA]] : !s32i, !cir.ptr<!s32i>
+ // CHECK: %[[RET:.+]] = cir.load %[[ALLOCA]] : !cir.ptr<!s32i>, !s32i
+ // CHECK: cir.return %[[RET]] : !s32i
+ %0 = cir.alloca "y" align(4) init : !cir.ptr<!s32i>
+ %1 = cir.const #cir.int<42> : !s32i
+ cir.store atomic(release) %1, %0 : !s32i, !cir.ptr<!s32i>
+ %2 = cir.load %0 : !cir.ptr<!s32i>, !s32i
+ cir.return %2 : !s32i
+ }
}
More information about the cfe-commits
mailing list