[clang] [CIR] Support Aggregate Atomic to non atomic with padding (PR #219680)
Amr Hesham via cfe-commits
cfe-commits at lists.llvm.org
Sat Aug 29 06:26:00 PDT 2026
https://github.com/AmrDeveloper created https://github.com/llvm/llvm-project/pull/219680
Implement support for Aggregate Atomic to non-atomic cast with padding
>From 65b09cf331d98676260bd60c27b0e2d80ae9ab64 Mon Sep 17 00:00:00 2001
From: Amr Hesham <amr96 at programmer.net>
Date: Sat, 29 Aug 2026 14:47:34 +0200
Subject: [PATCH] [CIR] Support Aggregate Atomic to non atomic with padding
---
clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp | 41 ++++++++++++--
clang/test/CIR/CodeGen/agg-atomic-cast.c | 55 ++++++++++++++++++-
2 files changed, 91 insertions(+), 5 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
index 922c4cb65e66e..2b3cf598f35a3 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
@@ -236,6 +236,17 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
void VisitLambdaExpr(LambdaExpr *e);
void VisitExprWithCleanups(ExprWithCleanups *e);
+ /// Attempt to look through various unimportant expressions to find a
+ /// cast of the given kind.
+ static Expr *findPeephole(Expr *op, CastKind kind, const ASTContext &ctx) {
+ op = op->IgnoreParenNoopCasts(ctx);
+ if (auto *castE = dyn_cast<CastExpr>(op)) {
+ if (castE->getCastKind() == kind)
+ return castE->getSubExpr();
+ }
+ return nullptr;
+ }
+
// Stubs -- These should be moved up when they are implemented.
void VisitCastExpr(CastExpr *e) {
switch (e->getCastKind()) {
@@ -282,10 +293,32 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
if (dest.isIgnored() || !cgf.cgm.isPaddedAtomicType(atomicType))
return Visit(e->getSubExpr());
- cgf.cgm.errorNYI(
- e->getSourceRange(),
- "AggExprEmitter: AtomicCast not ignored and has padded atomic type");
- return;
+ // These two cases are reverses of each other; try to peephole them.
+ CastKind peepholeTarget =
+ (isToAtomic ? CK_AtomicToNonAtomic : CK_NonAtomicToAtomic);
+ if (Expr *op =
+ findPeephole(e->getSubExpr(), peepholeTarget, cgf.getContext())) {
+ cgf.cgm.errorNYI(op->getSourceRange(),
+ "AggExprEmitter: VisitCastExpr peephole");
+ }
+
+ // If we're converting an r-value of non-atomic type to an r-value
+ // of atomic type, just emit directly into the relevant sub-object.
+ if (isToAtomic) {
+ cgf.cgm.errorNYI(e->getSourceRange(),
+ "AggExprEmitter: VisitCastExpr r-value of non-atomic "
+ "type to an r-value of atomic type");
+ return;
+ }
+
+ mlir::Location loc = cgf.getLoc(e->getExprLoc());
+ AggValueSlot atomicSlot = cgf.createAggTemp(atomicType, loc);
+ cgf.emitAggExpr(e->getSubExpr(), atomicSlot);
+
+ Address valueAddr = cgf.getBuilder().createGetMember(
+ loc, atomicSlot.getAddress(), "value_addr", 0);
+ RValue rvalue = RValue::getAggregate(valueAddr, atomicSlot.isVolatile());
+ return emitFinalDestCopy(valueType, rvalue);
}
case CK_LValueToRValue:
// If we're loading from a volatile type, force the destination
diff --git a/clang/test/CIR/CodeGen/agg-atomic-cast.c b/clang/test/CIR/CodeGen/agg-atomic-cast.c
index 1ba901d8d96ca..a6c7452ce94d3 100644
--- a/clang/test/CIR/CodeGen/agg-atomic-cast.c
+++ b/clang/test/CIR/CodeGen/agg-atomic-cast.c
@@ -18,7 +18,7 @@ void non_atomic_to_atomic_cast() {
// CIR: %[[SA_ADDR:.*]] = cir.alloca "as" {{.*}} init : !cir.ptr<!rec_S>
// CIR: cir.copy %[[S_ADDR]] align(4) to %[[SA_ADDR]] align(4) : !cir.ptr<!rec_S>
-// LLVM: %[[S_ADDR:.*]] = alloca %struct.S, align 4
+// LLVM: %[[S_ADDR:.*]] = alloca %struct.S, align 4
// LLVM: %[[SA_ADDR:.*]] = alloca %struct.S, align 4
// LLVM: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %[[SA_ADDR]], ptr align 4 %[[S_ADDR]], i64 4, i1 false)
@@ -48,3 +48,56 @@ void atomic_to_non_atomic_cast() {
// OGCG: %[[S_ADDR:.*]] = alloca %struct.S, align 4
// OGCG: %[[ATOMIC_LOAD:.*]] = load atomic i32, ptr %[[AS_ADDR]] seq_cst, align 4
// OGCG: store i32 %[[ATOMIC_LOAD]], ptr %[[S_ADDR]], align 4
+
+struct T {
+ char a, b, c;
+}; // size 3 => padded atomic representation
+
+
+struct T load_atomic_struct() {
+ _Atomic(struct T) a;
+ return a;
+}
+
+// CIR: %[[RET_ADDR:.*]] = cir.alloca "coerce" {{.*}} : !cir.ptr<!rec_T>
+// CIR: %[[RET_VAL_ADDR:.*]] = cir.alloca "__retval" {{.*}} : !cir.ptr<!rec_T>
+// CIR: %[[A_ADDR:.*]] = cir.alloca "a" {{.*}} : !cir.ptr<!rec_anon_struct>
+// CIR: %[[NON_ATOMIC_TMP_ADDR:.*]] = cir.alloca "tmp" {{.*}} : !cir.ptr<!rec_anon_struct>
+// CIR: %[[A_U32:.*]] = cir.cast bitcast %[[A_ADDR]] : !cir.ptr<!rec_anon_struct> -> !cir.ptr<!u32i>
+// CIR: %[[TMP_ATOMIC_A:.*]] = cir.load {{.*}} atomic(seq_cst) %[[A_U32]] : !cir.ptr<!u32i>, !u32i
+// CIR: %[[NON_ATOMIC_TMP:.*]] = cir.cast bitcast %[[NON_ATOMIC_TMP_ADDR]] : !cir.ptr<!rec_anon_struct> -> !cir.ptr<!u32i>
+// CIR: cir.store {{.*}} %[[TMP_ATOMIC_A]], %[[NON_ATOMIC_TMP]] : !u32i, !cir.ptr<!u32i>
+// CIR: %[[VALUE_ADDR:.*]] = cir.get_member %[[NON_ATOMIC_TMP_ADDR]][0] {name = "value_addr"} : !cir.ptr<!rec_anon_struct> -> !cir.ptr<!rec_T>
+// CIR: cir.copy %[[VALUE_ADDR]] {{.*}} to %[[RET_VAL_ADDR]] {{.*}} : !cir.ptr<!rec_T>
+// CIR: %[[TMP_RET_VAL:.*]] = cir.load %[[RET_VAL_ADDR]] : !cir.ptr<!rec_T>, !rec_T
+// CIR: cir.store %[[TMP_RET_VAL]], %[[RET_ADDR]] : !rec_T, !cir.ptr<!rec_T>
+// CIR: %[[RET_ADDR_U64:.*]] = cir.cast bitcast %[[RET_ADDR]] : !cir.ptr<!rec_T> -> !cir.ptr<!cir.int<u, 24>>
+// CIR: %[[TMP_RET:.*]] = cir.load %[[RET_ADDR_U64]] : !cir.ptr<!cir.int<u, 24>>, !cir.int<u, 24>
+// CIR: cir.return %[[TMP_RET]] : !cir.int<u, 24>
+
+// The difference between LLVM and OGCG in type from struct.T to i24 is due to missing ABI lowering.
+
+// LLVM: %[[RET_ADDR:.*]] = alloca %struct.T, align 4
+// LLVM: %[[RET_VAL_ADDR:.*]] = alloca %struct.T, align 1
+// LLVM: %[[A_ADDR:.*]] = alloca { %struct.T, [1 x i8] }, align 4
+// LLVM: %[[NON_ATOMIC_TMP:.*]] = alloca { %struct.T, [1 x i8] }, align 4
+// LLVM: %[[TMP_A:.*]] = load atomic i32, ptr %[[A_ADDR]] seq_cst, align 4
+// LLVM: store i32 %[[TMP_A]], ptr %[[NON_ATOMIC_TMP]], align 4
+// LLVM: %[[NON_ATOMIC_PTR:.*]] = getelementptr inbounds nuw { %struct.T, [1 x i8] }, ptr %[[NON_ATOMIC_TMP]], i32 0, i32 0
+// LLVM: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %[[RET_VAL_ADDR]], ptr align 4 %[[NON_ATOMIC_PTR]], i64 3, i1 false)
+// LLVM: %[[TMP_RET_VAL:.*]] = load %struct.T, ptr %[[RET_VAL_ADDR]], align 1
+// LLVM: store %struct.T %[[TMP_RET_VAL]], ptr %[[RET_ADDR]], align 1
+// LLVM: %[[TMP_RET:.*]] = load i24, ptr %[[RET_ADDR]], align 4
+// LLVM: ret i24 %[[TMP_RET]]
+
+// OGCG: %[[RET_VAL_ADDR:.*]] = alloca %struct.T, align 1
+// OGCG: %[[A_ADDR:.*]] = alloca { %struct.T, [1 x i8] }, align 4
+// OGCG: %[[NON_ATOMIC_TMP:.*]] = alloca { %struct.T, [1 x i8] }, align 4
+// OGCG: %[[RET_ADDR:.*]] = alloca i24, align 4
+// OGCG: %[[TMP_A:.*]] = load atomic i32, ptr %[[A_ADDR]] seq_cst, align 4
+// OGCG: store i32 %[[TMP_A]], ptr %[[NON_ATOMIC_TMP]], align 4
+// OGCG: %[[NON_ATOMIC_PTR:.*]] = getelementptr inbounds nuw { %struct.T, [1 x i8] }, ptr %atomic-to-nonatomic.temp, i32 0, i32 0
+// OGCG: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %[[RET_VAL_ADDR]], ptr align 4 %[[NON_ATOMIC_PTR]], i64 3, i1 false)
+// OGCG: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %[[RET_ADDR]], ptr align 1 %[[RET_VAL_ADDR]], i64 3, i1 false)
+// OGCG: %[[TMP_RET:.*]] = load i24, ptr %[[RET_ADDR]], align 4
+// OGCG: ret i24 %[[TMP_RET]]
More information about the cfe-commits
mailing list