Skip to content

Commit bd36f73

Browse files
[CIR] Add initial support for bitfields in structs (#142041)
This change adds support for bitfields CIR records can now contain bit fields. I’ve updated the `CIRGenBitFieldInfo` comment, which originally came from the incubator and was identical to the one in OGCodeGen, to better reflect the current implementation. Support for bitfields in unions big-endian architectures and `get` and `set` operations remains unimplemented and will be addressed in a future patch.
1 parent 225768d commit bd36f73

File tree

7 files changed

+534
-6
lines changed

7 files changed

+534
-6
lines changed

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ struct MissingFeatures {
149149
static bool cxxabiUseARMGuardVarABI() { return false; }
150150
static bool cxxabiAppleARM64CXXABI() { return false; }
151151
static bool cxxabiStructorImplicitParam() { return false; }
152+
static bool isDiscreteBitFieldABI() { return false; }
153+
static bool isBigEndian() { return false; }
152154

153155
// Address class
154156
static bool addressOffset() { return false; }
@@ -239,6 +241,7 @@ struct MissingFeatures {
239241
static bool builtinCall() { return false; }
240242
static bool builtinCallF128() { return false; }
241243
static bool builtinCallMathErrno() { return false; }
244+
static bool nonFineGrainedBitfields() { return false; }
242245

243246
// Missing types
244247
static bool dataMemberType() { return false; }

clang/lib/CIR/CodeGen/CIRGenRecordLayout.h

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,106 @@
1414

1515
namespace clang::CIRGen {
1616

17+
/// Record with information about how a bitfield should be accessed. This is
18+
/// very similar to what LLVM codegen does, once CIR evolves it's possible we
19+
/// can use a more higher level representation.
20+
///
21+
/// Often we lay out a sequence of bitfields as a contiguous sequence of bits.
22+
/// When the AST record layout does this, we represent it in CIR as a
23+
/// `!cir.record` type, which directly reflects the structure's layout,
24+
/// including bitfield packing and padding, using CIR types such as
25+
/// `!cir.bool`, `!s8i`, `!u16i`.
26+
///
27+
/// To access a particular bitfield in CIR, we use the operations
28+
/// `cir.get_bitfield` (`GetBitfieldOp`) or `cir.set_bitfield`
29+
/// (`SetBitfieldOp`). These operations rely on the `bitfield_info`
30+
/// attribute, which provides detailed metadata required for access,
31+
/// such as the size and offset of the bitfield, the type and size of
32+
/// the underlying storage, and whether the value is signed.
33+
/// The CIRGenRecordLayout also has a bitFields map which encodes which
34+
/// byte-sequence this bitfield falls within. Let's assume the following C
35+
/// struct:
36+
///
37+
/// struct S {
38+
/// char a, b, c;
39+
/// unsigned bits : 3;
40+
/// unsigned more_bits : 4;
41+
/// unsigned still_more_bits : 7;
42+
/// };
43+
///
44+
/// This will end up as the following cir.record. The bitfield members are
45+
/// represented by one !u16i value, and the array provides padding to align the
46+
/// struct to a 4-byte alignment.
47+
///
48+
/// !rec_S = !cir.record<struct "S" padded {!s8i, !s8i, !s8i, !u16i,
49+
/// !cir.array<!u8i x 3>}>
50+
///
51+
/// When generating code to access more_bits, we'll generate something
52+
/// essentially like this:
53+
///
54+
/// #bfi_more_bits = #cir.bitfield_info<name = "more_bits", storage_type =
55+
/// !u16i, size = 4, offset = 3, is_signed = false>
56+
///
57+
/// cir.func @store_field() {
58+
/// %0 = cir.alloca !rec_S, !cir.ptr<!rec_S>, ["s"] {alignment = 4 : i64}
59+
/// %1 = cir.const #cir.int<2> : !s32i
60+
/// %2 = cir.cast(integral, %1 : !s32i), !u32i
61+
/// %3 = cir.get_member %0[3] {name = "more_bits"} : !cir.ptr<!rec_S> ->
62+
/// !cir.ptr<!u16i>
63+
/// %4 = cir.set_bitfield(#bfi_more_bits, %3 :
64+
/// !cir.ptr<!u16i>, %2 : !u32i) -> !u32i
65+
/// cir.return
66+
/// }
67+
///
68+
struct CIRGenBitFieldInfo {
69+
/// The offset within a contiguous run of bitfields that are represented as
70+
/// a single "field" within the cir.record type. This offset is in bits.
71+
unsigned offset : 16;
72+
73+
/// The total size of the bit-field, in bits.
74+
unsigned size : 15;
75+
76+
/// Whether the bit-field is signed.
77+
unsigned isSigned : 1;
78+
79+
/// The storage size in bits which should be used when accessing this
80+
/// bitfield.
81+
unsigned storageSize;
82+
83+
/// The offset of the bitfield storage from the start of the record.
84+
clang::CharUnits storageOffset;
85+
86+
/// The offset within a contiguous run of bitfields that are represented as a
87+
/// single "field" within the cir.record type, taking into account the AAPCS
88+
/// rules for volatile bitfields. This offset is in bits.
89+
unsigned volatileOffset : 16;
90+
91+
/// The storage size in bits which should be used when accessing this
92+
/// bitfield.
93+
unsigned volatileStorageSize;
94+
95+
/// The offset of the bitfield storage from the start of the record.
96+
clang::CharUnits volatileStorageOffset;
97+
98+
/// The name of a bitfield
99+
llvm::StringRef name;
100+
101+
// The actual storage type for the bitfield
102+
mlir::Type storageType;
103+
104+
CIRGenBitFieldInfo()
105+
: offset(), size(), isSigned(), storageSize(), volatileOffset(),
106+
volatileStorageSize() {}
107+
108+
CIRGenBitFieldInfo(unsigned offset, unsigned size, bool isSigned,
109+
unsigned storageSize, clang::CharUnits storageOffset)
110+
: offset(offset), size(size), isSigned(isSigned),
111+
storageSize(storageSize), storageOffset(storageOffset) {}
112+
113+
void print(llvm::raw_ostream &os) const;
114+
LLVM_DUMP_METHOD void dump() const;
115+
};
116+
17117
/// This class handles record and union layout info while lowering AST types
18118
/// to CIR types.
19119
///
@@ -41,6 +141,10 @@ class CIRGenRecordLayout {
41141
// for both virtual and non-virtual bases.
42142
llvm::DenseMap<const clang::CXXRecordDecl *, unsigned> nonVirtualBases;
43143

144+
/// Map from (bit-field) record field to the corresponding CIR record type
145+
/// field no. This info is populated by record builder.
146+
llvm::DenseMap<const clang::FieldDecl *, CIRGenBitFieldInfo> bitFields;
147+
44148
/// False if any direct or indirect subobject of this class, when considered
45149
/// as a complete object, requires a non-zero bitpattern when
46150
/// zero-initialized.
@@ -83,6 +187,16 @@ class CIRGenRecordLayout {
83187
/// Check whether this struct can be C++ zero-initialized
84188
/// with a zeroinitializer when considered as a base subobject.
85189
bool isZeroInitializableAsBase() const { return zeroInitializableAsBase; }
190+
191+
/// Return the BitFieldInfo that corresponds to the field FD.
192+
const CIRGenBitFieldInfo &getBitFieldInfo(const clang::FieldDecl *fd) const {
193+
fd = fd->getCanonicalDecl();
194+
assert(fd->isBitField() && "Invalid call for non-bit-field decl!");
195+
llvm::DenseMap<const clang::FieldDecl *, CIRGenBitFieldInfo>::const_iterator
196+
it = bitFields.find(fd);
197+
assert(it != bitFields.end() && "Unable to find bitfield info");
198+
return it->second;
199+
}
86200
};
87201

88202
} // namespace clang::CIRGen

0 commit comments

Comments
 (0)