https://github.com/Andres-Salamanca updated 
https://github.com/llvm/llvm-project/pull/147609

>From 38b98a54b718973ec2ce1375e31996ee190e0d75 Mon Sep 17 00:00:00 2001
From: Andres Salamanca <andrealebarbari...@gmail.com>
Date: Tue, 8 Jul 2025 17:33:52 -0500
Subject: [PATCH 1/3] [CIR] Upstream new SetBitfieldOp for handling C and C++
 struct bitfields

---
 clang/include/clang/CIR/Dialect/IR/CIROps.td  | 84 +++++++++++++++++++
 clang/lib/CIR/CodeGen/CIRGenBuilder.h         |  9 ++
 clang/lib/CIR/CodeGen/CIRGenExpr.cpp          | 27 ++++--
 clang/lib/CIR/CodeGen/CIRGenValue.h           |  2 +
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 77 +++++++++++++++++
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.h   | 10 +++
 clang/test/CIR/CodeGen/bitfields.c            | 26 ++++++
 clang/test/CIR/CodeGen/bitfields.cpp          | 25 ++++++
 clang/test/CIR/CodeGen/bitfields_be.c         | 70 ++++++++++++++++
 9 files changed, 324 insertions(+), 6 deletions(-)

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 6529f1386599c..37800ea82e8af 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -1669,6 +1669,90 @@ def GetGlobalOp : CIR_Op<"get_global",
   }];
 }
 
+//===----------------------------------------------------------------------===//
+// SetBitfieldOp
+//===----------------------------------------------------------------------===//
+
+def SetBitfieldOp : CIR_Op<"set_bitfield"> {
+  let summary = "Set the value of a bitfield member";
+  let description = [{
+    The `cir.set_bitfield` operation provides a store-like access to
+    a bit field of a record.
+
+    It expects an address of a storage where to store, a type of the storage,
+    a value being stored, a name of a bit field, a pointer to the storage in 
the
+    base record, a size of the storage, a size the bit field, an offset
+    of the bit field and a sign. Returns a value being stored.
+
+    A unit attribute `volatile` can be used to indicate a volatile load of the
+    bitfield.
+
+    Example.
+    Suppose we have a struct with multiple bitfields stored in
+    different storages. The `cir.set_bitfield` operation sets the value
+    of the bitfield.
+    ```C++
+    typedef struct {
+      int a : 4;
+      int b : 27;
+      int c : 17;
+      int d : 2;
+      int e : 15;
+    } S;
+
+    void store_bitfield(S& s) {
+      s.e = 3;
+    }
+    ```
+
+    ```mlir
+    // 'e' is in the storage with the index 1
+    !record_type = !cir.record<struct "S" packed padded {!u64i, !u16i,
+                               !cir.array<!u8i x 2>} #cir.record.decl.ast>
+    #bfi_e = #cir.bitfield_info<name = "e", storage_type = !u16i, size = 15,
+                                offset = 0, is_signed = true>
+
+    %1 = cir.const #cir.int<3> : !s32i
+    %2 = cir.load %0 : !cir.ptr<!cir.ptr<!record_type>>, !cir.ptr<!record_type>
+    %3 = cir.get_member %2[1] {name = "e"} : !cir.ptr<!record_type>
+                                                             -> !cir.ptr<!u16i>
+    %4 = cir.set_bitfield(#bfi_e, %3 : !cir.ptr<!u16i>, %1 : !s32i) -> !s32i
+    ```
+   }];
+
+  let arguments = (ins
+    Arg<CIR_PointerType, "the address to store the value", [MemWrite]>:$addr,
+    CIR_AnyType:$src,
+    BitfieldInfoAttr:$bitfield_info,
+    UnitAttr:$is_volatile
+  );
+
+  let results = (outs CIR_IntType:$result);
+
+  let assemblyFormat = [{ `(`$bitfield_info`,` 
$addr`:`qualified(type($addr))`,`
+    $src`:`type($src) `)`  attr-dict `->` type($result) }];
+
+  let builders = [
+    OpBuilder<(ins "mlir::Type":$type,
+                   "mlir::Value":$addr,
+                   "mlir::Type":$storage_type,
+                   "mlir::Value":$src,
+                   "llvm::StringRef":$name,
+                   "unsigned":$size,
+                   "unsigned":$offset,
+                   "bool":$is_signed,
+                   "bool":$is_volatile
+                   ),
+   [{
+      BitfieldInfoAttr info =
+        BitfieldInfoAttr::get($_builder.getContext(),
+                              name, storage_type,
+                              size, offset, is_signed);
+      build($_builder, $_state, type, addr, src, info, is_volatile);
+    }]>
+  ];
+}
+
 
//===----------------------------------------------------------------------===//
 // GetBitfieldOp
 
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h 
b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index 0b33f6c7d03b7..d95ea36a5e0d0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -394,6 +394,15 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
     return createGlobal(module, loc, uniqueName, type, linkage);
   }
 
+  mlir::Value createSetBitfield(mlir::Location loc, mlir::Type resultType,
+                                mlir::Value dstAddr, mlir::Type storageType,
+                                mlir::Value src, const CIRGenBitFieldInfo 
&info,
+                                bool isLvalueVolatile, bool useVolatile) {
+    return create<cir::SetBitfieldOp>(loc, resultType, dstAddr, storageType,
+                                      src, info.name, info.size, info.offset,
+                                      info.isSigned, isLvalueVolatile);
+  }
+
   mlir::Value createGetBitfield(mlir::Location loc, mlir::Type resultType,
                                 mlir::Value addr, mlir::Type storageType,
                                 const CIRGenBitFieldInfo &info,
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index 68d7f1f5bca48..5e6f63d9395c6 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -224,6 +224,10 @@ void CIRGenFunction::emitStoreThroughLValue(RValue src, 
LValue dst,
       return;
     }
 
+    assert(dst.isBitField() && "NIY LValue type");
+    emitStoreThroughBitfieldLValue(src, dst);
+    return;
+
     cgm.errorNYI(dst.getPointer().getLoc(),
                  "emitStoreThroughLValue: non-simple lvalue");
     return;
@@ -321,9 +325,20 @@ void CIRGenFunction::emitStoreOfScalar(mlir::Value value, 
Address addr,
 
 mlir::Value CIRGenFunction::emitStoreThroughBitfieldLValue(RValue src,
                                                            LValue dst) {
-  assert(!cir::MissingFeatures::bitfields());
-  cgm.errorNYI("bitfields");
-  return {};
+
+  assert(!cir::MissingFeatures::armComputeVolatileBitfields());
+
+  const CIRGenBitFieldInfo &info = dst.getBitFieldInfo();
+  mlir::Type resLTy = convertTypeForMem(dst.getType());
+  Address ptr = dst.getBitFieldAddress();
+
+  const bool useVolatile = false;
+
+  mlir::Value dstAddr = dst.getAddress().getPointer();
+
+  return builder.createSetBitfield(dstAddr.getLoc(), resLTy, dstAddr,
+                                   ptr.getElementType(), src.getValue(), info,
+                                   dst.isVolatileQualified(), useVolatile);
 }
 
 RValue CIRGenFunction::emitLoadOfBitfieldLValue(LValue lv, SourceLocation loc) 
{
@@ -1063,10 +1078,10 @@ LValue CIRGenFunction::emitBinaryOperatorLValue(const 
BinaryOperator *e) {
 
     SourceLocRAIIObject loc{*this, getLoc(e->getSourceRange())};
     if (lv.isBitField()) {
-      cgm.errorNYI(e->getSourceRange(), "bitfields");
-      return {};
+      emitStoreThroughBitfieldLValue(rv, lv);
+    } else {
+      emitStoreThroughLValue(rv, lv);
     }
-    emitStoreThroughLValue(rv, lv);
 
     if (getLangOpts().OpenMP) {
       cgm.errorNYI(e->getSourceRange(), "openmp");
diff --git a/clang/lib/CIR/CodeGen/CIRGenValue.h 
b/clang/lib/CIR/CodeGen/CIRGenValue.h
index e1b0f805a7b21..0a6dba5e80a62 100644
--- a/clang/lib/CIR/CodeGen/CIRGenValue.h
+++ b/clang/lib/CIR/CodeGen/CIRGenValue.h
@@ -186,6 +186,8 @@ class LValue {
   bool isBitField() const { return lvType == BitField; }
   bool isVolatile() const { return quals.hasVolatile(); }
 
+  bool isVolatileQualified() const { return quals.hasVolatile(); }
+
   unsigned getVRQualifiers() const {
     return quals.getCVRQualifiers() & ~clang::Qualifiers::Const;
   }
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 5ac42b6a63b09..5364a515fb384 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -2049,6 +2049,7 @@ void ConvertCIRToLLVMPass::runOnOperation() {
                CIRToLLVMGetGlobalOpLowering,
                CIRToLLVMGetMemberOpLowering,
                CIRToLLVMSelectOpLowering,
+               CIRToLLVMSetBitfieldOpLowering,
                CIRToLLVMShiftOpLowering,
                CIRToLLVMStackRestoreOpLowering,
                CIRToLLVMStackSaveOpLowering,
@@ -2384,6 +2385,82 @@ mlir::LogicalResult 
CIRToLLVMComplexImagOpLowering::matchAndRewrite(
   return mlir::success();
 }
 
+mlir::LogicalResult CIRToLLVMSetBitfieldOpLowering::matchAndRewrite(
+    cir::SetBitfieldOp op, OpAdaptor adaptor,
+    mlir::ConversionPatternRewriter &rewriter) const {
+  mlir::OpBuilder::InsertionGuard guard(rewriter);
+  rewriter.setInsertionPoint(op);
+
+  cir::BitfieldInfoAttr info = op.getBitfieldInfo();
+  uint64_t size = info.getSize();
+  uint64_t offset = info.getOffset();
+  mlir::Type storageType = info.getStorageType();
+  mlir::MLIRContext *context = storageType.getContext();
+
+  unsigned storageSize = 0;
+
+  mlir::IntegerType intType =
+      TypeSwitch<mlir::Type, mlir::IntegerType>(storageType)
+          .Case<cir::ArrayType>([&](cir::ArrayType atTy) {
+            storageSize = atTy.getSize() * 8;
+            return mlir::IntegerType::get(context, storageSize);
+          })
+          .Case<cir::IntType>([&](cir::IntType intTy) {
+            storageSize = intTy.getWidth();
+            return mlir::IntegerType::get(context, storageSize);
+          })
+          .Default([](mlir::Type) -> mlir::IntegerType {
+            llvm_unreachable(
+                "Either ArrayType or IntType expected for bitfields storage");
+          });
+
+  mlir::Value srcVal = createIntCast(rewriter, adaptor.getSrc(), intType);
+  unsigned srcWidth = storageSize;
+  mlir::Value resultVal = srcVal;
+
+  if (storageSize != size) {
+    assert(storageSize > size && "Invalid bitfield size.");
+
+    mlir::Value val = rewriter.create<mlir::LLVM::LoadOp>(
+        op.getLoc(), intType, adaptor.getAddr(), /* alignment */ 0,
+        op.getIsVolatile());
+
+    srcVal =
+        createAnd(rewriter, srcVal, llvm::APInt::getLowBitsSet(srcWidth, 
size));
+    resultVal = srcVal;
+    srcVal = createShL(rewriter, srcVal, offset);
+
+    // Mask out the original value.
+    val = createAnd(rewriter, val,
+                    ~llvm::APInt::getBitsSet(srcWidth, offset, offset + size));
+
+    // Or together the unchanged values and the source value.
+    srcVal = rewriter.create<mlir::LLVM::OrOp>(op.getLoc(), val, srcVal);
+  }
+
+  rewriter.create<mlir::LLVM::StoreOp>(op.getLoc(), srcVal, adaptor.getAddr(),
+                                       /* alignment */ 0, op.getIsVolatile());
+
+  mlir::Type resultTy = getTypeConverter()->convertType(op.getType());
+
+  if (info.getIsSigned()) {
+    assert(size <= storageSize);
+    unsigned highBits = storageSize - size;
+
+    if (highBits) {
+      resultVal = createShL(rewriter, resultVal, highBits);
+      resultVal = createAShR(rewriter, resultVal, highBits);
+    }
+  }
+
+  resultVal = createIntCast(rewriter, resultVal,
+                            mlir::cast<mlir::IntegerType>(resultTy),
+                            info.getIsSigned());
+
+  rewriter.replaceOp(op, resultVal);
+  return mlir::success();
+}
+
 mlir::LogicalResult CIRToLLVMGetBitfieldOpLowering::matchAndRewrite(
     cir::GetBitfieldOp op, OpAdaptor adaptor,
     mlir::ConversionPatternRewriter &rewriter) const {
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
index d9fb91066317b..6c26d10269ea2 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
@@ -513,6 +513,16 @@ class CIRToLLVMComplexImagOpLowering
                   mlir::ConversionPatternRewriter &) const override;
 };
 
+class CIRToLLVMSetBitfieldOpLowering
+    : public mlir::OpConversionPattern<cir::SetBitfieldOp> {
+public:
+  using mlir::OpConversionPattern<cir::SetBitfieldOp>::OpConversionPattern;
+
+  mlir::LogicalResult
+  matchAndRewrite(cir::SetBitfieldOp op, OpAdaptor,
+                  mlir::ConversionPatternRewriter &) const override;
+};
+
 class CIRToLLVMGetBitfieldOpLowering
     : public mlir::OpConversionPattern<cir::GetBitfieldOp> {
 public:
diff --git a/clang/test/CIR/CodeGen/bitfields.c 
b/clang/test/CIR/CodeGen/bitfields.c
index 6eb753c5cc3d2..ccb9cf0616a1e 100644
--- a/clang/test/CIR/CodeGen/bitfields.c
+++ b/clang/test/CIR/CodeGen/bitfields.c
@@ -134,3 +134,29 @@ unsigned int load_field_unsigned(A* s) {
 //OGCG:   [[TMP4:%.*]] = lshr i16 [[TMP3]], 3
 //OGCG:   [[TMP5:%.*]] = and i16 [[TMP4]], 15
 //OGCG:   [[TMP6:%.*]] = zext i16 [[TMP5]] to i32
+
+void store_field() {
+  S s;
+  s.e = 3;
+}
+// CIR: cir.func {{.*@store_field}}
+// CIR:   [[TMP0:%.*]] = cir.alloca !rec_S, !cir.ptr<!rec_S>
+// CIR:   [[TMP1:%.*]] = cir.const #cir.int<3> : !s32i
+// CIR:   [[TMP2:%.*]] = cir.get_member [[TMP0]][1] {name = "e"} : 
!cir.ptr<!rec_S> -> !cir.ptr<!u16i>
+// CIR:   cir.set_bitfield(#bfi_e, [[TMP2]] : !cir.ptr<!u16i>, [[TMP1]] : 
!s32i)
+
+// LLVM: define dso_local void @store_field()
+// LLVM:   [[TMP0:%.*]] = alloca %struct.S, i64 1, align 4
+// LLVM:   [[TMP1:%.*]] = getelementptr %struct.S, ptr [[TMP0]], i32 0, i32 1
+// LLVM:   [[TMP2:%.*]] = load i16, ptr [[TMP1]], align 2
+// LLVM:   [[TMP3:%.*]] = and i16 [[TMP2]], -32768
+// LLVM:   [[TMP4:%.*]] = or i16 [[TMP3]], 3
+// LLVM:   store i16 [[TMP4]], ptr [[TMP1]], align 2
+
+// OGCG: define dso_local void @store_field()
+// OGCG:   [[TMP0:%.*]] = alloca %struct.S, align 4
+// OGCG:   [[TMP1:%.*]] = getelementptr inbounds nuw %struct.S, ptr [[TMP0]], 
i32 0, i32 1
+// OGCG:   [[TMP2:%.*]] = load i16, ptr [[TMP1]], align 4
+// OGCG:   [[TMP3:%.*]] = and i16 [[TMP2]], -32768
+// OGCG:   [[TMP4:%.*]] = or i16 [[TMP3]], 3
+// OGCG:   store i16 [[TMP4]], ptr [[TMP1]], align 4
diff --git a/clang/test/CIR/CodeGen/bitfields.cpp 
b/clang/test/CIR/CodeGen/bitfields.cpp
index a4d58b5cadcec..f8f41942b2a40 100644
--- a/clang/test/CIR/CodeGen/bitfields.cpp
+++ b/clang/test/CIR/CodeGen/bitfields.cpp
@@ -58,3 +58,28 @@ int load_field(S* s) {
 // OGCG:  [[TMP3:%.*]] = shl i64 [[TMP2]], 15
 // OGCG:  [[TMP4:%.*]] = ashr i64 [[TMP3]], 47
 // OGCG:  [[TMP5:%.*]] = trunc i64 [[TMP4]] to i32
+
+void store_field() {
+  S s;
+  s.a = 3;
+}
+// CIR: cir.func dso_local @_Z11store_field
+// CIR:   [[TMP0:%.*]] = cir.alloca !rec_S, !cir.ptr<!rec_S>
+// CIR:   [[TMP1:%.*]] = cir.const #cir.int<3> : !s32i
+// CIR:   [[TMP2:%.*]] = cir.get_member [[TMP0]][0] {name = "a"} : 
!cir.ptr<!rec_S> -> !cir.ptr<!u64i>
+// CIR:   cir.set_bitfield(#bfi_a, [[TMP2]] : !cir.ptr<!u64i>, [[TMP1]] : 
!s32i)
+
+// LLVM: define dso_local void @_Z11store_fieldv
+// LLVM:   [[TMP0:%.*]] = alloca %struct.S, i64 1, align 4
+// LLVM:   [[TMP1:%.*]] = getelementptr %struct.S, ptr [[TMP0]], i32 0, i32 0
+// LLVM:   [[TMP2:%.*]] = load i64, ptr [[TMP1]], align 8
+// LLVM:   [[TMP3:%.*]] = and i64 [[TMP2]], -16
+// LLVM:   [[TMP4:%.*]] = or i64 [[TMP3]], 3
+// LLVM:   store i64 [[TMP4]], ptr [[TMP1]], align 8
+
+// OGCG: define dso_local void @_Z11store_fieldv()
+// OGCG:   [[TMP0:%.*]] = alloca %struct.S, align 4
+// OGCG:   [[TMP1:%.*]] = load i64, ptr [[TMP0]], align 4
+// OGCG:   [[TMP2:%.*]] = and i64 [[TMP1]], -16
+// OGCG:   [[TMP3:%.*]] = or i64 [[TMP2]], 3
+// OGCG:   store i64 [[TMP3]], ptr [[TMP0]], align 4
diff --git a/clang/test/CIR/CodeGen/bitfields_be.c 
b/clang/test/CIR/CodeGen/bitfields_be.c
index e839bc2b9698d..6133927b67d21 100644
--- a/clang/test/CIR/CodeGen/bitfields_be.c
+++ b/clang/test/CIR/CodeGen/bitfields_be.c
@@ -42,3 +42,73 @@ int init(S* s) {
 //OGCG:   [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4
 //OGCG:   [[TMP3:%.*]] = shl i32 [[TMP2]], 15
 //OGCG:   [[TMP4:%.*]] = ashr i32 [[TMP3]], 15
+
+
+void load(S* s) {
+    s->a = -4;
+    s->b = 42;
+    s->c = -12345;
+}
+
+// field 'a'
+// CIR: cir.func dso_local @load
+// CIR:    %[[PTR0:.*]] = cir.alloca !cir.ptr<!rec_S>, 
!cir.ptr<!cir.ptr<!rec_S>>, ["s", init] {alignment = 8 : i64} loc(#loc35)
+// CIR:    %[[CONST1:.*]] = cir.const #cir.int<4> : !s32i
+// CIR:    %[[MIN1:.*]] = cir.unary(minus, %[[CONST1]]) nsw : !s32i, !s32i
+// CIR:    %[[VAL0:.*]] = cir.load align(8) %[[PTR0]] : 
!cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S>
+// CIR:    %[[GET0:.*]] = cir.get_member %[[VAL0]][0] {name = "a"} : 
!cir.ptr<!rec_S> -> !cir.ptr<!u32i>
+// CIR:    %[[SET0:.*]] = cir.set_bitfield(#bfi_a, %[[GET0]] : 
!cir.ptr<!u32i>, %[[MIN1]] : !s32i) -> !s32i
+
+// LLVM: define dso_local void @load
+// LLVM:   %[[PTR0:.*]] = load ptr
+// LLVM:   %[[GET0:.*]] = getelementptr %struct.S, ptr %[[PTR0]], i32 0, i32 0
+// LLVM:   %[[VAL0:.*]] = load i32, ptr %[[GET0]], align 4
+// LLVM:   %[[AND0:.*]] = and i32 %[[VAL0]], 268435455
+// LLVM:   %[[OR0:.*]] = or i32 %[[AND0]], -1073741824
+// LLVM:   store i32 %[[OR0]], ptr %[[GET0]]
+
+// OGCG: define dso_local void @load
+// OGCG:   %[[PTR0:.*]] = load ptr
+// OGCG:   %[[VAL0:.*]] = load i32, ptr %[[PTR0]]
+// OGCG:   %[[AND0:.*]] = and i32 %[[VAL0]], 268435455
+// OGCG:   %[[OR0:.*]] = or i32 %[[AND0]], -1073741824
+// OGCG:   store i32 %[[OR0]], ptr %[[PTR0]]
+
+// field 'b'
+// CIR:    %[[CONST2:.*]] = cir.const #cir.int<42> : !s32i
+// CIR:    %[[VAL1:.*]] = cir.load align(8) %[[PTR0]] : 
!cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S>
+// CIR:    %[[GET1:.*]] = cir.get_member %[[VAL1]][0] {name = "b"} : 
!cir.ptr<!rec_S> -> !cir.ptr<!u32i>
+// CIR:    %[[SET1:.*]] = cir.set_bitfield(#bfi_b, %[[GET1]] : 
!cir.ptr<!u32i>, %[[CONST2]] : !s32i) -> !s32i
+
+// LLVM:  %[[PTR1:.*]] = load ptr
+// LLVM:  %[[GET1:.*]] = getelementptr %struct.S, ptr %[[PTR1]], i32 0, i32 0
+// LLVM:  %[[VAL1:.*]] = load i32, ptr %[[GET1]], align 4
+// LLVM:  %[[AND1:.*]] = and i32 %[[VAL1]], -268304385
+// LLVM:  %[[OR1:.*]] = or i32 %[[AND1]], 5505024
+// LLVM:  store i32 %[[OR1]], ptr %[[GET1]]
+
+// OGCG:   %[[PTR1:.*]] = load ptr
+// OGCG:   %[[VAL1:.*]] = load i32, ptr %[[PTR1]]
+// OGCG:   %[[AND1:.*]] = and i32 %[[VAL1]], -268304385
+// OGCG:   %[[OR1:.*]] = or i32 %[[AND1]], 5505024
+// OGCG:   store i32 %[[OR1]], ptr %[[PTR1]]
+
+// field 'c'
+// CIR:    %[[CONST3:.*]] = cir.const #cir.int<12345> : !s32i
+// CIR:    %[[MIN2:.*]] = cir.unary(minus, %[[CONST3]]) nsw : !s32i, !s32i
+// CIR:    %[[VAL2:.*]] = cir.load align(8) %[[PTR0]] : 
!cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S>
+// CIR:    %[[GET2:.*]] = cir.get_member %[[VAL2]][0] {name = "c"} : 
!cir.ptr<!rec_S> -> !cir.ptr<!u32i>
+// CIR:    %[[SET2:.*]] = cir.set_bitfield(#bfi_c, %[[GET2]] : 
!cir.ptr<!u32i>, %[[MIN2]] : !s32i) -> !s32i
+
+// LLVM:  %[[PTR2:.*]] = load ptr
+// LLVM:  %[[GET2:.*]] = getelementptr %struct.S, ptr  %[[PTR2]], i32 0, i32 0
+// LLVM:  %[[VAL2:.*]] = load i32, ptr %[[GET2]], align 4
+// LLVM:  %[[AND2:.*]] = and i32 %[[VAL2]], -131072
+// LLVM:  %[[OR2:.*]] = or i32 %[[AND2]], 118727
+// LLVM:  store i32 %[[OR2]], ptr %[[GET2]]
+
+// OGCG:   %[[PTR2:.*]] = load ptr
+// OGCG:   %[[VAL2:.*]] = load i32, ptr %[[PTR2]]
+// OGCG:   %[[AND2:.*]] = and i32 %[[VAL2]], -131072
+// OGCG:   %[[OR2:.*]] = or i32 %[[AND2]], 118727
+// OGCG:   store i32 %[[OR2]], ptr %[[PTR2]]

>From 47fbf708394bbde0a653d6b27b788526b97f374e Mon Sep 17 00:00:00 2001
From: Andres Salamanca <andrealebarbari...@gmail.com>
Date: Wed, 9 Jul 2025 15:42:05 -0500
Subject: [PATCH 2/3] Add requested example, test, and address review comments

---
 clang/include/clang/CIR/Dialect/IR/CIROps.td  |  6 +-
 clang/lib/CIR/CodeGen/CIRGenExpr.cpp          |  8 +-
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 46 +++++------
 clang/test/CIR/CodeGen/bitfields.c            | 79 +++++++++++++++++++
 4 files changed, 108 insertions(+), 31 deletions(-)

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 37800ea82e8af..191ffcde8653a 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -1684,8 +1684,12 @@ def SetBitfieldOp : CIR_Op<"set_bitfield"> {
     base record, a size of the storage, a size the bit field, an offset
     of the bit field and a sign. Returns a value being stored.
 
-    A unit attribute `volatile` can be used to indicate a volatile load of the
+    A unit attribute `volatile` can be used to indicate a volatile store of the
     bitfield.
+      ```mlir
+        cir.set_bitfield(#bfi, %0 : !cir.ptr<!u32i>, %1 : !s32i) {is_volatile}
+                                                                       -> !s32i
+      ```
 
     Example.
     Suppose we have a struct with multiple bitfields stored in
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index 5e6f63d9395c6..46e3cc7601b0b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -224,7 +224,7 @@ void CIRGenFunction::emitStoreThroughLValue(RValue src, 
LValue dst,
       return;
     }
 
-    assert(dst.isBitField() && "NIY LValue type");
+    assert(dst.isBitField() && "Unknown LValue type");
     emitStoreThroughBitfieldLValue(src, dst);
     return;
 
@@ -332,6 +332,7 @@ mlir::Value 
CIRGenFunction::emitStoreThroughBitfieldLValue(RValue src,
   mlir::Type resLTy = convertTypeForMem(dst.getType());
   Address ptr = dst.getBitFieldAddress();
 
+  assert(!cir::MissingFeatures::armComputeVolatileBitfields());
   const bool useVolatile = false;
 
   mlir::Value dstAddr = dst.getAddress().getPointer();
@@ -1077,11 +1078,10 @@ LValue CIRGenFunction::emitBinaryOperatorLValue(const 
BinaryOperator *e) {
     LValue lv = emitLValue(e->getLHS());
 
     SourceLocRAIIObject loc{*this, getLoc(e->getSourceRange())};
-    if (lv.isBitField()) {
+    if (lv.isBitField())
       emitStoreThroughBitfieldLValue(rv, lv);
-    } else {
+    else
       emitStoreThroughLValue(rv, lv);
-    }
 
     if (getLangOpts().OpenMP) {
       cgm.errorNYI(e->getSourceRange(), "openmp");
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 5364a515fb384..b3ab559cf87c7 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -2385,6 +2385,24 @@ mlir::LogicalResult 
CIRToLLVMComplexImagOpLowering::matchAndRewrite(
   return mlir::success();
 }
 
+mlir::IntegerType computeBitfieldIntType(mlir::Type storageType,
+                                         mlir::MLIRContext *context,
+                                         unsigned &storageSize) {
+  return TypeSwitch<mlir::Type, mlir::IntegerType>(storageType)
+      .Case<cir::ArrayType>([&](cir::ArrayType atTy) {
+        storageSize = atTy.getSize() * 8;
+        return mlir::IntegerType::get(context, storageSize);
+      })
+      .Case<cir::IntType>([&](cir::IntType intTy) {
+        storageSize = intTy.getWidth();
+        return mlir::IntegerType::get(context, storageSize);
+      })
+      .Default([](mlir::Type) -> mlir::IntegerType {
+        llvm_unreachable(
+            "Either ArrayType or IntType expected for bitfields storage");
+      });
+}
+
 mlir::LogicalResult CIRToLLVMSetBitfieldOpLowering::matchAndRewrite(
     cir::SetBitfieldOp op, OpAdaptor adaptor,
     mlir::ConversionPatternRewriter &rewriter) const {
@@ -2400,19 +2418,7 @@ mlir::LogicalResult 
CIRToLLVMSetBitfieldOpLowering::matchAndRewrite(
   unsigned storageSize = 0;
 
   mlir::IntegerType intType =
-      TypeSwitch<mlir::Type, mlir::IntegerType>(storageType)
-          .Case<cir::ArrayType>([&](cir::ArrayType atTy) {
-            storageSize = atTy.getSize() * 8;
-            return mlir::IntegerType::get(context, storageSize);
-          })
-          .Case<cir::IntType>([&](cir::IntType intTy) {
-            storageSize = intTy.getWidth();
-            return mlir::IntegerType::get(context, storageSize);
-          })
-          .Default([](mlir::Type) -> mlir::IntegerType {
-            llvm_unreachable(
-                "Either ArrayType or IntType expected for bitfields storage");
-          });
+      computeBitfieldIntType(storageType, context, storageSize);
 
   mlir::Value srcVal = createIntCast(rewriter, adaptor.getSrc(), intType);
   unsigned srcWidth = storageSize;
@@ -2476,19 +2482,7 @@ mlir::LogicalResult 
CIRToLLVMGetBitfieldOpLowering::matchAndRewrite(
   unsigned storageSize = 0;
 
   mlir::IntegerType intType =
-      TypeSwitch<mlir::Type, mlir::IntegerType>(storageType)
-          .Case<cir::ArrayType>([&](cir::ArrayType atTy) {
-            storageSize = atTy.getSize() * 8;
-            return mlir::IntegerType::get(context, storageSize);
-          })
-          .Case<cir::IntType>([&](cir::IntType intTy) {
-            storageSize = intTy.getWidth();
-            return mlir::IntegerType::get(context, storageSize);
-          })
-          .Default([](mlir::Type) -> mlir::IntegerType {
-            llvm_unreachable(
-                "Either ArrayType or IntType expected for bitfields storage");
-          });
+      computeBitfieldIntType(storageType, context, storageSize);
 
   mlir::Value val = rewriter.create<mlir::LLVM::LoadOp>(
       op.getLoc(), intType, adaptor.getAddr(), 0, op.getIsVolatile());
diff --git a/clang/test/CIR/CodeGen/bitfields.c 
b/clang/test/CIR/CodeGen/bitfields.c
index ccb9cf0616a1e..f3c730e561737 100644
--- a/clang/test/CIR/CodeGen/bitfields.c
+++ b/clang/test/CIR/CodeGen/bitfields.c
@@ -160,3 +160,82 @@ void store_field() {
 // OGCG:   [[TMP3:%.*]] = and i16 [[TMP2]], -32768
 // OGCG:   [[TMP4:%.*]] = or i16 [[TMP3]], 3
 // OGCG:   store i16 [[TMP4]], ptr [[TMP1]], align 4
+
+void store_bitfield_to_bitfield() {
+  S s;
+  s.a = s.c;
+}
+
+// CIR: cir.func {{.*@store_bitfield_to_bitfield}}
+// CIR:   [[TMP0:%.*]] = cir.alloca !rec_S, !cir.ptr<!rec_S>, ["s"] {alignment 
= 4 : i64}
+// CIR:   [[TMP1:%.*]] = cir.get_member [[TMP0]][0] {name = "c"} : 
!cir.ptr<!rec_S> -> !cir.ptr<!u64i>
+// CIR:   [[TMP2:%.*]] = cir.get_bitfield(#bfi_c, [[TMP1]] : !cir.ptr<!u64i>) 
-> !s32i
+// CIR:   [[TMP3:%.*]] = cir.get_member [[TMP0]][0] {name = "a"} : 
!cir.ptr<!rec_S> -> !cir.ptr<!u64i>
+// CIR:   [[TMP4:%.*]] = cir.set_bitfield(#bfi_a, [[TMP3]] : !cir.ptr<!u64i>, 
[[TMP2]] : !s32i) -> !s32i
+
+// LLVM: define dso_local void @store_bitfield_to_bitfield()
+// LLVM:  [[TMP0:%.*]] = alloca %struct.S, i64 1, align 4
+// LLVM:  [[TMP1:%.*]] = getelementptr %struct.S, ptr [[TMP0]], i32 0, i32 0
+// LLVM:  [[TMP2:%.*]] = load i64, ptr [[TMP1]], align 8
+// LLVM:  [[TMP3:%.*]] = shl i64 [[TMP2]], 15
+// LLVM:  [[TMP4:%.*]] = ashr i64 [[TMP3]], 47
+// LLVM:  [[TMP5:%.*]] = trunc i64 [[TMP4]] to i32
+// LLVM:  [[TMP6:%.*]] = getelementptr %struct.S, ptr [[TMP0]], i32 0, i32 0
+// LLVM:  [[TMP7:%.*]] = zext i32 [[TMP5]] to i64
+// LLVM:  [[TMP8:%.*]] = load i64, ptr [[TMP6]], align 8
+// LLVM:  [[TMP9:%.*]] = and i64 [[TMP7]], 15
+// LLVM:  [[TMP10:%.*]] = and i64 [[TMP8]], -16
+// LLVM:  [[TMP11:%.*]] = or i64 [[TMP10]], [[TMP9]]
+// LLVM:  store i64 [[TMP11]], ptr [[TMP6]], align 8
+// LLVM:  [[TMP12:%.*]] = shl i64 [[TMP9]], 60
+// LLVM:  [[TMP13:%.*]] = ashr i64 [[TMP12]], 60
+// LLVM:  [[TMP15:%.*]] = trunc i64 [[TMP13]] to i32
+
+// OGCG: define dso_local void @store_bitfield_to_bitfield()
+// OGCG:  [[TMP0:%.*]] = alloca %struct.S, align 4
+// OGCG:  [[TMP1:%.*]] = load i64, ptr [[TMP0]], align 4
+// OGCG:  [[TMP2:%.*]] = shl i64 [[TMP1]], 15
+// OGCG:  [[TMP3:%.*]] = ashr i64 [[TMP2]], 47
+// OGCG:  [[TMP4:%.*]] = trunc i64 [[TMP3]] to i32
+// OGCG:  [[TMP5:%.*]] = zext i32 [[TMP4]] to i64
+// OGCG:  [[TMP6:%.*]] = load i64, ptr [[TMP0]], align 4
+// OGCG:  [[TMP7:%.*]] = and i64 [[TMP5]], 15
+// OGCG:  [[TMP8:%.*]] = and i64 [[TMP6]], -16
+// OGCG:  [[TMP9:%.*]] = or i64 [[TMP8]], [[TMP7]]
+// OGCG:  store i64 [[TMP9]], ptr [[TMP0]], align 4
+// OGCG:  [[TMP10:%.*]] = shl i64 %bf.value, 60
+// OGCG:  [[TMP11:%.*]] = ashr i64 [[TMP10]], 60
+// OGCG:  [[TMP12:%.*]] = trunc i64 [[TMP11]] to i32
+
+typedef struct {
+  int a : 30;
+  int volatile b : 8;
+  int c;
+} V;
+
+void set_volatile(V* v) {
+  v->b = 3;
+}
+//CIR: cir.func dso_local @set_volatile
+//CIR:   [[TMP0:%.*]] = cir.alloca !cir.ptr<!rec_V>, 
!cir.ptr<!cir.ptr<!rec_V>>, ["v", init] {alignment = 8 : i64}
+//CIR:   [[TMP1:%.*]] = cir.const #cir.int<3> : !s32i
+//CIR:   [[TMP2:%.*]] = cir.load align(8) [[TMP0]] : 
!cir.ptr<!cir.ptr<!rec_V>>, !cir.ptr<!rec_V>
+//CIR:   [[TMP3:%.*]] = cir.get_member [[TMP2]][0] {name = "b"} : 
!cir.ptr<!rec_V> -> !cir.ptr<!u64i>
+//CIR:   [[TMP4:%.*]] = cir.set_bitfield(#bfi_b, [[TMP3]] : !cir.ptr<!u64i>, 
[[TMP1]] : !s32i) {is_volatile} -> !s32i
+
+// LLVM: define dso_local void @set_volatile
+// LLVM:   [[TMP0:%.*]] = alloca ptr, i64 1, align 8
+// LLVM:   [[TMP1:%.*]] = load ptr, ptr [[TMP0]], align 8
+// LLVM:   [[TMP2:%.*]] = getelementptr %struct.V, ptr [[TMP1]], i32 0, i32 0
+// LLVM:   [[TMP3:%.*]] = load volatile i64, ptr [[TMP2]], align 8
+// LLVM:   [[TMP4:%.*]] = and i64 [[TMP3]], -1095216660481
+// LLVM:   [[TMP5:%.*]] = or i64 [[TMP4]], 12884901888
+// LLVM:   store volatile i64 [[TMP5]], ptr [[TMP2]], align 8
+
+// OGCG: define dso_local void @set_volatile
+// OGCG:   [[TMP0:%.*]] = alloca ptr, align 8
+// OGCG:   [[TMP1:%.*]] = load ptr, ptr [[TMP0]], align 8
+// OGCG:   [[TMP2:%.*]] = load volatile i64, ptr [[TMP1]], align 4
+// OGCG:   [[TMP3:%.*]] = and i64 [[TMP2]], -1095216660481
+// OGCG:   [[TMP4:%.*]] = or i64 [[TMP3]], 12884901888
+// OGCG:   store volatile i64 [[TMP4]], ptr [[TMP1]], align 4

>From b960b638f2e42769504808284be7cbea01c67498 Mon Sep 17 00:00:00 2001
From: Andres Salamanca <andrealebarbari...@gmail.com>
Date: Thu, 10 Jul 2025 11:03:10 -0500
Subject: [PATCH 3/3] Update operation description

---
 clang/include/clang/CIR/Dialect/IR/CIROps.td | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 191ffcde8653a..33f4c5ae77b8c 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -1679,10 +1679,10 @@ def SetBitfieldOp : CIR_Op<"set_bitfield"> {
     The `cir.set_bitfield` operation provides a store-like access to
     a bit field of a record.
 
-    It expects an address of a storage where to store, a type of the storage,
-    a value being stored, a name of a bit field, a pointer to the storage in 
the
-    base record, a size of the storage, a size the bit field, an offset
-    of the bit field and a sign. Returns a value being stored.
+    A bitfield info attribute must be provided to describe the location of
+    the bitfield within the memory referenced by the $addr argument.
+    The $src argument is inserted at the appropriate place in the memory and
+    the value that was stored. Returns a value being stored.
 
     A unit attribute `volatile` can be used to indicate a volatile store of the
     bitfield.

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to