Skip to content

Commit bcdea60

Browse files
mmhaandykaylor
andauthored
[CIR] Implement constant expression bitfield initialization (#162143)
This adds support for emitting bitfields in constant aggregatge initializers with the exception of single element records and decomposing large bitfields into smaller constants. --------- Co-authored-by: Andy Kaylor <[email protected]>
1 parent c7274fc commit bcdea60

File tree

2 files changed

+328
-7
lines changed

2 files changed

+328
-7
lines changed

clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp

Lines changed: 240 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ class ConstantAggregateBuilder : private ConstantAggregateBuilderUtils {
118118
/// non-packed LLVM struct will give the correct layout.
119119
bool naturalLayout = true;
120120

121+
bool split(size_t index, CharUnits hint);
122+
std::optional<size_t> splitAt(CharUnits pos);
123+
121124
static mlir::Attribute buildFrom(CIRGenModule &cgm, ArrayRef<Element> elems,
122125
CharUnits startOffset, CharUnits size,
123126
bool naturalLayout, mlir::Type desiredTy,
@@ -137,6 +140,10 @@ class ConstantAggregateBuilder : private ConstantAggregateBuilderUtils {
137140
/// Update or overwrite the bits starting at \p offsetInBits with \p bits.
138141
bool addBits(llvm::APInt bits, uint64_t offsetInBits, bool allowOverwrite);
139142

143+
/// Attempt to condense the value starting at \p offset to a constant of type
144+
/// \p desiredTy.
145+
void condense(CharUnits offset, mlir::Type desiredTy);
146+
140147
/// Produce a constant representing the entire accumulated value, ideally of
141148
/// the specified type. If \p allowOversized, the constant might be larger
142149
/// than implied by \p desiredTy (eg, if there is a flexible array member).
@@ -176,6 +183,195 @@ bool ConstantAggregateBuilder::add(mlir::TypedAttr typedAttr, CharUnits offset,
176183
return false;
177184
}
178185

186+
bool ConstantAggregateBuilder::addBits(llvm::APInt bits, uint64_t offsetInBits,
187+
bool allowOverwrite) {
188+
const ASTContext &astContext = cgm.getASTContext();
189+
const uint64_t charWidth = astContext.getCharWidth();
190+
mlir::Type charTy = cgm.getBuilder().getUIntNTy(charWidth);
191+
192+
// Offset of where we want the first bit to go within the bits of the
193+
// current char.
194+
unsigned offsetWithinChar = offsetInBits % charWidth;
195+
196+
// We split bit-fields up into individual bytes. Walk over the bytes and
197+
// update them.
198+
for (CharUnits offsetInChars =
199+
astContext.toCharUnitsFromBits(offsetInBits - offsetWithinChar);
200+
/**/; ++offsetInChars) {
201+
// Number of bits we want to fill in this char.
202+
unsigned wantedBits =
203+
std::min((uint64_t)bits.getBitWidth(), charWidth - offsetWithinChar);
204+
205+
// Get a char containing the bits we want in the right places. The other
206+
// bits have unspecified values.
207+
llvm::APInt bitsThisChar = bits;
208+
if (bitsThisChar.getBitWidth() < charWidth)
209+
bitsThisChar = bitsThisChar.zext(charWidth);
210+
if (cgm.getDataLayout().isBigEndian()) {
211+
// Figure out how much to shift by. We may need to left-shift if we have
212+
// less than one byte of Bits left.
213+
int shift = bits.getBitWidth() - charWidth + offsetWithinChar;
214+
if (shift > 0)
215+
bitsThisChar.lshrInPlace(shift);
216+
else if (shift < 0)
217+
bitsThisChar = bitsThisChar.shl(-shift);
218+
} else {
219+
bitsThisChar = bitsThisChar.shl(offsetWithinChar);
220+
}
221+
if (bitsThisChar.getBitWidth() > charWidth)
222+
bitsThisChar = bitsThisChar.trunc(charWidth);
223+
224+
if (wantedBits == charWidth) {
225+
// Got a full byte: just add it directly.
226+
add(cir::IntAttr::get(charTy, bitsThisChar), offsetInChars,
227+
allowOverwrite);
228+
} else {
229+
// Partial byte: update the existing integer if there is one. If we
230+
// can't split out a 1-CharUnit range to update, then we can't add
231+
// these bits and fail the entire constant emission.
232+
std::optional<size_t> firstElemToUpdate = splitAt(offsetInChars);
233+
if (!firstElemToUpdate)
234+
return false;
235+
std::optional<size_t> lastElemToUpdate =
236+
splitAt(offsetInChars + CharUnits::One());
237+
if (!lastElemToUpdate)
238+
return false;
239+
assert(*lastElemToUpdate - *firstElemToUpdate < 2 &&
240+
"should have at most one element covering one byte");
241+
242+
// Figure out which bits we want and discard the rest.
243+
llvm::APInt updateMask(charWidth, 0);
244+
if (cgm.getDataLayout().isBigEndian())
245+
updateMask.setBits(charWidth - offsetWithinChar - wantedBits,
246+
charWidth - offsetWithinChar);
247+
else
248+
updateMask.setBits(offsetWithinChar, offsetWithinChar + wantedBits);
249+
bitsThisChar &= updateMask;
250+
bool isNull = false;
251+
if (*firstElemToUpdate < elements.size()) {
252+
auto firstEltToUpdate =
253+
mlir::dyn_cast<cir::IntAttr>(elements[*firstElemToUpdate].element);
254+
isNull = firstEltToUpdate && firstEltToUpdate.isNullValue();
255+
}
256+
257+
if (*firstElemToUpdate == *lastElemToUpdate || isNull) {
258+
// All existing bits are either zero or undef.
259+
add(cir::IntAttr::get(charTy, bitsThisChar), offsetInChars,
260+
/*allowOverwrite*/ true);
261+
} else {
262+
cir::IntAttr ci =
263+
mlir::dyn_cast<cir::IntAttr>(elements[*firstElemToUpdate].element);
264+
// In order to perform a partial update, we need the existing bitwise
265+
// value, which we can only extract for a constant int.
266+
if (!ci)
267+
return false;
268+
// Because this is a 1-CharUnit range, the constant occupying it must
269+
// be exactly one CharUnit wide.
270+
assert(ci.getBitWidth() == charWidth && "splitAt failed");
271+
assert((!(ci.getValue() & updateMask) || allowOverwrite) &&
272+
"unexpectedly overwriting bitfield");
273+
bitsThisChar |= (ci.getValue() & ~updateMask);
274+
elements[*firstElemToUpdate].element =
275+
cir::IntAttr::get(charTy, bitsThisChar);
276+
}
277+
}
278+
279+
// Stop if we've added all the bits.
280+
if (wantedBits == bits.getBitWidth())
281+
break;
282+
283+
// Remove the consumed bits from Bits.
284+
if (!cgm.getDataLayout().isBigEndian())
285+
bits.lshrInPlace(wantedBits);
286+
bits = bits.trunc(bits.getBitWidth() - wantedBits);
287+
288+
// The remaining bits go at the start of the following bytes.
289+
offsetWithinChar = 0;
290+
}
291+
292+
return true;
293+
}
294+
295+
/// Returns a position within elements such that all elements
296+
/// before the returned index end before pos and all elements at or after
297+
/// the returned index begin at or after pos. Splits elements as necessary
298+
/// to ensure this. Returns std::nullopt if we find something we can't split.
299+
std::optional<size_t> ConstantAggregateBuilder::splitAt(CharUnits pos) {
300+
if (pos >= size)
301+
return elements.size();
302+
303+
while (true) {
304+
// Find the first element that starts after pos.
305+
Element *iter =
306+
llvm::upper_bound(elements, pos, [](CharUnits pos, const Element &elt) {
307+
return pos < elt.offset;
308+
});
309+
310+
if (iter == elements.begin())
311+
return 0;
312+
313+
size_t index = iter - elements.begin() - 1;
314+
const Element &elt = elements[index];
315+
316+
// If we already have an element starting at pos, we're done.
317+
if (elt.offset == pos)
318+
return index;
319+
320+
// Check for overlap with the element that starts before pos.
321+
CharUnits eltEnd = elt.offset + getSize(elt.element);
322+
if (eltEnd <= pos)
323+
return index + 1;
324+
325+
// Try to decompose it into smaller constants.
326+
if (!split(index, pos))
327+
return std::nullopt;
328+
}
329+
}
330+
331+
/// Split the constant at index, if possible. Return true if we did.
332+
/// Hint indicates the location at which we'd like to split, but may be
333+
/// ignored.
334+
bool ConstantAggregateBuilder::split(size_t index, CharUnits hint) {
335+
cgm.errorNYI("split constant at index");
336+
return false;
337+
}
338+
339+
void ConstantAggregateBuilder::condense(CharUnits offset,
340+
mlir::Type desiredTy) {
341+
CharUnits desiredSize = getSize(desiredTy);
342+
343+
std::optional<size_t> firstElemToReplace = splitAt(offset);
344+
if (!firstElemToReplace)
345+
return;
346+
size_t first = *firstElemToReplace;
347+
348+
std::optional<size_t> lastElemToReplace = splitAt(offset + desiredSize);
349+
if (!lastElemToReplace)
350+
return;
351+
size_t last = *lastElemToReplace;
352+
353+
size_t length = last - first;
354+
if (length == 0)
355+
return;
356+
357+
if (length == 1 && elements[first].offset == offset &&
358+
getSize(elements[first].element) == desiredSize) {
359+
cgm.errorNYI("re-wrapping single element records");
360+
return;
361+
}
362+
363+
// Build a new constant from the elements in the range.
364+
SmallVector<Element> subElems(elements.begin() + first,
365+
elements.begin() + last);
366+
mlir::Attribute replacement =
367+
buildFrom(cgm, subElems, offset, desiredSize,
368+
/*naturalLayout=*/false, desiredTy, false);
369+
370+
// Replace the range with the condensed constant.
371+
Element newElt(mlir::cast<mlir::TypedAttr>(replacement), offset);
372+
replace(elements, first, last, {newElt});
373+
}
374+
179375
mlir::Attribute
180376
ConstantAggregateBuilder::buildFrom(CIRGenModule &cgm, ArrayRef<Element> elems,
181377
CharUnits startOffset, CharUnits size,
@@ -301,6 +497,9 @@ class ConstRecordBuilder {
301497
bool appendBytes(CharUnits fieldOffsetInChars, mlir::TypedAttr initCst,
302498
bool allowOverwrite = false);
303499

500+
bool appendBitField(const FieldDecl *field, uint64_t fieldOffset,
501+
cir::IntAttr ci, bool allowOverwrite = false);
502+
304503
bool build(InitListExpr *ile, bool allowOverwrite);
305504
bool build(const APValue &val, const RecordDecl *rd, bool isPrimaryBase,
306505
const CXXRecordDecl *vTableClass, CharUnits baseOffset);
@@ -325,6 +524,30 @@ bool ConstRecordBuilder::appendBytes(CharUnits fieldOffsetInChars,
325524
return builder.add(initCst, startOffset + fieldOffsetInChars, allowOverwrite);
326525
}
327526

527+
bool ConstRecordBuilder::appendBitField(const FieldDecl *field,
528+
uint64_t fieldOffset, cir::IntAttr ci,
529+
bool allowOverwrite) {
530+
const CIRGenRecordLayout &rl =
531+
cgm.getTypes().getCIRGenRecordLayout(field->getParent());
532+
const CIRGenBitFieldInfo &info = rl.getBitFieldInfo(field);
533+
llvm::APInt fieldValue = ci.getValue();
534+
535+
// Promote the size of FieldValue if necessary
536+
// FIXME: This should never occur, but currently it can because initializer
537+
// constants are cast to bool, and because clang is not enforcing bitfield
538+
// width limits.
539+
if (info.size > fieldValue.getBitWidth())
540+
fieldValue = fieldValue.zext(info.size);
541+
542+
// Truncate the size of FieldValue to the bit field size.
543+
if (info.size < fieldValue.getBitWidth())
544+
fieldValue = fieldValue.trunc(info.size);
545+
546+
return builder.addBits(fieldValue,
547+
cgm.getASTContext().toBits(startOffset) + fieldOffset,
548+
allowOverwrite);
549+
}
550+
328551
bool ConstRecordBuilder::build(InitListExpr *ile, bool allowOverwrite) {
329552
RecordDecl *rd = ile->getType()
330553
->castAs<clang::RecordType>()
@@ -407,12 +630,14 @@ bool ConstRecordBuilder::build(InitListExpr *ile, bool allowOverwrite) {
407630
} else {
408631
// Otherwise we have a bitfield.
409632
if (auto constInt = dyn_cast<cir::IntAttr>(eltInit)) {
410-
assert(!cir::MissingFeatures::bitfields());
411-
cgm.errorNYI(field->getSourceRange(), "bitfields");
633+
if (!appendBitField(field, layout.getFieldOffset(index), constInt,
634+
allowOverwrite))
635+
return false;
636+
} else {
637+
// We are trying to initialize a bitfield with a non-trivial constant,
638+
// this must require run-time code.
639+
return false;
412640
}
413-
// We are trying to initialize a bitfield with a non-trivial constant,
414-
// this must require run-time code.
415-
return false;
416641
}
417642
}
418643

@@ -510,8 +735,16 @@ bool ConstRecordBuilder::build(const APValue &val, const RecordDecl *rd,
510735
if (field->hasAttr<NoUniqueAddressAttr>())
511736
allowOverwrite = true;
512737
} else {
513-
assert(!cir::MissingFeatures::bitfields());
514-
cgm.errorNYI(field->getSourceRange(), "bitfields");
738+
// Otherwise we have a bitfield.
739+
if (auto constInt = dyn_cast<cir::IntAttr>(eltInit)) {
740+
if (!appendBitField(field, layout.getFieldOffset(index) + offsetBits,
741+
constInt, allowOverwrite))
742+
return false;
743+
} else {
744+
// We are trying to initialize a bitfield with a non-trivial constant,
745+
// this must require run-time code.
746+
return false;
747+
}
515748
}
516749
}
517750

0 commit comments

Comments
 (0)