Skip to content

Commit 71130bf

Browse files
committed
[CIR] Implement constant expression bitfield initialization
This adds support for emitting constant bitfield values in global and static variable initializers. The implementation currently supports byte-aligned bitfields (8-bit, 16-bit, etc.). Partial-byte bitfields (e.g., 3-bit, 5-bit fields) require additional split/merge logic for overlapping elements and are left as future work.
1 parent 27c207e commit 71130bf

File tree

2 files changed

+135
-7
lines changed

2 files changed

+135
-7
lines changed

clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp

Lines changed: 106 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,69 @@ bool ConstantAggregateBuilder::add(mlir::TypedAttr typedAttr, CharUnits offset,
176176
return false;
177177
}
178178

179+
bool ConstantAggregateBuilder::addBits(llvm::APInt bits, uint64_t offsetInBits,
180+
bool allowOverwrite) {
181+
const ASTContext &astContext = cgm.getASTContext();
182+
const uint64_t charWidth = cgm.getASTContext().getCharWidth();
183+
mlir::Type charTy = cgm.getBuilder().getUIntNTy(charWidth);
184+
185+
// Offset of where we want the first bit to go within the bits of the
186+
// current char.
187+
unsigned offsetWithinChar = offsetInBits % charWidth;
188+
189+
// We split bit-fields up into individual bytes. Walk over the bytes and
190+
// update them.
191+
for (CharUnits offsetInChars =
192+
astContext.toCharUnitsFromBits(offsetInBits - offsetWithinChar);
193+
/**/; ++offsetInChars) {
194+
// Number of bits we want to fill in this char.
195+
unsigned wantedBits =
196+
std::min((uint64_t)bits.getBitWidth(), charWidth - offsetWithinChar);
197+
198+
// Get a char containing the bits we want in the right places. The other
199+
// bits have unspecified values.
200+
llvm::APInt bitsThisChar = bits;
201+
if (bitsThisChar.getBitWidth() < charWidth)
202+
bitsThisChar = bitsThisChar.zext(charWidth);
203+
if (cgm.getDataLayout().isBigEndian()) {
204+
// Figure out how much to shift by. We may need to left-shift if we have
205+
// less than one byte of Bits left.
206+
int shift = bits.getBitWidth() - charWidth + offsetWithinChar;
207+
if (shift > 0)
208+
bitsThisChar.lshrInPlace(shift);
209+
else if (shift < 0)
210+
bitsThisChar = bitsThisChar.shl(-shift);
211+
} else {
212+
bitsThisChar = bitsThisChar.shl(offsetWithinChar);
213+
}
214+
if (bitsThisChar.getBitWidth() > charWidth)
215+
bitsThisChar = bitsThisChar.trunc(charWidth);
216+
217+
if (wantedBits == charWidth) {
218+
// Got a full byte: just add it directly.
219+
add(cir::IntAttr::get(charTy, bitsThisChar), offsetInChars,
220+
allowOverwrite);
221+
} else {
222+
cgm.errorNYI("partial byte bitfield updates");
223+
return false;
224+
}
225+
226+
// Stop if we've added all the bits.
227+
if (wantedBits == bits.getBitWidth())
228+
break;
229+
230+
// Remove the consumed bits from Bits.
231+
if (!cgm.getDataLayout().isBigEndian())
232+
bits.lshrInPlace(wantedBits);
233+
bits = bits.trunc(bits.getBitWidth() - wantedBits);
234+
235+
// The remaining bits go at the start of the following bytes.
236+
offsetWithinChar = 0;
237+
}
238+
239+
return true;
240+
}
241+
179242
mlir::Attribute
180243
ConstantAggregateBuilder::buildFrom(CIRGenModule &cgm, ArrayRef<Element> elems,
181244
CharUnits startOffset, CharUnits size,
@@ -301,6 +364,9 @@ class ConstRecordBuilder {
301364
bool appendBytes(CharUnits fieldOffsetInChars, mlir::TypedAttr initCst,
302365
bool allowOverwrite = false);
303366

367+
bool appendBitField(const FieldDecl *field, uint64_t fieldOffset,
368+
cir::IntAttr ci, bool allowOverwrite = false);
369+
304370
bool build(InitListExpr *ile, bool allowOverwrite);
305371
bool build(const APValue &val, const RecordDecl *rd, bool isPrimaryBase,
306372
const CXXRecordDecl *vTableClass, CharUnits baseOffset);
@@ -325,6 +391,29 @@ bool ConstRecordBuilder::appendBytes(CharUnits fieldOffsetInChars,
325391
return builder.add(initCst, startOffset + fieldOffsetInChars, allowOverwrite);
326392
}
327393

394+
bool ConstRecordBuilder::appendBitField(const FieldDecl *field,
395+
uint64_t fieldOffset, cir::IntAttr ci,
396+
bool allowOverwrite) {
397+
const auto &rl = cgm.getTypes().getCIRGenRecordLayout(field->getParent());
398+
const auto &info = rl.getBitFieldInfo(field);
399+
llvm::APInt fieldValue = ci.getValue();
400+
401+
// Promote the size of FieldValue if necessary
402+
// FIXME: This should never occur, but currently it can because initializer
403+
// constants are cast to bool, and because clang is not enforcing bitfield
404+
// width limits.
405+
if (info.size > fieldValue.getBitWidth())
406+
fieldValue = fieldValue.zext(info.size);
407+
408+
// Truncate the size of FieldValue to the bit field size.
409+
if (info.size < fieldValue.getBitWidth())
410+
fieldValue = fieldValue.trunc(info.size);
411+
412+
return builder.addBits(fieldValue,
413+
cgm.getASTContext().toBits(startOffset) + fieldOffset,
414+
allowOverwrite);
415+
}
416+
328417
bool ConstRecordBuilder::build(InitListExpr *ile, bool allowOverwrite) {
329418
RecordDecl *rd = ile->getType()
330419
->castAs<clang::RecordType>()
@@ -407,12 +496,14 @@ bool ConstRecordBuilder::build(InitListExpr *ile, bool allowOverwrite) {
407496
} else {
408497
// Otherwise we have a bitfield.
409498
if (auto constInt = dyn_cast<cir::IntAttr>(eltInit)) {
410-
assert(!cir::MissingFeatures::bitfields());
411-
cgm.errorNYI(field->getSourceRange(), "bitfields");
499+
if (!appendBitField(field, layout.getFieldOffset(index), constInt,
500+
allowOverwrite))
501+
return false;
502+
} else {
503+
// We are trying to initialize a bitfield with a non-trivial constant,
504+
// this must require run-time code.
505+
return false;
412506
}
413-
// We are trying to initialize a bitfield with a non-trivial constant,
414-
// this must require run-time code.
415-
return false;
416507
}
417508
}
418509

@@ -510,8 +601,16 @@ bool ConstRecordBuilder::build(const APValue &val, const RecordDecl *rd,
510601
if (field->hasAttr<NoUniqueAddressAttr>())
511602
allowOverwrite = true;
512603
} else {
513-
assert(!cir::MissingFeatures::bitfields());
514-
cgm.errorNYI(field->getSourceRange(), "bitfields");
604+
// Otherwise we have a bitfield.
605+
if (auto constInt = dyn_cast<cir::IntAttr>(eltInit)) {
606+
if (!appendBitField(field, layout.getFieldOffset(index) + offsetBits,
607+
constInt, allowOverwrite))
608+
return false;
609+
} else {
610+
// We are trying to initialize a bitfield with a non-trivial constant,
611+
// this must require run-time code.
612+
return false;
613+
}
515614
}
516615
}
517616

clang/test/CIR/CodeGen/constant-inits.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,21 @@ struct simple {
3030
int a, b;
3131
};
3232

33+
struct Bitfields {
34+
unsigned int a : 8;
35+
unsigned int b : 8;
36+
unsigned int c : 16;
37+
};
38+
39+
struct SignedBitfields {
40+
int x : 8;
41+
int y : 8;
42+
};
43+
44+
struct ByteBitfields {
45+
unsigned char a : 8;
46+
};
47+
3348
void function() {
3449
constexpr static empty e;
3550

@@ -54,6 +69,10 @@ void function() {
5469
constexpr static simple simple_array[] {
5570
s, {1111, 2222}, s
5671
};
72+
73+
constexpr static Bitfields bf1 = {0xFF, 0xAA, 0x1234};
74+
constexpr static SignedBitfields bf2 = {-1, 127};
75+
constexpr static ByteBitfields bf3 = {42};
5776
}
5877

5978
// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE1e = #cir.zero : !rec_empty
@@ -83,6 +102,10 @@ void function() {
83102
// CIR-DAG-SAME: #cir.zero : !rec_packed_and_aligned
84103
// CIR-DAG-SAME: ]> : !cir.array<!rec_packed_and_aligned x 2>
85104

105+
// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE3bf1 = #cir.const_record<{#cir.int<255> : !u8i, #cir.int<170> : !u8i, #cir.int<52> : !u8i, #cir.int<18> : !u8i}> : !rec_anon_struct
106+
// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE3bf2 = #cir.const_record<{#cir.int<255> : !u8i, #cir.int<127> : !u8i, #cir.const_array<[#cir.zero : !u8i, #cir.zero : !u8i]> : !cir.array<!u8i x 2>}> : !rec_anon_struct
107+
// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE3bf3 = #cir.const_record<{#cir.int<42> : !u8i}> : !rec_ByteBitfields
108+
86109
// CIR-LABEL: cir.func dso_local @_Z8functionv()
87110
// CIR: cir.return
88111

@@ -96,6 +119,9 @@ void function() {
96119
// LLVM-DAG: @_ZZ8functionvE3paa = internal global %struct.packed_and_aligned <{ i16 1, i8 2, float 3.000000e+00, i8 0 }>
97120
// LLVM-DAG: @_ZZ8functionvE5array = internal global [2 x %struct.Point] [%struct.Point { i32 123, i32 456, [3 x i8] c"\0B\16!", i32 789 }, %struct.Point { i32 10, i32 20, [3 x i8] zeroinitializer, i32 40 }]
98121
// LLVM-DAG: @_ZZ8functionvE9paa_array = internal global [2 x %struct.packed_and_aligned] [%struct.packed_and_aligned <{ i16 1, i8 2, float 3.000000e+00, i8 0 }>, %struct.packed_and_aligned zeroinitializer]
122+
// LLVM-DAG: @_ZZ8functionvE3bf1 = internal global { i8, i8, i8, i8 } { i8 -1, i8 -86, i8 52, i8 18 }
123+
// LLVM-DAG: @_ZZ8functionvE3bf2 = internal global { i8, i8, [2 x i8] } { i8 -1, i8 127, [2 x i8] zeroinitializer }
124+
// LLVM-DAG: @_ZZ8functionvE3bf3 = internal global %struct.ByteBitfields { i8 42 }
99125

100126
// LLVM-LABEL: define{{.*}} void @_Z8functionv
101127
// LLVM: ret void
@@ -110,6 +136,9 @@ void function() {
110136
// OGCG-DAG: @_ZZ8functionvE3paa = internal constant %struct.packed_and_aligned <{ i16 1, i8 2, float 3.000000e+00, i8 undef }>
111137
// OGCG-DAG: @_ZZ8functionvE5array = internal constant [2 x %struct.Point] [%struct.Point { i32 123, i32 456, [3 x i8] c"\0B\16!", i32 789 }, %struct.Point { i32 10, i32 20, [3 x i8] zeroinitializer, i32 40 }]
112138
// OGCG-DAG: @_ZZ8functionvE9paa_array = internal constant [2 x %struct.packed_and_aligned] [%struct.packed_and_aligned <{ i16 1, i8 2, float 3.000000e+00, i8 undef }>, %struct.packed_and_aligned <{ i16 0, i8 0, float 0.000000e+00, i8 undef }>]
139+
// OGCG-DAG: @_ZZ8functionvE3bf1 = internal constant { i8, i8, i8, i8 } { i8 -1, i8 -86, i8 52, i8 18 }
140+
// OGCG-DAG: @_ZZ8functionvE3bf2 = internal constant { i8, i8, [2 x i8] } { i8 -1, i8 127, [2 x i8] undef }
141+
// OGCG-DAG: @_ZZ8functionvE3bf3 = internal constant %struct.ByteBitfields { i8 42 }
113142

114143
// OGCG-LABEL: define{{.*}} void @_Z8functionv
115144
// OGCG: ret void

0 commit comments

Comments
 (0)