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 <[email protected]>
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]]

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to