Skip to content

Commit 69d861e

Browse files
authored
[clang] Move tailclipping to bitfield allocation (#87090)
Move bitfield access clipping to bitfield access computation.
1 parent 905d2ec commit 69d861e

File tree

2 files changed

+62
-40
lines changed

2 files changed

+62
-40
lines changed

clang/lib/CodeGen/CGRecordLayoutBuilder.cpp

Lines changed: 44 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,11 @@ namespace {
4141
/// contains enough information to determine where the runs break. Microsoft
4242
/// and Itanium follow different rules and use different codepaths.
4343
/// * It is desired that, when possible, bitfields use the appropriate iN type
44-
/// when lowered to llvm types. For example unsigned x : 24 gets lowered to
44+
/// when lowered to llvm types. For example unsigned x : 24 gets lowered to
4545
/// i24. This isn't always possible because i24 has storage size of 32 bit
46-
/// and if it is possible to use that extra byte of padding we must use
47-
/// [i8 x 3] instead of i24. The function clipTailPadding does this.
46+
/// and if it is possible to use that extra byte of padding we must use [i8 x
47+
/// 3] instead of i24. This is computed when accumulating bitfields in
48+
/// accumulateBitfields.
4849
/// C++ examples that require clipping:
4950
/// struct { int a : 24; char b; }; // a must be clipped, b goes at offset 3
5051
/// struct A { int a : 24; ~A(); }; // a must be clipped because:
@@ -62,11 +63,7 @@ namespace {
6263
/// that the tail padding is not used in the complete class.) However,
6364
/// because LLVM reads from the complete type it can generate incorrect code
6465
/// if we do not clip the tail padding off of the bitfield in the complete
65-
/// layout. This introduces a somewhat awkward extra unnecessary clip stage.
66-
/// The location of the clip is stored internally as a sentinel of type
67-
/// SCISSOR. If LLVM were updated to read base types (which it probably
68-
/// should because locations of things such as VBases are bogus in the llvm
69-
/// type anyway) then we could eliminate the SCISSOR.
66+
/// layout.
7067
/// * Itanium allows nearly empty primary virtual bases. These bases don't get
7168
/// get their own storage because they're laid out as part of another base
7269
/// or at the beginning of the structure. Determining if a VBase actually
@@ -200,9 +197,7 @@ struct CGRecordLowering {
200197
const CXXRecordDecl *Query) const;
201198
void calculateZeroInit();
202199
CharUnits calculateTailClippingOffset(bool isNonVirtualBaseType) const;
203-
/// Lowers bitfield storage types to I8 arrays for bitfields with tail
204-
/// padding that is or can potentially be used.
205-
void clipTailPadding();
200+
void checkBitfieldClipping() const;
206201
/// Determines if we need a packed llvm struct.
207202
void determinePacked(bool NVBaseType);
208203
/// Inserts padding everywhere it's needed.
@@ -305,7 +300,7 @@ void CGRecordLowering::lower(bool NVBaseType) {
305300
}
306301
llvm::stable_sort(Members);
307302
Members.push_back(StorageInfo(Size, getIntNType(8)));
308-
clipTailPadding();
303+
checkBitfieldClipping();
309304
determinePacked(NVBaseType);
310305
insertPadding();
311306
Members.pop_back();
@@ -531,6 +526,7 @@ CGRecordLowering::accumulateBitFields(bool isNonVirtualBaseType,
531526
// available padding characters.
532527
RecordDecl::field_iterator BestEnd = Begin;
533528
CharUnits BestEndOffset;
529+
bool BestClipped; // Whether the representation must be in a byte array.
534530

535531
for (;;) {
536532
// AtAlignedBoundary is true iff Field is the (potential) start of a new
@@ -593,10 +589,9 @@ CGRecordLowering::accumulateBitFields(bool isNonVirtualBaseType,
593589
// this is the best seen so far.
594590
BestEnd = Field;
595591
BestEndOffset = BeginOffset + AccessSize;
596-
if (Types.getCodeGenOpts().FineGrainedBitfieldAccesses)
597-
// Fine-grained access, so no merging of spans.
598-
InstallBest = true;
599-
else if (!BitSizeSinceBegin)
592+
// Assume clipped until proven not below.
593+
BestClipped = true;
594+
if (!BitSizeSinceBegin)
600595
// A zero-sized initial span -- this will install nothing and reset
601596
// for another.
602597
InstallBest = true;
@@ -624,6 +619,12 @@ CGRecordLowering::accumulateBitFields(bool isNonVirtualBaseType,
624619
// The access unit is not at a naturally aligned offset within the
625620
// structure.
626621
InstallBest = true;
622+
623+
if (InstallBest && BestEnd == Field)
624+
// We're installing the first span, whose clipping was presumed
625+
// above. Compute it correctly.
626+
if (getSize(Type) == AccessSize)
627+
BestClipped = false;
627628
}
628629

629630
if (!InstallBest) {
@@ -656,11 +657,15 @@ CGRecordLowering::accumulateBitFields(bool isNonVirtualBaseType,
656657
// access unit.
657658
BestEndOffset = BeginOffset + TypeSize;
658659
BestEnd = Field;
660+
BestClipped = false;
659661
}
660662

661663
if (Barrier)
662664
// The next field is a barrier that we cannot merge across.
663665
InstallBest = true;
666+
else if (Types.getCodeGenOpts().FineGrainedBitfieldAccesses)
667+
// Fine-grained access, so no merging of spans.
668+
InstallBest = true;
664669
else
665670
// Otherwise, we're not installing. Update the bit size
666671
// of the current span to go all the way to LimitOffset, which is
@@ -679,7 +684,17 @@ CGRecordLowering::accumulateBitFields(bool isNonVirtualBaseType,
679684
// Add the storage member for the access unit to the record. The
680685
// bitfields get the offset of their storage but come afterward and
681686
// remain there after a stable sort.
682-
llvm::Type *Type = getIntNType(Context.toBits(AccessSize));
687+
llvm::Type *Type;
688+
if (BestClipped) {
689+
assert(getSize(getIntNType(Context.toBits(AccessSize))) >
690+
AccessSize &&
691+
"Clipped access need not be clipped");
692+
Type = getByteArrayType(AccessSize);
693+
} else {
694+
Type = getIntNType(Context.toBits(AccessSize));
695+
assert(getSize(Type) == AccessSize &&
696+
"Unclipped access must be clipped");
697+
}
683698
Members.push_back(StorageInfo(BeginOffset, Type));
684699
for (; Begin != BestEnd; ++Begin)
685700
if (!Begin->isZeroLengthBitField(Context))
@@ -934,32 +949,21 @@ void CGRecordLowering::calculateZeroInit() {
934949
}
935950
}
936951

937-
void CGRecordLowering::clipTailPadding() {
938-
std::vector<MemberInfo>::iterator Prior = Members.begin();
939-
CharUnits Tail = getSize(Prior->Data);
940-
for (std::vector<MemberInfo>::iterator Member = Prior + 1,
941-
MemberEnd = Members.end();
942-
Member != MemberEnd; ++Member) {
952+
// Verify accumulateBitfields computed the correct storage representations.
953+
void CGRecordLowering::checkBitfieldClipping() const {
954+
#ifndef NDEBUG
955+
auto Tail = CharUnits::Zero();
956+
for (const auto &M : Members) {
943957
// Only members with data and the scissor can cut into tail padding.
944-
if (!Member->Data && Member->Kind != MemberInfo::Scissor)
958+
if (!M.Data && M.Kind != MemberInfo::Scissor)
945959
continue;
946-
if (Member->Offset < Tail) {
947-
assert(Prior->Kind == MemberInfo::Field &&
948-
"Only storage fields have tail padding!");
949-
if (!Prior->FD || Prior->FD->isBitField())
950-
Prior->Data = getByteArrayType(bitsToCharUnits(llvm::alignTo(
951-
cast<llvm::IntegerType>(Prior->Data)->getIntegerBitWidth(), 8)));
952-
else {
953-
assert(Prior->FD->hasAttr<NoUniqueAddressAttr>() &&
954-
"should not have reused this field's tail padding");
955-
Prior->Data = getByteArrayType(
956-
Context.getTypeInfoDataSizeInChars(Prior->FD->getType()).Width);
957-
}
958-
}
959-
if (Member->Data)
960-
Prior = Member;
961-
Tail = Prior->Offset + getSize(Prior->Data);
960+
961+
assert(M.Offset >= Tail && "Bitfield access unit is not clipped");
962+
Tail = M.Offset;
963+
if (M.Data)
964+
Tail += getSize(M.Data);
962965
}
966+
#endif
963967
}
964968

965969
void CGRecordLowering::determinePacked(bool NVBaseType) {

clang/test/CodeGen/bitfield-access-unit.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,24 @@ struct G {
222222
// LAYOUT-DWN32-NEXT: <CGBitFieldInfo Offset:{{[0-9]+}} Size:7 IsSigned:1 StorageSize:8 StorageOffset:4
223223
// CHECK-NEXT: ]>
224224

225+
struct __attribute__((aligned(8))) H {
226+
char a;
227+
unsigned b : 24; // on expensive alignment we want this to stay 24
228+
unsigned c __attribute__((aligned(8))); // Think 'long long' or lp64 ptr
229+
} h;
230+
// CHECK-LABEL: LLVMType:%struct.H =
231+
// LAYOUT-FLEX-SAME: type <{ i8, i32, [3 x i8], i32, [4 x i8] }>
232+
// LAYOUT-STRICT-SAME: type { i8, [3 x i8], [4 x i8], i32, [4 x i8] }
233+
// LAYOUT-DWN32-FLEX-SAME: type <{ i8, i32, [3 x i8], i32, [4 x i8] }>
234+
// LAYOUT-DWN32-STRICT-SAME: type { i8, [3 x i8], [4 x i8], i32, [4 x i8] }
235+
// CHECK: BitFields:[
236+
// LAYOUT-FLEX-NEXT: <CGBitFieldInfo Offset:{{[0-9]+}} Size:24 IsSigned:0 StorageSize:32 StorageOffset:1
237+
// LAYOUT-STRICT-NEXT: <CGBitFieldInfo Offset:{{[0-9]+}} Size:24 IsSigned:0 StorageSize:24 StorageOffset:1
238+
239+
// LAYOUT-DWN32-FLEX-NEXT: <CGBitFieldInfo Offset:{{[0-9]+}} Size:24 IsSigned:0 StorageSize:32 StorageOffset:1
240+
// LAYOUT-DWN32-STRICT-NEXT: <CGBitFieldInfo Offset:{{[0-9]+}} Size:24 IsSigned:0 StorageSize:24 StorageOffset:1
241+
// CHECK-NEXT: ]>
242+
225243
#if _LP64
226244
struct A64 {
227245
int a : 16;

0 commit comments

Comments
 (0)