[clang] [CIR] Upstream new SetBitfieldOp for handling C and C++ struct bitfields (PR #147609)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Jul 10 09:03:32 PDT 2025
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 <andrealebarbaritos at 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 <andrealebarbaritos at 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 <andrealebarbaritos at 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.
More information about the cfe-commits
mailing list