Skip to content

Commit 87d1c38

Browse files
Update accumulated bit fields algorithm
1 parent 215b3e0 commit 87d1c38

File tree

6 files changed

+321
-128
lines changed

6 files changed

+321
-128
lines changed

clang/lib/CIR/CodeGen/CIRGenRecordLayout.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,26 +42,26 @@ namespace clang::CIRGen {
4242
/// };
4343
///
4444
/// This will end up as the following cir.record. The bitfield members are
45-
/// represented by two !u8i values, and the array provides padding to align the
45+
/// represented by one !u16i value, and the array provides padding to align the
4646
/// struct to a 4-byte alignment.
4747
///
48-
/// !rec_S = !cir.record<struct "S" padded {!s8i, !s8i, !s8i, !u8i, !u8i,
48+
/// !rec_S = !cir.record<struct "S" padded {!s8i, !s8i, !s8i, !u16i,
4949
/// !cir.array<!u8i x 3>}>
5050
///
5151
/// When generating code to access more_bits, we'll generate something
5252
/// essentially like this:
5353
///
5454
/// #bfi_more_bits = #cir.bitfield_info<name = "more_bits", storage_type =
55-
/// !u8i, size = 4, offset = 3, is_signed = false>
55+
/// !u16i, size = 4, offset = 3, is_signed = false>
5656
///
5757
/// cir.func @store_field() {
5858
/// %0 = cir.alloca !rec_S, !cir.ptr<!rec_S>, ["s"] {alignment = 4 : i64}
5959
/// %1 = cir.const #cir.int<2> : !s32i
6060
/// %2 = cir.cast(integral, %1 : !s32i), !u32i
6161
/// %3 = cir.get_member %0[3] {name = "more_bits"} : !cir.ptr<!rec_S> ->
62-
/// !cir.ptr<!u8i>
62+
/// !cir.ptr<!u16i>
6363
/// %4 = cir.set_bitfield(#bfi_more_bits, %3 :
64-
/// !cir.ptr<!u8i>, %2 : !u32i) -> !u32i
64+
/// !cir.ptr<!u16i>, %2 : !u32i) -> !u32i
6565
/// cir.return
6666
/// }
6767
///

clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp

Lines changed: 188 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,9 @@ struct CIRRecordLowering final {
8282
void accumulateBases(const CXXRecordDecl *cxxRecordDecl);
8383
void accumulateVPtrs();
8484
void accumulateFields();
85-
void accumulateBitFields(RecordDecl::field_iterator field,
86-
RecordDecl::field_iterator fieldEnd);
85+
RecordDecl::field_iterator
86+
accumulateBitFields(RecordDecl::field_iterator field,
87+
RecordDecl::field_iterator fieldEnd);
8788

8889
CharUnits bitsToCharUnits(uint64_t bitOffset) {
8990
return astContext.toCharUnitsFromBits(bitOffset);
@@ -290,87 +291,199 @@ void CIRRecordLowering::fillOutputFields() {
290291
}
291292
}
292293

293-
void CIRRecordLowering::accumulateBitFields(
294-
RecordDecl::field_iterator field, RecordDecl::field_iterator fieldEnd) {
295-
// 'run' stores the first element of the current run of bitfields. 'fieldEnd'
296-
// is used as a special value to note that we don't have a current run. A
297-
// bitfield run is a contiguous collection of bitfields that can be stored in
298-
// the same storage block. Zero-sized bitfields and bitfields that would
299-
// cross an alignment boundary break a run and start a new one.
300-
RecordDecl::field_iterator run = fieldEnd;
301-
// 'tail' is the offset of the first bit off the end of the current run. It's
302-
// used to determine if the ASTRecordLayout is treating these two bitfields as
303-
// contiguous. 'startBitOffset' is offset of the beginning of the run.
304-
uint64_t startBitOffset, tail = 0;
294+
RecordDecl::field_iterator
295+
CIRRecordLowering::accumulateBitFields(RecordDecl::field_iterator field,
296+
RecordDecl::field_iterator fieldEnd) {
305297
assert(!cir::MissingFeatures::isDiscreteBitFieldABI());
306298

307-
// Check if 'offsetInRecord' (the size in bits of the current run) is better
308-
// as a single field run. When OffsetInRecord has legal integer width, and
309-
// its bitfield offset is naturally aligned, it is better to make the
310-
// bitfield a separate storage component so as it can be accessed directly
311-
// with lower cost.
312-
assert(!cir::MissingFeatures::nonFineGrainedBitfields());
299+
CharUnits regSize =
300+
bitsToCharUnits(astContext.getTargetInfo().getRegisterWidth());
301+
unsigned charBits = astContext.getCharWidth();
302+
303+
// Data about the start of the span we're accumulating to create an access
304+
// unit from. 'Begin' is the first bitfield of the span. If 'begin' is
305+
// 'fieldEnd', we've not got a current span. The span starts at the
306+
// 'beginOffset' character boundary. 'bitSizeSinceBegin' is the size (in bits)
307+
// of the span -- this might include padding when we've advanced to a
308+
// subsequent bitfield run.
309+
RecordDecl::field_iterator begin = fieldEnd;
310+
CharUnits beginOffset;
311+
uint64_t bitSizeSinceBegin;
312+
313+
// The (non-inclusive) end of the largest acceptable access unit we've found
314+
// since 'begin'. If this is 'begin', we're gathering the initial set of
315+
// bitfields of a new span. 'bestEndOffset' is the end of that acceptable
316+
// access unit -- it might extend beyond the last character of the bitfield
317+
// run, using available padding characters.
318+
RecordDecl::field_iterator bestEnd = begin;
319+
CharUnits bestEndOffset;
320+
bool bestClipped; // Whether the representation must be in a byte array.
313321

314322
for (;;) {
315-
// Check to see if we need to start a new run.
316-
if (run == fieldEnd) {
317-
// If we're out of fields, return.
318-
if (field == fieldEnd)
323+
// atAlignedBoundary is true if 'field' is the (potential) start of a new
324+
// span (or the end of the bitfields). When true, limitOffset is the
325+
// character offset of that span and barrier indicates whether the new
326+
// span cannot be merged into the current one.
327+
bool atAlignedBoundary = false;
328+
bool barrier = false; // a barrier can be a zero Bit Width or non bit member
329+
if (field != fieldEnd && field->isBitField()) {
330+
uint64_t bitOffset = getFieldBitOffset(*field);
331+
if (begin == fieldEnd) {
332+
// Beginning a new span.
333+
begin = field;
334+
bestEnd = begin;
335+
336+
assert((bitOffset % charBits) == 0 && "Not at start of char");
337+
beginOffset = bitsToCharUnits(bitOffset);
338+
bitSizeSinceBegin = 0;
339+
} else if ((bitOffset % charBits) != 0) {
340+
// Bitfield occupies the same character as previous bitfield, it must be
341+
// part of the same span. This can include zero-length bitfields, should
342+
// the target not align them to character boundaries. Such non-alignment
343+
// is at variance with the standards, which require zero-length
344+
// bitfields be a barrier between access units. But of course we can't
345+
// achieve that in the middle of a character.
346+
assert(bitOffset ==
347+
astContext.toBits(beginOffset) + bitSizeSinceBegin &&
348+
"Concatenating non-contiguous bitfields");
349+
} else {
350+
// Bitfield potentially begins a new span. This includes zero-length
351+
// bitfields on non-aligning targets that lie at character boundaries
352+
// (those are barriers to merging).
353+
if (field->isZeroLengthBitField())
354+
barrier = true;
355+
atAlignedBoundary = true;
356+
}
357+
} else {
358+
// We've reached the end of the bitfield run. Either we're done, or this
359+
// is a barrier for the current span.
360+
if (begin == fieldEnd)
319361
break;
320-
// Any non-zero-length bitfield can start a new run.
321-
if (!field->isZeroLengthBitField()) {
322-
run = field;
323-
startBitOffset = getFieldBitOffset(*field);
324-
tail = startBitOffset + field->getBitWidthValue();
325-
assert(!cir::MissingFeatures::nonFineGrainedBitfields());
362+
363+
barrier = true;
364+
atAlignedBoundary = true;
365+
}
366+
367+
// 'installBest' indicates whether we should create an access unit for the
368+
// current best span: fields ['begin', 'bestEnd') occupying characters
369+
// ['beginOffset', 'bestEndOffset').
370+
bool installBest = false;
371+
if (atAlignedBoundary) {
372+
// 'field' is the start of a new span or the end of the bitfields. The
373+
// just-seen span now extends to 'bitSizeSinceBegin'.
374+
375+
// Determine if we can accumulate that just-seen span into the current
376+
// accumulation.
377+
CharUnits accessSize = bitsToCharUnits(bitSizeSinceBegin + charBits - 1);
378+
if (bestEnd == begin) {
379+
// This is the initial run at the start of a new span. By definition,
380+
// this is the best seen so far.
381+
bestEnd = field;
382+
bestEndOffset = beginOffset + accessSize;
383+
// Assume clipped until proven not below.
384+
bestClipped = true;
385+
if (!bitSizeSinceBegin)
386+
// A zero-sized initial span -- this will install nothing and reset
387+
// for another.
388+
installBest = true;
389+
} else if (accessSize > regSize) {
390+
// Accumulating the just-seen span would create a multi-register access
391+
// unit, which would increase register pressure.
392+
installBest = true;
393+
}
394+
395+
if (!installBest) {
396+
// Determine if accumulating the just-seen span will create an expensive
397+
// access unit or not.
398+
mlir::Type type = getUIntNType(astContext.toBits(accessSize));
399+
if (!astContext.getTargetInfo().hasCheapUnalignedBitFieldAccess())
400+
cirGenTypes.getCGModule().errorNYI(
401+
field->getSourceRange(), "NYI CheapUnalignedBitFieldAccess");
402+
403+
if (!installBest) {
404+
// Find the next used storage offset to determine what the limit of
405+
// the current span is. That's either the offset of the next field
406+
// with storage (which might be field itself) or the end of the
407+
// non-reusable tail padding.
408+
CharUnits limitOffset;
409+
for (auto probe = field; probe != fieldEnd; ++probe)
410+
if (!isEmptyFieldForLayout(astContext, *probe)) {
411+
// A member with storage sets the limit.
412+
assert((getFieldBitOffset(*probe) % charBits) == 0 &&
413+
"Next storage is not byte-aligned");
414+
limitOffset = bitsToCharUnits(getFieldBitOffset(*probe));
415+
goto FoundLimit;
416+
}
417+
assert(!cir::MissingFeatures::cxxSupport());
418+
limitOffset = astRecordLayout.getDataSize();
419+
FoundLimit:
420+
CharUnits typeSize = getSize(type);
421+
if (beginOffset + typeSize <= limitOffset) {
422+
// There is space before limitOffset to create a naturally-sized
423+
// access unit.
424+
bestEndOffset = beginOffset + typeSize;
425+
bestEnd = field;
426+
bestClipped = false;
427+
}
428+
if (barrier) {
429+
// The next field is a barrier that we cannot merge across.
430+
installBest = true;
431+
} else if (cirGenTypes.getCGModule()
432+
.getCodeGenOpts()
433+
.FineGrainedBitfieldAccesses) {
434+
assert(!cir::MissingFeatures::nonFineGrainedBitfields());
435+
cirGenTypes.getCGModule().errorNYI(field->getSourceRange(),
436+
"NYI FineGrainedBitfield");
437+
} else {
438+
// Otherwise, we're not installing. Update the bit size
439+
// of the current span to go all the way to limitOffset, which is
440+
// the (aligned) offset of next bitfield to consider.
441+
bitSizeSinceBegin = astContext.toBits(limitOffset - beginOffset);
442+
}
443+
}
326444
}
327-
++field;
328-
continue;
329445
}
330446

331-
// Decide whether to continue extending the current bitfield run.
332-
//
333-
// Skip the block below and go directly to emitting storage if any of the
334-
// following is true:
335-
// - 1. The first field in the run is better treated as its own run.
336-
// - 2. We have reached the end of the fields.
337-
// - 3. The current field (or set of fields) is better as its own run.
338-
// - 4. The current field is a zero-width bitfield or:
339-
// - Zero-length bitfield alignment is enabled, and
340-
// - Bitfield type alignment is enabled.
341-
// - 5. The current field's offset doesn't match the expected tail (i.e.,
342-
// layout isn't contiguous).
343-
//
344-
// If none of the above conditions are met, add the current field to the
345-
// current run.
346-
uint64_t nextTail = tail;
347-
if (field != fieldEnd)
348-
nextTail += field->getBitWidthValue();
349-
350-
// TODO: add condition 1 and 3
351-
assert(!cir::MissingFeatures::nonFineGrainedBitfields());
352-
if (field != fieldEnd &&
353-
(!field->isZeroLengthBitField() ||
354-
(!astContext.getTargetInfo().useZeroLengthBitfieldAlignment() &&
355-
!astContext.getTargetInfo().useBitFieldTypeAlignment())) &&
356-
tail == getFieldBitOffset(*field)) {
357-
tail = nextTail;
447+
if (installBest) {
448+
assert((field == fieldEnd || !field->isBitField() ||
449+
(getFieldBitOffset(*field) % charBits) == 0) &&
450+
"Installing but not at an aligned bitfield or limit");
451+
CharUnits accessSize = bestEndOffset - beginOffset;
452+
if (!accessSize.isZero()) {
453+
// Add the storage member for the access unit to the record. The
454+
// bitfields get the offset of their storage but come afterward and
455+
// remain there after a stable sort.
456+
mlir::Type type;
457+
if (bestClipped) {
458+
assert(getSize(getUIntNType(astContext.toBits(accessSize))) >
459+
accessSize &&
460+
"Clipped access need not be clipped");
461+
type = getByteArrayType(accessSize);
462+
} else {
463+
type = getUIntNType(astContext.toBits(accessSize));
464+
assert(getSize(type) == accessSize &&
465+
"Unclipped access must be clipped");
466+
}
467+
members.push_back(makeStorageInfo(beginOffset, type));
468+
for (; begin != bestEnd; ++begin)
469+
if (!begin->isZeroLengthBitField())
470+
members.push_back(MemberInfo(
471+
beginOffset, MemberInfo::InfoKind::Field, nullptr, *begin));
472+
}
473+
// Reset to start a new span.
474+
field = bestEnd;
475+
begin = fieldEnd;
476+
} else {
477+
assert(field != fieldEnd && field->isBitField() &&
478+
"Accumulating past end of bitfields");
479+
assert(!barrier && "Accumulating across barrier");
480+
// Accumulate this bitfield into the current (potential) span.
481+
bitSizeSinceBegin += field->getBitWidthValue();
358482
++field;
359-
continue;
360483
}
361-
362-
// We've hit a break-point in the run and need to emit a storage field.
363-
mlir::Type type = getBitfieldStorageType(tail - startBitOffset);
364-
365-
// Add the storage member to the record and set the bitfield info for all of
366-
// the bitfields in the run. Bitfields get the offset of their storage but
367-
// come afterward and remain there after a stable sort.
368-
members.push_back(makeStorageInfo(bitsToCharUnits(startBitOffset), type));
369-
for (; run != field; ++run)
370-
members.push_back(MemberInfo(bitsToCharUnits(startBitOffset),
371-
MemberInfo::InfoKind::Field, nullptr, *run));
372-
run = fieldEnd;
373484
}
485+
486+
return field;
374487
}
375488

376489
void CIRRecordLowering::accumulateFields() {
@@ -382,7 +495,9 @@ void CIRRecordLowering::accumulateFields() {
382495
// Iterate to gather the list of bitfields.
383496
for (++field; field != fieldEnd && field->isBitField(); ++field)
384497
;
385-
accumulateBitFields(start, field);
498+
field = accumulateBitFields(start, field);
499+
assert((field == fieldEnd || !field->isBitField()) &&
500+
"Failed to accumulate all the bitfields");
386501
} else if (!field->isZeroSize(astContext)) {
387502
members.push_back(MemberInfo(bitsToCharUnits(getFieldBitOffset(*field)),
388503
MemberInfo::InfoKind::Field,

clang/lib/CIR/CodeGen/TargetInfo.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,43 @@
33

44
using namespace clang;
55
using namespace clang::CIRGen;
6+
7+
bool clang::CIRGen::isEmptyRecordForLayout(const ASTContext &context,
8+
QualType t) {
9+
const RecordType *rt = t->getAs<RecordType>();
10+
if (!rt)
11+
return false;
12+
13+
const RecordDecl *rd = rt->getDecl();
14+
15+
// If this is a C++ record, check the bases first.
16+
if (const CXXRecordDecl *cxxrd = dyn_cast<CXXRecordDecl>(rd)) {
17+
if (cxxrd->isDynamicClass())
18+
return false;
19+
20+
for (const auto &I : cxxrd->bases())
21+
if (!isEmptyRecordForLayout(context, I.getType()))
22+
return false;
23+
}
24+
25+
for (const auto *I : rd->fields())
26+
if (!isEmptyFieldForLayout(context, I))
27+
return false;
28+
29+
return true;
30+
}
31+
32+
bool clang::CIRGen::isEmptyFieldForLayout(const ASTContext &context,
33+
const FieldDecl *fd) {
34+
if (fd->isZeroLengthBitField())
35+
return true;
36+
37+
if (fd->isUnnamedBitField())
38+
return false;
39+
40+
return isEmptyRecordForLayout(context, fd->getType());
41+
}
42+
643
namespace {
744

845
class X8664ABIInfo : public ABIInfo {

clang/lib/CIR/CodeGen/TargetInfo.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@
2222

2323
namespace clang::CIRGen {
2424

25+
/// isEmptyFieldForLayout - Return true if the field is "empty", that is,
26+
/// either a zero-width bit-field or an isEmptyRecordForLayout.
27+
bool isEmptyFieldForLayout(const ASTContext &context, const FieldDecl *fd);
28+
29+
/// isEmptyRecordForLayout - Return true if a structure contains only empty
30+
/// base classes (per isEmptyRecordForLayout) and fields (per
31+
/// isEmptyFieldForLayout). Note, C++ record fields are considered empty
32+
/// if the [[no_unique_address]] attribute would have made them empty.
33+
bool isEmptyRecordForLayout(const ASTContext &context, QualType t);
34+
2535
class TargetCIRGenInfo {
2636
std::unique_ptr<ABIInfo> info;
2737

0 commit comments

Comments
 (0)