[CIR] Add support for storing into _Atomic variables (#165872)

This commit is contained in:
Sirui Mu 2025-11-05 19:32:34 +08:00 committed by GitHub
parent e8564830c1
commit c1dc064ba0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 162 additions and 8 deletions

View File

@ -180,6 +180,8 @@ struct MissingFeatures {
static bool atomicSyncScopeID() { return false; }
static bool atomicTypes() { return false; }
static bool atomicUseLibCall() { return false; }
static bool atomicMicrosoftVolatile() { return false; }
static bool atomicOpenMP() { return false; }
// Global ctor handling
static bool globalCtorLexOrder() { return false; }

View File

@ -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 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
/// 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.
@ -211,10 +225,38 @@ bool AtomicInfo::emitMemSetZeroIfNecessary() const {
return false;
cgf.cgm.errorNYI(loc,
"AtomicInfo::emitMemSetZeroIfNecessary: emit memset zero");
"AtomicInfo::emitMemSetZeroIfNecaessary: emit memset zero");
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,79 @@ RValue CIRGenFunction::emitAtomicExpr(AtomicExpr *e) {
e->getExprLoc());
}
void CIRGenFunction::emitAtomicStore(RValue rvalue, LValue dest, bool isInit) {
bool isVolatile = dest.isVolatileQualified();
auto order = cir::MemOrder::SequentiallyConsistent;
if (!dest.getType()->isAtomicType()) {
assert(!cir::MissingFeatures::atomicMicrosoftVolatile());
}
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.
mlir::Location loc = dest.getPointer().getLoc();
assert(!rvalue.isAggregate() ||
rvalue.getAggregateAddress().getElementType() ==
dest.getAddress().getElementType());
AtomicInfo atomics(*this, dest, loc);
LValue lvalue = atomics.getAtomicLValue();
if (lvalue.isSimple()) {
// If this is an initialization, just put the value there normally.
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) {
assert(!cir::MissingFeatures::atomicOpenMP());
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()));

View File

@ -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,

View File

@ -1271,6 +1271,9 @@ public:
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 = {});
@ -1680,8 +1683,8 @@ public:
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

View File

@ -46,6 +46,32 @@ 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 f4(_Atomic(float) *p) {
*p = 3.14;
}
// CIR-LABEL: @f4
// CIR: cir.store align(4) atomic(seq_cst) %{{.+}}, %{{.+}} : !cir.float, !cir.ptr<!cir.float>
// LLVM-LABEL: @f4
// LLVM: store atomic float 0x40091EB860000000, ptr %{{.+}} seq_cst, align 4
// OGCG-LABEL: @f4
// OGCG: store atomic float 0x40091EB860000000, ptr %{{.+}} seq_cst, align 4
void load(int *ptr) {
int x;
__atomic_load(ptr, &x, __ATOMIC_RELAXED);