diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h --- a/clang/lib/CodeGen/CGDebugInfo.h +++ b/clang/lib/CodeGen/CGDebugInfo.h @@ -321,11 +321,16 @@ RD); } - /// Create new bit field member. + /// Create new bit field member and zero-size separator if needed. llvm::DIType *createBitFieldType(const FieldDecl *BitFieldDecl, llvm::DIScope *RecordTy, const RecordDecl *RD); + llvm::DIType * + createBitFieldZeroLengthSeparatorType(const FieldDecl *BitFieldDecl, + llvm::DIScope *RecordTy, + const RecordDecl *RD); + /// Helpers for collecting fields of a record. /// @{ void CollectRecordLambdaFields(const CXXRecordDecl *CXXDecl, diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -1466,6 +1466,35 @@ return F; } +llvm::DIType *CGDebugInfo::createBitFieldZeroLengthSeparatorType( + const FieldDecl *BitFieldDecl, llvm::DIScope *RecordTy, + const RecordDecl *RD) { + const CGBitFieldInfo &BitFieldInfo = + CGM.getTypes().getCGRecordLayout(RD).getBitFieldInfo(BitFieldDecl); + + FieldDecl *ZeroLengthSeparator = BitFieldInfo.RunStartedBy0LenghtBitField; + if (!ZeroLengthSeparator) + return nullptr; + + // This bitfield doesn't really exist in practice. Make it point to the + // begining of the storage offset (the word in the struct). This works for any + // endianness. + uint64_t StorageOffsetInBits = + CGM.getContext().toBits(BitFieldInfo.StorageOffset); + uint64_t OffsetInBits = StorageOffsetInBits; + + QualType Ty = ZeroLengthSeparator->getType(); + SourceLocation Loc = ZeroLengthSeparator->getLocation(); + llvm::DIFile *VUnit = getOrCreateFile(Loc); + llvm::DIType *DebugType = getOrCreateType(Ty, VUnit); + + llvm::DIFile *File = getOrCreateFile(Loc); + unsigned ZeroLine = getLineNumber(Loc); + return DBuilder.createBitFieldMemberType( + RecordTy, "", File, ZeroLine, 0, OffsetInBits, StorageOffsetInBits, + llvm::DINode::DIFlags::FlagZero, DebugType, {}); +} + llvm::DIType *CGDebugInfo::createBitFieldType(const FieldDecl *BitFieldDecl, llvm::DIScope *RecordTy, const RecordDecl *RD) { @@ -1607,6 +1636,9 @@ llvm::DIType *FieldType; if (field->isBitField()) { + if (llvm::DIType *RunSeparator = + createBitFieldZeroLengthSeparatorType(field, RecordTy, RD)) + elements.push_back(RunSeparator); FieldType = createBitFieldType(field, RecordTy, RD); } else { auto Align = getDeclAlignIfRequired(field, CGM.getContext()); diff --git a/clang/lib/CodeGen/CGRecordLayout.h b/clang/lib/CodeGen/CGRecordLayout.h --- a/clang/lib/CodeGen/CGRecordLayout.h +++ b/clang/lib/CodeGen/CGRecordLayout.h @@ -92,14 +92,24 @@ /// The offset of the bitfield storage from the start of the struct. CharUnits VolatileStorageOffset; + /// On AMDGPU, the calling convention states that adjacent bitfields + /// (not separated by a 0-length annonymous bitfield) are considered as a + /// whole field passed together in the same register. + FieldDecl *RunStartedBy0LenghtBitField; + CGBitFieldInfo() : Offset(), Size(), IsSigned(), StorageSize(), VolatileOffset(), - VolatileStorageSize() {} + VolatileStorageSize(), RunStartedBy0LenghtBitField(nullptr) {} CGBitFieldInfo(unsigned Offset, unsigned Size, bool IsSigned, unsigned StorageSize, CharUnits StorageOffset) : Offset(Offset), Size(Size), IsSigned(IsSigned), - StorageSize(StorageSize), StorageOffset(StorageOffset) {} + StorageSize(StorageSize), StorageOffset(StorageOffset), + RunStartedBy0LenghtBitField(nullptr) {} + + void setRunStartedBy0Length0BitField(FieldDecl *Zero) { + RunStartedBy0LenghtBitField = Zero; + } void print(raw_ostream &OS) const; void dump() const; diff --git a/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp b/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp --- a/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp +++ b/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -#include "CGRecordLayout.h" #include "CGCXXABI.h" +#include "CGRecordLayout.h" #include "CodeGenTypes.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" @@ -465,6 +465,8 @@ // The start field is better as a single field run. bool StartFieldAsSingleRun = false; + FieldDecl *ZeroBitfieldStartingRun = nullptr; + for (;;) { // Check to see if we need to start a new run. if (Run == FieldEnd) { @@ -478,6 +480,15 @@ Tail = StartBitOffset + Field->getBitWidthValue(Context); StartFieldAsSingleRun = IsBetterAsSingleFieldRun(Tail - StartBitOffset, StartBitOffset); + + if (ZeroBitfieldStartingRun) { + BitFields[*Field].setRunStartedBy0Length0BitField( + ZeroBitfieldStartingRun); + ZeroBitfieldStartingRun = nullptr; + } + } else { + if (getFieldBitOffset(*Field) != 0) + ZeroBitfieldStartingRun = *Field; } ++Field; continue; @@ -487,7 +498,6 @@ // if current field (or consecutive fields) is better as a single run, or // if current field has zero width bitfield and either // UseZeroLengthBitfieldAlignment or UseBitFieldTypeAlignment is set to - // true, or // if the offset of current field is inconsistent with the offset of // previous field plus its offset, // skip the block below and go ahead to emit the storage. @@ -1057,7 +1067,9 @@ << " StorageOffset:" << StorageOffset.getQuantity() << " VolatileOffset:" << VolatileOffset << " VolatileStorageSize:" << VolatileStorageSize - << " VolatileStorageOffset:" << VolatileStorageOffset.getQuantity() << ">"; + << " VolatileStorageOffset:" << VolatileStorageOffset.getQuantity() + << " RunStarteddByLenght0BitField:" + << (RunStartedBy0LenghtBitField ? "true" : "false") << ">"; } LLVM_DUMP_METHOD void CGBitFieldInfo::dump() const { diff --git a/clang/test/CodeGen/debug-info-bitfield-0-struct.c b/clang/test/CodeGen/debug-info-bitfield-0-struct.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/debug-info-bitfield-0-struct.c @@ -0,0 +1,97 @@ +// RUN: %clang_cc1 -triple x86_64-unk-unk -o - -emit-llvm -debug-info-kind=limited %s | FileCheck %s + +struct First { + // CHECK-DAG: ![[FIRST:[0-9]+]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "First", file: !{{[0-9]+}}, line: {{[0-9]+}}, size: 32, elements: ![[FIRST_ELEMENTS:[0-9]+]]) + // CHECK-DAG: ![[FIRST_ELEMENTS]] = !{![[FIRST_X:[0-9]+]], ![[FIRST_Y:[0-9]+]]} + // CHECK-DAG: ![[FIRST_X]] = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: ![[FIRST]], file: !{{[0-9]+}}, line: {{[0-9]+}}, baseType: !{{[0-9]+}}, size: 4, flags: DIFlagBitField, extraData: i64 0) + // CHECK-DAG: ![[FIRST_Y]] = !DIDerivedType(tag: DW_TAG_member, name: "y", scope: ![[FIRST]], file: !{{[0-9]+}}, line: {{[0-9]+}}, baseType: !{{[0-9]+}}, size: 4, offset: 4, flags: DIFlagBitField, extraData: i64 0) + int : 0; + int x : 4; + int y : 4; +}; + +struct FirstDuplicate { + // CHECK-DAG: ![[FIRSTD:[0-9]+]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "FirstDuplicate", file: !{{[0-9]+}}, line: {{[0-9]+}}, size: 32, elements: ![[FIRSTD_ELEMENTS:[0-9]+]]) + // CHECK-DAG: ![[FIRSTD_ELEMENTS]] = !{![[FIRSTD_X:[0-9]+]], ![[FIRSTD_Y:[0-9]+]]} + // CHECK-DAG: ![[FIRSTD_X]] = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: ![[FIRSTD]], file: !{{[0-9]+}}, line: {{[0-9]+}}, baseType: !{{[0-9]+}}, size: 4, flags: DIFlagBitField, extraData: i64 0) + // CHECK-DAG: ![[FIRSTD_Y]] = !DIDerivedType(tag: DW_TAG_member, name: "y", scope: ![[FIRSTD]], file: !{{[0-9]+}}, line: {{[0-9]+}}, baseType: !{{[0-9]+}}, size: 4, offset: 4, flags: DIFlagBitField, extraData: i64 0) + int : 0; + int : 0; + int x : 4; + int y : 4; +}; + +struct Second { + // CHECK-DAG: ![[SECOND:[0-9]+]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Second", file: !{{[0-9]+}}, line: {{[0-9]+}}, size: 64, elements: ![[SECOND_ELEMENTS:[0-9]+]]) + // CHECK-DAG: ![[SECOND_ELEMENTS]] = !{![[SECOND_X:[0-9]+]], ![[SECOND_ZERO:[0-9]+]], ![[SECOND_Y:[0-9]+]]} + // CHECK-DAG: ![[SECOND_X]] = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: ![[SECOND]], file: !{{[0-9]+}}, line: {{[0-9]+}}, baseType: !{{[0-9]+}}, size: 4, flags: DIFlagBitField, extraData: i64 0) + // CHECK-DAG: ![[SECOND_ZERO]] = !DIDerivedType(tag: DW_TAG_member, scope: ![[SECOND]], file: !{{[0-9]+}}, line: {{[0-9]+}}, baseType: !{{[0-9]+}}, offset: 32, flags: DIFlagBitField, extraData: i64 32) + // CHECK-DAG: ![[SECOND_Y]] = !DIDerivedType(tag: DW_TAG_member, name: "y", scope: ![[SECOND]], file: !{{[0-9]+}}, line: {{[0-9]+}}, baseType: !{{[0-9]+}}, size: 4, offset: 32, flags: DIFlagBitField, extraData: i64 32) + int x : 4; + int : 0; + int y : 4; +}; + +struct SecondDuplicate { + // CHECK-DAG: ![[SECONDD:[0-9]+]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "SecondDuplicate", file: !{{[0-9]+}}, line: {{[0-9]+}}, size: 64, elements: ![[SECONDD_ELEMENTS:[0-9]+]]) + // CHECK-DAG: ![[SECONDD_ELEMENTS]] = !{![[SECONDD_X:[0-9]+]], ![[SECONDD_ZERO:[0-9]+]], ![[SECONDD_Y:[0-9]+]]} + // CHECK-DAG: ![[SECONDD_X]] = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: ![[SECONDD]], file: !{{[0-9]+}}, line: {{[0-9]+}}, baseType: !{{[0-9]+}}, size: 4, flags: DIFlagBitField, extraData: i64 0) + // CHECK-DAG: ![[SECONDD_ZERO]] = !DIDerivedType(tag: DW_TAG_member, scope: ![[SECONDD]], file: !{{[0-9]+}}, line: {{[0-9]+}}, baseType: !{{[0-9]+}}, offset: 32, flags: DIFlagBitField, extraData: i64 32) + // CHECK-DAG: ![[SECONDD_Y]] = !DIDerivedType(tag: DW_TAG_member, name: "y", scope: ![[SECONDD]], file: !{{[0-9]+}}, line: {{[0-9]+}}, baseType: !{{[0-9]+}}, size: 4, offset: 32, flags: DIFlagBitField, extraData: i64 32) + int x : 4; + int : 0; + int : 0; + int y : 4; +}; + +struct Last { + // CHECK-DAG: ![[LAST:[0-9]+]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Last", file: !{{[0-9]+}}, line: {{[0-9]+}}, size: 32, elements: ![[LAST_ELEMENTS:[0-9]+]]) + // CHECK-DAG: ![[LAST_ELEMENTS]] = !{![[LAST_X:[0-9]+]], ![[LAST_Y:[0-9]+]]} + // CHECK-DAG: ![[LAST_X]] = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: ![[LAST]], file: !{{[0-9]+}}, line: {{[0-9]+}}, baseType: !{{[0-9]+}}, size: 4, flags: DIFlagBitField, extraData: i64 0) + // CHECK-DAG: ![[LAST_Y]] = !DIDerivedType(tag: DW_TAG_member, name: "y", scope: ![[LAST]], file: !{{[0-9]+}}, line: {{[0-9]+}}, baseType: !{{[0-9]+}}, size: 4, offset: 4, flags: DIFlagBitField, extraData: i64 0) + int x : 4; + int y : 4; + int : 0; +}; + +struct Several { + // CHECK-DAG: ![[SEVERAL:[0-9]+]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Several", file: !{{[0-9]+}}, line: {{[0-9]+}}, size: 96, elements: ![[SEVERAL_ELEMENTS:[0-9]+]]) + // CHECK-DAG: ![[SEVERAL_ELEMENTS]] = !{![[SEVERAL_X:[0-9]+]], ![[SEVERAL_FIRST_ZERO:[0-9]+]], ![[SEVERAL_Y:[0-9]+]], ![[SEVERAL_SECOND_ZERO:[0-9]+]], ![[SEVERAL_Z:[0-9]+]]} + // CHECK-DAG: ![[SEVERAL_X]] = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: ![[SEVERAL]], file: !{{[0-9]+}}, line: {{[0-9]+}}, baseType: !{{[0-9]+}}, size: 4, flags: DIFlagBitField, extraData: i64 0) + // CHECK-DAG: ![[SEVERAL_FIRST_ZERO]] = !DIDerivedType(tag: DW_TAG_member, scope: ![[SEVERAL]], file: !{{[0-9]+}}, line: {{[0-9]+}}, baseType: !{{[0-9]+}}, offset: 32, flags: DIFlagBitField, extraData: i64 32) + // CHECK-DAG: ![[SEVERAL_Y]] = !DIDerivedType(tag: DW_TAG_member, name: "y", scope: ![[SEVERAL]], file: !{{[0-9]+}}, line: {{[0-9]+}}, baseType: !{{[0-9]+}}, size: 4, offset: 32, flags: DIFlagBitField, extraData: i64 32) + // CHECK-DAG: ![[SEVERAL_SECOND_ZERO]] = !DIDerivedType(tag: DW_TAG_member, scope: ![[SEVERAL]], file: !{{[0-9]+}}, line: {{[0-9]+}}, baseType: !{{[0-9]+}}, offset: 64, flags: DIFlagBitField, extraData: i64 64) + // CHECK-DAG: ![[SEVERAL_Z]] = !DIDerivedType(tag: DW_TAG_member, name: "z", scope: ![[SEVERAL]], file: !{{[0-9]+}}, line: {{[0-9]+}}, baseType: !{{[0-9]+}}, size: 4, offset: 64, flags: DIFlagBitField, extraData: i64 64) + int x : 4; + int : 0; + int y : 4; + int : 0; + int z : 4; +}; + +struct None { + // CHECK-DAG: ![[NONE:[0-9]+]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "None", file: !{{[0-9]+}}, line: {{[0-9]+}}, size: 64, elements: ![[NONE_ELEMENTS:[0-9]+]]) + // CHECK-DAG: ![[NONE_ELEMENTS]] = !{![[NONE_FIELD:[0-9]+]], ![[NONE_X:[0-9]+]]} + // CHECK-DAG: ![[NONE_FIELD]] = !DIDerivedType(tag: DW_TAG_member, name: "field", scope: ![[NONE]], file: !{{[0-9]+}}, line: {{[0-9]+}}, baseType: !{{[0-9]+}}, size: 32) + // CHECK-DAG: ![[NONE_X]] = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: ![[NONE]], file: !{{[0-9]+}}, line: {{[0-9]+}}, baseType: !{{[0-9]+}}, size: 4, offset: 32, flags: DIFlagBitField, extraData: i64 32) + int : 0; + int field; + int x : 4; +}; + +struct Mixed { + // CHECK-DAG: ![[MIXED:[0-9]+]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Mixed", file: !{{[0-9]+}}, line: {{[0-9]+}}, size: 64, elements: ![[MIXED_ELEMENTS:[0-9]+]]) + // CHECK-DAG: ![[MIXED_ELEMENTS]] = !{![[MIXED_FIELD:[0-9]+]], ![[MIXED_ZERO:[0-9]+]], ![[MIXED_X:[0-9]+]], ![[MIXED_Y:[0-9]+]]} + // CHECK-DAG: ![[MIXED_FIELD]] = !DIDerivedType(tag: DW_TAG_member, name: "field", scope: ![[MIXED]], file: !{{[0-9]+}}, line: {{[0-9]+}}, baseType: !{{[0-9]+}}, size: 32) + // CHECK-DAG: ![[MIXED_ZERO]] = !DIDerivedType(tag: DW_TAG_member, scope: ![[MIXED]], file: !{{[0-9]+}}, line: {{[0-9]+}}, baseType: !{{[0-9]+}}, offset: 32, flags: DIFlagBitField, extraData: i64 32) + // CHECK-DAG: ![[MIXED_X]] = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: ![[MIXED]], file: !{{[0-9]+}}, line: {{[0-9]+}}, baseType: !{{[0-9]+}}, size: 4, offset: 32, flags: DIFlagBitField, extraData: i64 32) + // CHECK-DAG: ![[MIXED_Y]] = !DIDerivedType(tag: DW_TAG_member, name: "y", scope: ![[MIXED]], file: !{{[0-9]+}}, line: {{[0-9]+}}, baseType: !{{[0-9]+}}, size: 4, offset: 36, flags: DIFlagBitField, extraData: i64 32) + int field; + int : 0; + int x : 4; + int y : 4; +}; + +void foo(struct First f, struct FirstDuplicate fs, struct Second s, struct SecondDuplicate sd, struct Last l, struct Several ss, struct None n, struct Mixed m) { + return; +}