[clang] [CIR] Add initial support for atomic types (PR #152923)
Sirui Mu via cfe-commits
cfe-commits at lists.llvm.org
Mon Aug 11 05:59:07 PDT 2025
https://github.com/Lancern updated https://github.com/llvm/llvm-project/pull/152923
>From 2aca18e40471552400218f0bf6f50a02209509da Mon Sep 17 00:00:00 2001
From: Sirui Mu <msrlancern at gmail.com>
Date: Sun, 10 Aug 2025 23:53:29 +0800
Subject: [PATCH] [CIR] Add initial support for atomic types
This patch adds the initial support for C11 atomic types, including:
- Convert QualType that represents atomic types to CIR types;
- Start emitting code for atomic value initializers.
---
clang/include/clang/CIR/MissingFeatures.h | 13 ++
clang/lib/CIR/CodeGen/CIRGenAtomic.cpp | 227 +++++++++++++++++++++
clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 7 +-
clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 4 +
clang/lib/CIR/CodeGen/CIRGenFunction.h | 5 +-
clang/lib/CIR/CodeGen/CIRGenTypes.cpp | 10 +
clang/lib/CIR/CodeGen/CIRGenValue.h | 1 +
clang/lib/CIR/CodeGen/CMakeLists.txt | 1 +
clang/test/CIR/CodeGen/atomic.c | 47 +++++
9 files changed, 312 insertions(+), 3 deletions(-)
create mode 100644 clang/lib/CIR/CodeGen/CIRGenAtomic.cpp
create mode 100644 clang/test/CIR/CodeGen/atomic.c
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index fcc8ce7caf111..11461d3693d44 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -162,6 +162,17 @@ struct MissingFeatures {
static bool addressIsKnownNonNull() { return false; }
static bool addressPointerAuthInfo() { return false; }
+ // Atomic
+ static bool convertAtomicType() { return false; }
+ static bool atomicExpr() { return false; }
+ static bool atomicInitComplex() { return false; }
+ static bool atomicInitAggregate() { return false; }
+ static bool atomicCopyAggregateIntoAtomic() { return false; }
+ static bool atomicCopyComplexIntoAtomic() { return false; }
+ static bool atomicInfo() { return false; }
+ static bool atomicInfoGetAtomicPointer() { return false; }
+ static bool atomicInfoGetAtomicAddress() { return false; }
+
// Misc
static bool abiArgInfo() { return false; }
static bool addHeapAllocSiteMetadata() { return false; }
@@ -197,6 +208,7 @@ struct MissingFeatures {
static bool cudaSupport() { return false; }
static bool cxxRecordStaticMembers() { return false; }
static bool dataLayoutTypeAllocSize() { return false; }
+ static bool dataLayoutTypeStoreSize() { return false; }
static bool deferredCXXGlobalInit() { return false; }
static bool ehCleanupFlags() { return false; }
static bool ehCleanupScope() { return false; }
@@ -232,6 +244,7 @@ struct MissingFeatures {
static bool objCBlocks() { return false; }
static bool objCGC() { return false; }
static bool objCLifetime() { return false; }
+ static bool openCL() { return false; }
static bool openMP() { return false; }
static bool opGlobalViewAttr() { return false; }
static bool opTBAA() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp b/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp
new file mode 100644
index 0000000000000..0cd9a22b1b7cc
--- /dev/null
+++ b/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp
@@ -0,0 +1,227 @@
+//===--- CIRGenAtomic.cpp - Emit CIR for atomic operations ----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the code for emitting atomic operations.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CIRGenFunction.h"
+#include "clang/CIR/MissingFeatures.h"
+
+using namespace clang;
+using namespace clang::CIRGen;
+using namespace cir;
+
+namespace {
+class AtomicInfo {
+ CIRGenFunction &cgf;
+ QualType atomicTy;
+ QualType valueTy;
+ uint64_t atomicSizeInBits = 0;
+ uint64_t valueSizeInBits = 0;
+ CharUnits atomicAlign;
+ CharUnits valueAlign;
+ TypeEvaluationKind evaluationKind = cir::TEK_Scalar;
+ LValue lvalue;
+ mlir::Location loc;
+
+public:
+ AtomicInfo(CIRGenFunction &cgf, LValue &lvalue, mlir::Location loc)
+ : cgf(cgf), loc(loc) {
+ assert(!lvalue.isGlobalReg());
+ ASTContext &ctx = cgf.getContext();
+ if (lvalue.isSimple()) {
+ atomicTy = lvalue.getType();
+ if (auto *ty = atomicTy->getAs<AtomicType>())
+ valueTy = ty->getValueType();
+ else
+ valueTy = atomicTy;
+ evaluationKind = cgf.getEvaluationKind(valueTy);
+
+ TypeInfo valueTypeInfo = ctx.getTypeInfo(valueTy);
+ TypeInfo atomicTypeInfo = ctx.getTypeInfo(atomicTy);
+ uint64_t valueAlignInBits = valueTypeInfo.Align;
+ uint64_t atomicAlignInBits = atomicTypeInfo.Align;
+ valueSizeInBits = valueTypeInfo.Width;
+ atomicSizeInBits = atomicTypeInfo.Width;
+ assert(valueSizeInBits <= atomicSizeInBits);
+ assert(valueAlignInBits <= atomicAlignInBits);
+
+ atomicAlign = ctx.toCharUnitsFromBits(atomicAlignInBits);
+ valueAlign = ctx.toCharUnitsFromBits(valueAlignInBits);
+ if (lvalue.getAlignment().isZero())
+ lvalue.setAlignment(atomicAlign);
+
+ this->lvalue = lvalue;
+ } else {
+ assert(!cir::MissingFeatures::atomicInfo());
+ }
+
+ assert(!cir::MissingFeatures::atomicInfo());
+ }
+
+ QualType getValueType() const { return valueTy; }
+ CharUnits getAtomicAlignment() const { return atomicAlign; }
+ TypeEvaluationKind getEvaluationKind() const { return evaluationKind; }
+ mlir::Value getAtomicPointer() const {
+ if (lvalue.isSimple())
+ return lvalue.getPointer();
+ assert(!cir::MissingFeatures::atomicInfoGetAtomicPointer());
+ return nullptr;
+ }
+ Address getAtomicAddress() const {
+ mlir::Type elemTy;
+ if (lvalue.isSimple()) {
+ elemTy = lvalue.getAddress().getElementType();
+ } else {
+ assert(!cir::MissingFeatures::atomicInfoGetAtomicAddress());
+ }
+ return Address(getAtomicPointer(), elemTy, getAtomicAlignment());
+ }
+
+ /// Is the atomic size larger than the underlying value type?
+ ///
+ /// Note that the absence of padding does not mean that atomic
+ /// objects are completely interchangeable with non-atomic
+ /// objects: we might have promoted the alignment of a type
+ /// without making it bigger.
+ bool hasPadding() const { return (valueSizeInBits != atomicSizeInBits); }
+
+ bool emitMemSetZeroIfNecessary() const;
+
+ /// Copy an atomic r-value into atomic-layout memory.
+ void emitCopyIntoMemory(RValue rvalue) const;
+
+ /// Project an l-value down to the value field.
+ LValue projectValue() const {
+ assert(lvalue.isSimple());
+ Address addr = getAtomicAddress();
+ if (hasPadding())
+ llvm_unreachable("NYI");
+
+ assert(!cir::MissingFeatures::opTBAA());
+ return LValue::makeAddr(addr, getValueType(), lvalue.getBaseInfo());
+ }
+
+private:
+ bool requiresMemSetZero(mlir::Type ty) const;
+};
+} // namespace
+
+/// Does a store of the given IR type modify the full expected width?
+static bool isFullSizeType(CIRGenModule &cgm, mlir::Type ty,
+ uint64_t expectedSize) {
+ assert(!cir::MissingFeatures::dataLayoutTypeStoreSize());
+ return true;
+}
+
+/// Does the atomic type require memsetting to zero before initialization?
+///
+/// The IR type is provided as a way of making certain queries faster.
+bool AtomicInfo::requiresMemSetZero(mlir::Type ty) const {
+ // If the atomic type has size padding, we definitely need a memset.
+ if (hasPadding())
+ return true;
+
+ // Otherwise, do some simple heuristics to try to avoid it:
+ switch (getEvaluationKind()) {
+ // For scalars and complexes, check whether the store size of the
+ // type uses the full size.
+ case cir::TEK_Scalar:
+ return !isFullSizeType(cgf.cgm, ty, atomicSizeInBits);
+ case cir::TEK_Complex:
+ llvm_unreachable("NYI");
+
+ // Padding in structs has an undefined bit pattern. User beware.
+ case cir::TEK_Aggregate:
+ return false;
+ }
+ llvm_unreachable("bad evaluation kind");
+}
+
+bool AtomicInfo::emitMemSetZeroIfNecessary() const {
+ assert(lvalue.isSimple());
+ Address addr = lvalue.getAddress();
+ if (!requiresMemSetZero(addr.getElementType()))
+ return false;
+
+ llvm_unreachable("NYI");
+}
+
+/// 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 {
+ assert(lvalue.isSimple());
+
+ // If we have an r-value, the rvalue should be of the atomic type,
+ // which means that the caller is responsible for having zeroed
+ // any padding. Just do an aggregate copy of that type.
+ if (rvalue.isAggregate()) {
+ assert(!cir::MissingFeatures::atomicCopyAggregateIntoAtomic());
+ cgf.cgm.errorNYI("copying aggregate into atomic lvalue is NYI");
+ return;
+ }
+
+ // Okay, otherwise we're copying stuff.
+
+ // Zero out the buffer if necessary.
+ emitMemSetZeroIfNecessary();
+
+ // Drill past the padding if present.
+ LValue tempLValue = projectValue();
+
+ // Okay, store the rvalue in.
+ if (rvalue.isScalar()) {
+ cgf.emitStoreOfScalar(rvalue.getValue(), tempLValue, /*isInit=*/true);
+ } else {
+ assert(!cir::MissingFeatures::atomicCopyComplexIntoAtomic());
+ cgf.cgm.errorNYI("copying complex into atomic lvalue is NYI");
+ }
+}
+
+RValue CIRGenFunction::emitAtomicExpr(AtomicExpr *e) {
+ QualType atomicTy = e->getPtr()->getType()->getPointeeType();
+ QualType memTy = atomicTy;
+ if (const auto *ty = atomicTy->getAs<AtomicType>())
+ memTy = ty->getValueType();
+
+ Address ptr = emitPointerWithAlignment(e->getPtr());
+
+ assert(!cir::MissingFeatures::openCL());
+ if (e->getOp() == AtomicExpr::AO__c11_atomic_init) {
+ LValue lvalue = makeAddrLValue(ptr, atomicTy);
+ emitAtomicInit(e->getVal1(), lvalue);
+ return RValue::get(nullptr);
+ }
+
+ assert(!cir::MissingFeatures::atomicExpr());
+ cgm.errorNYI(e->getSourceRange(), "atomic expr is NYI");
+ return RValue::get(nullptr);
+}
+
+void CIRGenFunction::emitAtomicInit(Expr *init, LValue dest) {
+ AtomicInfo atomics(*this, dest, getLoc(init->getSourceRange()));
+
+ switch (atomics.getEvaluationKind()) {
+ case cir::TEK_Scalar: {
+ mlir::Value value = emitScalarExpr(init);
+ atomics.emitCopyIntoMemory(RValue::get(value));
+ return;
+ }
+
+ case cir::TEK_Complex:
+ assert(!cir::MissingFeatures::atomicInitComplex());
+ return;
+
+ case cir::TEK_Aggregate:
+ assert(!cir::MissingFeatures::atomicInitAggregate());
+ return;
+ }
+
+ llvm_unreachable("bad evaluation kind");
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index c437b14dd8d1c..a53857c019b0d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -184,8 +184,11 @@ Address CIRGenFunction::emitPointerWithAlignment(const Expr *expr,
if (const UnaryOperator *uo = dyn_cast<UnaryOperator>(expr)) {
// TODO(cir): maybe we should use cir.unary for pointers here instead.
if (uo->getOpcode() == UO_AddrOf) {
- cgm.errorNYI(expr->getSourceRange(), "emitPointerWithAlignment: unary &");
- return Address::invalid();
+ LValue lv = emitLValue(uo->getSubExpr());
+ if (baseInfo)
+ *baseInfo = lv.getBaseInfo();
+ assert(!cir::MissingFeatures::opTBAA());
+ return lv.getAddress();
}
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 3e06513bb6cf0..e0015f505e9ed 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -1060,6 +1060,10 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
return maybePromoteBoolResult(resOp.getResult(), resTy);
}
+
+ mlir::Value VisitAtomicExpr(AtomicExpr *e) {
+ return cgf.emitAtomicExpr(e).getValue();
+ }
};
LValue ScalarExprEmitter::emitCompoundAssignLValue(
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 065039ec041e0..37f5c1a35198e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -893,6 +893,9 @@ class CIRGenFunction : public CIRGenTypeCache {
Address emitArrayToPointerDecay(const Expr *array);
+ RValue emitAtomicExpr(AtomicExpr *e);
+ void emitAtomicInit(Expr *init, LValue dest);
+
AutoVarEmission emitAutoVarAlloca(const clang::VarDecl &d,
mlir::OpBuilder::InsertPoint ip = {});
@@ -1197,7 +1200,7 @@ class CIRGenFunction : public CIRGenTypeCache {
/// reasonable to just ignore the returned alignment when it isn't from an
/// explicit source.
Address emitPointerWithAlignment(const clang::Expr *expr,
- LValueBaseInfo *baseInfo);
+ LValueBaseInfo *baseInfo = nullptr);
/// Emits a reference binding to the passed in expression.
RValue emitReferenceBindingToExpr(const Expr *e);
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
index 2084b6d9e8989..baaf7cb50b96f 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
@@ -491,6 +491,16 @@ mlir::Type CIRGenTypes::convertType(QualType type) {
break;
}
+ case Type::Atomic: {
+ QualType valueType = cast<AtomicType>(ty)->getValueType();
+ resultType = convertTypeForMem(valueType);
+
+ // Pad out to the inflated size if necessary.
+ assert(!cir::MissingFeatures::convertAtomicType());
+
+ break;
+ }
+
default:
cgm.errorNYI(SourceLocation(), "processing of type",
type->getTypeClassName());
diff --git a/clang/lib/CIR/CodeGen/CIRGenValue.h b/clang/lib/CIR/CodeGen/CIRGenValue.h
index 0832c4141a10f..661cecf8416b6 100644
--- a/clang/lib/CIR/CodeGen/CIRGenValue.h
+++ b/clang/lib/CIR/CodeGen/CIRGenValue.h
@@ -190,6 +190,7 @@ class LValue {
bool isSimple() const { return lvType == Simple; }
bool isVectorElt() const { return lvType == VectorElt; }
bool isBitField() const { return lvType == BitField; }
+ bool isGlobalReg() const { return lvType == GlobalReg; }
bool isVolatile() const { return quals.hasVolatile(); }
bool isVolatileQualified() const { return quals.hasVolatile(); }
diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt
index ca3a329d0c56d..29aa5372c66f1 100644
--- a/clang/lib/CIR/CodeGen/CMakeLists.txt
+++ b/clang/lib/CIR/CodeGen/CMakeLists.txt
@@ -8,6 +8,7 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
add_clang_library(clangCIR
CIRGenerator.cpp
+ CIRGenAtomic.cpp
CIRGenBuilder.cpp
CIRGenCall.cpp
CIRGenClass.cpp
diff --git a/clang/test/CIR/CodeGen/atomic.c b/clang/test/CIR/CodeGen/atomic.c
new file mode 100644
index 0000000000000..8db4ae43d7389
--- /dev/null
+++ b/clang/test/CIR/CodeGen/atomic.c
@@ -0,0 +1,47 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+
+void f1(void) {
+ _Atomic(int) x = 42;
+}
+
+// CIR-LABEL: @f1
+// CIR: %[[SLOT:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init] {alignment = 4 : i64}
+// CIR-NEXT: %[[INIT:.+]] = cir.const #cir.int<42> : !s32i
+// CIR-NEXT: cir.store align(4) %[[INIT]], %[[SLOT]] : !s32i, !cir.ptr<!s32i>
+// CIR: }
+
+// LLVM-LABEL: @f1
+// LLVM: %[[SLOT:.+]] = alloca i32, i64 1, align 4
+// LLVM-NEXT: store i32 42, ptr %[[SLOT]], align 4
+// LLVM: }
+
+// OGCG-LABEL: @f1
+// OGCG: %[[SLOT:.+]] = alloca i32, align 4
+// OGCG-NEXT: store i32 42, ptr %[[SLOT]], align 4
+// OGCG: }
+
+void f2(void) {
+ _Atomic(int) x;
+ __c11_atomic_init(&x, 42);
+}
+
+// CIR-LABEL: @f2
+// CIR: %[[SLOT:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["x"] {alignment = 4 : i64}
+// CIR-NEXT: %[[INIT:.+]] = cir.const #cir.int<42> : !s32i
+// CIR-NEXT: cir.store align(4) %[[INIT]], %[[SLOT]] : !s32i, !cir.ptr<!s32i>
+// CIR: }
+
+// LLVM-LABEL: @f2
+// LLVM: %[[SLOT:.+]] = alloca i32, i64 1, align 4
+// LLVM-NEXT: store i32 42, ptr %[[SLOT]], align 4
+// LLVM: }
+
+// OGCG-LABEL: @f2
+// OGCG: %[[SLOT:.+]] = alloca i32, align 4
+// OGCG-NEXT: store i32 42, ptr %[[SLOT]], align 4
+// OGCG: }
More information about the cfe-commits
mailing list