[clang] [CIR] Add support for storing into _Atomic variables (PR #165872)
Sirui Mu via cfe-commits
cfe-commits at lists.llvm.org
Fri Oct 31 09:18:13 PDT 2025
https://github.com/Lancern updated https://github.com/llvm/llvm-project/pull/165872
>From 33ae461007a0583cbddec3c526121504c6fbcde8 Mon Sep 17 00:00:00 2001
From: Sirui Mu <msrlancern at gmail.com>
Date: Fri, 31 Oct 2025 23:05:04 +0800
Subject: [PATCH] [CIR] Add support for storing into _Atomic variables
---
clang/lib/CIR/CodeGen/CIRGenAtomic.cpp | 125 ++++++++++++++++++++++++-
clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 14 ++-
clang/lib/CIR/CodeGen/CIRGenFunction.h | 7 +-
clang/test/CIR/CodeGen/atomic.c | 13 +++
4 files changed, 152 insertions(+), 7 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp b/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp
index 7db6e283ec0a5..5e4032e09bfcb 100644
--- a/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp
@@ -27,6 +27,7 @@ class AtomicInfo {
CharUnits atomicAlign;
CharUnits valueAlign;
TypeEvaluationKind evaluationKind = cir::TEK_Scalar;
+ bool useLibCall = true;
LValue lvalue;
mlir::Location loc;
@@ -62,8 +63,8 @@ class AtomicInfo {
assert(!cir::MissingFeatures::atomicInfo());
cgf.cgm.errorNYI(loc, "AtomicInfo: non-simple lvalue");
}
-
- assert(!cir::MissingFeatures::atomicUseLibCall());
+ useLibCall = !ctx.getTargetInfo().hasBuiltinAtomic(
+ atomicSizeInBits, ctx.toBits(lvalue.getAlignment()));
}
QualType getValueType() const { return valueTy; }
@@ -75,6 +76,8 @@ class AtomicInfo {
assert(!cir::MissingFeatures::atomicInfoGetAtomicPointer());
return nullptr;
}
+ bool shouldUseLibCall() const { return useLibCall; }
+ const LValue &getAtomicLValue() const { return lvalue; }
Address getAtomicAddress() const {
mlir::Type elemTy;
if (lvalue.isSimple()) {
@@ -96,6 +99,8 @@ class AtomicInfo {
bool emitMemSetZeroIfNecessary() const;
+ mlir::Value getScalarRValValueOrNull(RValue rvalue) const;
+
/// Cast the given pointer to an integer pointer suitable for atomic
/// operations on the source.
Address castToAtomicIntPointer(Address addr) const;
@@ -105,6 +110,9 @@ class AtomicInfo {
/// copy the value across.
Address convertToAtomicIntPointer(Address addr) const;
+ /// Converts a rvalue to integer value.
+ mlir::Value convertRValueToInt(RValue rvalue, bool cmpxchg = false) const;
+
/// Copy an atomic r-value into atomic-layout memory.
void emitCopyIntoMemory(RValue rvalue) const;
@@ -195,6 +203,12 @@ Address AtomicInfo::createTempAlloca() const {
return tempAlloca;
}
+mlir::Value AtomicInfo::getScalarRValValueOrNull(RValue rvalue) const {
+ if (rvalue.isScalar() && (!hasPadding() || !lvalue.isSimple()))
+ return rvalue.getValue();
+ return nullptr;
+}
+
Address AtomicInfo::castToAtomicIntPointer(Address addr) const {
auto intTy = mlir::dyn_cast<cir::IntType>(addr.getElementType());
// Don't bother with int casts if the integer size is the same.
@@ -215,6 +229,34 @@ bool AtomicInfo::emitMemSetZeroIfNecessary() const {
return false;
}
+/// Return true if \param valueTy is a type that should be casted to integer
+/// around the atomic memory operation. If \param cmpxchg is true, then the
+/// cast of a floating point type is made as that instruction can not have
+/// floating point operands. TODO: Allow compare-and-exchange and FP - see
+/// comment in CIRGenAtomicExpandPass.cpp.
+static bool shouldCastToInt(mlir::Type valueTy, bool cmpxchg) {
+ if (cir::isAnyFloatingPointType(valueTy))
+ return isa<cir::FP80Type>(valueTy) || cmpxchg;
+ return !isa<cir::IntType>(valueTy) && !isa<cir::PointerType>(valueTy);
+}
+
+mlir::Value AtomicInfo::convertRValueToInt(RValue rvalue, bool cmpxchg) const {
+ // If we've got a scalar value of the right size, try to avoid going
+ // through memory. Floats get casted if needed by AtomicExpandPass.
+ if (mlir::Value value = getScalarRValValueOrNull(rvalue)) {
+ if (!shouldCastToInt(value.getType(), cmpxchg))
+ return cgf.emitToMemory(value, valueTy);
+
+ cgf.cgm.errorNYI(
+ loc, "AtomicInfo::convertRValueToInt: cast scalar rvalue to int");
+ return nullptr;
+ }
+
+ cgf.cgm.errorNYI(
+ loc, "AtomicInfo::convertRValueToInt: cast non-scalar rvalue to int");
+ return nullptr;
+}
+
/// Copy an r-value into memory as part of storing to an atomic type.
/// This needs to create a bit-pattern suitable for atomic operations.
void AtomicInfo::emitCopyIntoMemory(RValue rvalue) const {
@@ -815,6 +857,85 @@ RValue CIRGenFunction::emitAtomicExpr(AtomicExpr *e) {
e->getExprLoc());
}
+void CIRGenFunction::emitAtomicStore(RValue rvalue, LValue dest, bool isInit) {
+ bool isVolatile = dest.isVolatileQualified();
+ cir::MemOrder order;
+ if (dest.getType()->isAtomicType()) {
+ order = cir::MemOrder::SequentiallyConsistent;
+ } else {
+ order = cir::MemOrder::Release;
+ isVolatile = true;
+ }
+ return emitAtomicStore(rvalue, dest, order, isVolatile, isInit);
+}
+
+/// Emit a store to an l-value of atomic type.
+///
+/// Note that the r-value is expected to be an r-value of the atomic type; this
+/// means that for aggregate r-values, it should include storage for any padding
+/// that was necessary.
+void CIRGenFunction::emitAtomicStore(RValue rvalue, LValue dest,
+ cir::MemOrder order, bool isVolatile,
+ bool isInit) {
+ // If this is an aggregate r-value, it should agree in type except
+ // maybe for address-space qualification.
+ auto loc = dest.getPointer().getLoc();
+ assert(!rvalue.isAggregate() ||
+ rvalue.getAggregateAddress().getElementType() ==
+ dest.getAddress().getElementType());
+
+ AtomicInfo atomics(*this, dest, loc);
+ LValue lvalue = atomics.getAtomicLValue();
+
+ // If this is an initialization, just put the value there normally.
+ if (lvalue.isSimple()) {
+ if (isInit) {
+ atomics.emitCopyIntoMemory(rvalue);
+ return;
+ }
+
+ // Check whether we should use a library call.
+ if (atomics.shouldUseLibCall()) {
+ assert(!cir::MissingFeatures::atomicUseLibCall());
+ cgm.errorNYI(loc, "emitAtomicStore: atomic store with library call");
+ return;
+ }
+
+ // Okay, we're doing this natively.
+ mlir::Value valueToStore = atomics.convertRValueToInt(rvalue);
+
+ // Do the atomic store.
+ Address addr = atomics.getAtomicAddress();
+ if (mlir::Value value = atomics.getScalarRValValueOrNull(rvalue)) {
+ if (shouldCastToInt(value.getType(), /*CmpXchg=*/false)) {
+ addr = atomics.castToAtomicIntPointer(addr);
+ valueToStore =
+ builder.createIntCast(valueToStore, addr.getElementType());
+ }
+ }
+ cir::StoreOp store = builder.createStore(loc, valueToStore, addr);
+
+ // Initializations don't need to be atomic.
+ if (!isInit) {
+ if (order == cir::MemOrder::Acquire)
+ order = cir::MemOrder::Relaxed; // Monotonic
+ else if (order == cir::MemOrder::AcquireRelease)
+ order = cir::MemOrder::Release;
+ store.setMemOrder(order);
+ }
+
+ // Other decoration.
+ if (isVolatile)
+ store.setIsVolatile(true);
+
+ assert(!cir::MissingFeatures::opLoadStoreTbaa());
+ return;
+ }
+
+ cgm.errorNYI(loc, "emitAtomicStore: non-simple atomic lvalue");
+ assert(!cir::MissingFeatures::opLoadStoreAtomic());
+}
+
void CIRGenFunction::emitAtomicInit(Expr *init, LValue dest) {
AtomicInfo atomics(*this, dest, getLoc(init->getSourceRange()));
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index 5ccb431e626ae..bb3b895f7f17f 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -311,7 +311,8 @@ static LValue emitGlobalVarDeclLValue(CIRGenFunction &cgf, const Expr *e,
void CIRGenFunction::emitStoreOfScalar(mlir::Value value, Address addr,
bool isVolatile, QualType ty,
- bool isInit, bool isNontemporal) {
+ LValueBaseInfo baseInfo, bool isInit,
+ bool isNontemporal) {
assert(!cir::MissingFeatures::opLoadStoreThreadLocal());
if (const auto *clangVecTy = ty->getAs<clang::VectorType>()) {
@@ -333,7 +334,13 @@ void CIRGenFunction::emitStoreOfScalar(mlir::Value value, Address addr,
value = emitToMemory(value, ty);
- assert(!cir::MissingFeatures::opLoadStoreAtomic());
+ assert(!cir::MissingFeatures::opLoadStoreTbaa());
+ LValue atomicLValue = LValue::makeAddr(addr, ty, baseInfo);
+ if (ty->isAtomicType() ||
+ (!isInit && isLValueSuitableForInlineAtomic(atomicLValue))) {
+ emitAtomicStore(RValue::get(value), atomicLValue, isInit);
+ return;
+ }
// Update the alloca with more info on initialization.
assert(addr.getPointer() && "expected pointer to exist");
@@ -550,7 +557,8 @@ void CIRGenFunction::emitStoreOfScalar(mlir::Value value, LValue lvalue,
}
emitStoreOfScalar(value, lvalue.getAddress(), lvalue.isVolatile(),
- lvalue.getType(), isInit, /*isNontemporal=*/false);
+ lvalue.getType(), lvalue.getBaseInfo(), isInit,
+ /*isNontemporal=*/false);
}
mlir::Value CIRGenFunction::emitLoadOfScalar(Address addr, bool isVolatile,
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index e5cecaa573a6e..39764c73d92a0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1271,6 +1271,9 @@ class CIRGenFunction : public CIRGenTypeCache {
RValue emitAtomicExpr(AtomicExpr *e);
void emitAtomicInit(Expr *init, LValue dest);
+ void emitAtomicStore(RValue rvalue, LValue dest, bool isInit);
+ void emitAtomicStore(RValue rvalue, LValue dest, cir::MemOrder order,
+ bool isVolatile, bool isInit);
AutoVarEmission emitAutoVarAlloca(const clang::VarDecl &d,
mlir::OpBuilder::InsertPoint ip = {});
@@ -1679,8 +1682,8 @@ class CIRGenFunction : public CIRGenTypeCache {
bool isInit);
void emitStoreOfScalar(mlir::Value value, Address addr, bool isVolatile,
- clang::QualType ty, bool isInit = false,
- bool isNontemporal = false);
+ clang::QualType ty, LValueBaseInfo baseInfo,
+ bool isInit = false, bool isNontemporal = false);
void emitStoreOfScalar(mlir::Value value, LValue lvalue, bool isInit);
/// Store the specified rvalue into the specified
diff --git a/clang/test/CIR/CodeGen/atomic.c b/clang/test/CIR/CodeGen/atomic.c
index 65799881a0cbe..7e819596f1bab 100644
--- a/clang/test/CIR/CodeGen/atomic.c
+++ b/clang/test/CIR/CodeGen/atomic.c
@@ -46,6 +46,19 @@ void f2(void) {
// OGCG-NEXT: store i32 42, ptr %[[SLOT]], align 4
// OGCG: }
+void f3(_Atomic(int) *p) {
+ *p = 42;
+}
+
+// CIR-LABEL: @f3
+// CIR: cir.store align(4) atomic(seq_cst) %{{.+}}, %{{.+}} : !s32i, !cir.ptr<!s32i>
+
+// LLVM-LABEL: @f3
+// LLVM: store atomic i32 42, ptr %{{.+}} seq_cst, align 4
+
+// OGCG-LABEL: @f3
+// OGCG: store atomic i32 42, ptr %{{.+}} seq_cst, align 4
+
void load(int *ptr) {
int x;
__atomic_load(ptr, &x, __ATOMIC_RELAXED);
More information about the cfe-commits
mailing list