-
Notifications
You must be signed in to change notification settings - Fork 12.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow packing fields into tail padding of record fields #122197
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -733,6 +733,7 @@ bool ConstStructBuilder::Build(const InitListExpr *ILE, bool AllowOverwrite) { | |
|
||
for (FieldDecl *Field : RD->fields()) { | ||
++FieldNo; | ||
QualType FieldTy = Field->getType(); | ||
|
||
// If this is a union, skip all the fields that aren't being initialized. | ||
if (RD->isUnion() && | ||
|
@@ -773,23 +774,23 @@ bool ConstStructBuilder::Build(const InitListExpr *ILE, bool AllowOverwrite) { | |
// represents additional overwriting of our current constant value, and not | ||
// a new constant to emit independently. | ||
if (AllowOverwrite && | ||
(Field->getType()->isArrayType() || Field->getType()->isRecordType())) { | ||
(FieldTy->isArrayType() || FieldTy->isRecordType())) { | ||
if (auto *SubILE = dyn_cast<InitListExpr>(Init)) { | ||
CharUnits Offset = CGM.getContext().toCharUnitsFromBits( | ||
Layout.getFieldOffset(FieldNo)); | ||
if (!EmitDesignatedInitUpdater(Emitter, Builder, StartOffset + Offset, | ||
Field->getType(), SubILE)) | ||
FieldTy, SubILE)) | ||
return false; | ||
// If we split apart the field's value, try to collapse it down to a | ||
// single value now. | ||
Builder.condense(StartOffset + Offset, | ||
CGM.getTypes().ConvertTypeForMem(Field->getType())); | ||
CGM.getTypes().ConvertTypeForMem(FieldTy)); | ||
continue; | ||
} | ||
} | ||
|
||
llvm::Constant *EltInit = | ||
Init ? Emitter.tryEmitPrivateForMemory(Init, Field->getType()) | ||
Init ? Emitter.tryEmitPrivateForMemory(Init, FieldTy) | ||
: Emitter.emitNullForMemory(Field->getType()); | ||
if (!EltInit) | ||
return false; | ||
|
@@ -803,10 +804,16 @@ bool ConstStructBuilder::Build(const InitListExpr *ILE, bool AllowOverwrite) { | |
if (!AppendField(Field, Layout.getFieldOffset(FieldNo), EltInit, | ||
AllowOverwrite)) | ||
return false; | ||
// After emitting a non-empty field with [[no_unique_address]], we may | ||
// need to overwrite its tail padding. | ||
if (Field->hasAttr<NoUniqueAddressAttr>()) | ||
AllowOverwrite = true; | ||
|
||
// Allow overwrites after a field with tail padding. This allows | ||
// overwriting tail padding of fields carrying [[no_unique_address]] | ||
// without checking for it, since it is not necessarily present in debug | ||
// info. | ||
if (const CXXRecordDecl *FieldRD = FieldTy->getAsCXXRecordDecl()) { | ||
const ASTRecordLayout &Layout = CGM.getContext().getASTRecordLayout(FieldRD); | ||
if (Layout.getDataSize() < Layout.getSize()) | ||
AllowOverwrite = true; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At first glance, I'm not sure what this is doing... if you have a non-empty field, only the tail padding can be overwritten... but the CGRecordLayoutBuilder change should ensure the tail padding doesn't actually count as part of the field in that case? I'm probably missing something obvious. Do we need to adjust the other |
||
} | ||
} else { | ||
// Otherwise we have a bitfield. | ||
if (!AppendBitField(Field, Layout.getFieldOffset(FieldNo), EltInit, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -198,7 +198,7 @@ struct CGRecordLowering { | |
const CXXRecordDecl *Query) const; | ||
void calculateZeroInit(); | ||
CharUnits calculateTailClippingOffset(bool isNonVirtualBaseType) const; | ||
void checkBitfieldClipping(bool isNonVirtualBaseType) const; | ||
void checkForOverlap(bool isNonVirtualBaseType); | ||
/// Determines if we need a packed llvm struct. | ||
void determinePacked(bool NVBaseType); | ||
/// Inserts padding everywhere it's needed. | ||
|
@@ -300,7 +300,7 @@ void CGRecordLowering::lower(bool NVBaseType) { | |
accumulateVBases(); | ||
} | ||
llvm::stable_sort(Members); | ||
checkBitfieldClipping(NVBaseType); | ||
checkForOverlap(NVBaseType); | ||
Members.push_back(StorageInfo(Size, getIntNType(8))); | ||
determinePacked(NVBaseType); | ||
insertPadding(); | ||
|
@@ -389,14 +389,28 @@ void CGRecordLowering::accumulateFields(bool isNonVirtualBaseType) { | |
// Empty fields have no storage. | ||
++Field; | ||
} else { | ||
// Use base subobject layout for the potentially-overlapping field, | ||
// as it is done in RecordLayoutBuilder | ||
Members.push_back(MemberInfo( | ||
bitsToCharUnits(getFieldBitOffset(*Field)), MemberInfo::Field, | ||
Field->isPotentiallyOverlapping() | ||
? getStorageType(Field->getType()->getAsCXXRecordDecl()) | ||
: getStorageType(*Field), | ||
*Field)); | ||
CharUnits CurOffset = bitsToCharUnits(getFieldBitOffset(*Field)); | ||
llvm::Type *StorageType = getStorageType(*Field); | ||
|
||
// Detect cases when the next field needs to be packed into tail padding | ||
// of a record field. This is typically caused by [[no_unique_address]], | ||
// but we try to infer when that is the case rather than checking for the | ||
// attribute explicitly because the attribute is typically not present in | ||
// debug info. Use the base subobject LLVM struct type in these cases, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe state more explicitly that this is an issue when the class layout is computed from debug info? |
||
// which will be less than data size bytes. | ||
if (const CXXRecordDecl *FieldRD = | ||
Field->getType()->getAsCXXRecordDecl()) { | ||
CharUnits NextOffset = Layout.getNonVirtualSize(); | ||
auto NextField = std::next(Field); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think NextField needs to be the next non-empty field, specifically, not just any field. |
||
if (NextField != FieldEnd) | ||
NextOffset = bitsToCharUnits(getFieldBitOffset(*NextField)); | ||
if (CurOffset + getSize(StorageType) > NextOffset) { | ||
StorageType = getStorageType(FieldRD); | ||
} | ||
} | ||
|
||
Members.push_back( | ||
MemberInfo(CurOffset, MemberInfo::Field, StorageType, *Field)); | ||
++Field; | ||
} | ||
} | ||
|
@@ -948,19 +962,19 @@ void CGRecordLowering::calculateZeroInit() { | |
} | ||
|
||
// Verify accumulateBitfields computed the correct storage representations. | ||
void CGRecordLowering::checkBitfieldClipping(bool IsNonVirtualBaseType) const { | ||
void CGRecordLowering::checkForOverlap(bool IsNonVirtualBaseType) { | ||
#ifndef NDEBUG | ||
auto ScissorOffset = calculateTailClippingOffset(IsNonVirtualBaseType); | ||
auto Tail = CharUnits::Zero(); | ||
for (const auto &M : Members) { | ||
for (MemberInfo &M : Members) { | ||
// Only members with data could possibly overlap. | ||
if (!M.Data) | ||
continue; | ||
|
||
assert(M.Offset >= Tail && "Bitfield access unit is not clipped"); | ||
assert(M.Offset >= Tail && "LLVM struct member types overlap"); | ||
Tail = M.Offset + getSize(M.Data); | ||
assert((Tail <= ScissorOffset || M.Offset >= ScissorOffset) && | ||
"Bitfield straddles scissor offset"); | ||
"Member straddles scissor offset"); | ||
} | ||
#endif | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd probably describe this more generally - something about external AST providers "if an external AST provider (such as lldb) says this is what the layout they want"?
Oh, I see, you aren't actually checking for an external AST provider here - but for the layout that implies no_unique_address or similar.
I guess then, maybe "even in the absence of [[no_unique_address]], such as when using an external AST provider like lldb"?