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,10 +321,17 @@ RD); } - /// Create new bit field member. - llvm::DIType *createBitFieldType(const FieldDecl *BitFieldDecl, - llvm::DIScope *RecordTy, - const RecordDecl *RD); + /// Create new bit field member + llvm::DIDerivedType *createBitFieldType(const FieldDecl *BitFieldDecl, + llvm::DIScope *RecordTy, + const RecordDecl *RD); + + /// Create an anonnymous zero-size separator for bit-field-decl if needed on + /// the target + llvm::DIDerivedType * + createBitFieldSeparatorIfNeeded(const FieldDecl *BitFieldDecl, + const llvm::DIDerivedType *BitFieldDI, + const RecordDecl *RD); /// Helpers for collecting fields of a record. /// @{ 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 @@ -18,6 +18,7 @@ #include "CodeGenFunction.h" #include "CodeGenModule.h" #include "ConstantEmitter.h" +#include "TargetInfo.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" #include "clang/AST/DeclFriend.h" @@ -1483,9 +1484,9 @@ return F; } -llvm::DIType *CGDebugInfo::createBitFieldType(const FieldDecl *BitFieldDecl, - llvm::DIScope *RecordTy, - const RecordDecl *RD) { +llvm::DIDerivedType * +CGDebugInfo::createBitFieldType(const FieldDecl *BitFieldDecl, + llvm::DIScope *RecordTy, const RecordDecl *RD) { StringRef Name = BitFieldDecl->getName(); QualType Ty = BitFieldDecl->getType(); SourceLocation Loc = BitFieldDecl->getLocation(); @@ -1516,6 +1517,70 @@ Flags, DebugType, Annotations); } +llvm::DIDerivedType *CGDebugInfo::createBitFieldSeparatorIfNeeded( + const FieldDecl *BitFieldDecl, const llvm::DIDerivedType *BitFieldDI, + const RecordDecl *RD) { + + if (!CGM.getTargetCodeGenInfo().shouldEmitDWARFBitFieldSeparators()) + return nullptr; + + /* + Add a *single* zero-bitfield separator between two non-zero bitfields + separated by one or more zero-bitfields. This is used to distinguish between + structures such the ones below, where the memory layout is the same, but how + the ABI assigns fields to registers differs. + + struct foo { + int space[4]; + char a : 8; // on amdgpu, passed on v4 + char b : 8; + char x : 8; + char y : 8; + }; + struct bar { + int space[4]; + char a : 8; // on amdgpu, passed on v4 + char b : 8; + char : 0; + char x : 8; // passed on v5 + char y : 8; + }; + */ + + ASTContext &Context = CGM.getContext(); + + bool EmitSeparator = false; + RecordDecl::field_iterator FieldIt = RD->field_begin(); + for (int FieldIndex = BitFieldDecl->getFieldIndex(); FieldIndex - 1 > 0; + --FieldIndex, ++FieldIt) { + if (!FieldIt->isZeroLengthBitField(Context)) + EmitSeparator = FieldIt->isBitField(); + } + + if (!EmitSeparator) + return nullptr; + + const FieldDecl *ZeroBitField = *FieldIt; + QualType Ty = ZeroBitField->getType(); + SourceLocation Loc = ZeroBitField->getLocation(); + llvm::DIFile *VUnit = getOrCreateFile(Loc); + llvm::DIType *DebugType = getOrCreateType(Ty, VUnit); + llvm::DIScope *RecordTy = BitFieldDI->getScope(); + + llvm::DIFile *File = getOrCreateFile(Loc); + unsigned Line = getLineNumber(Loc); + + uint64_t StorageOffsetInBits = + cast(BitFieldDI->getStorageOffsetInBits()) + ->getZExtValue(); + + llvm::DINode::DIFlags Flags = getAccessFlag(ZeroBitField->getAccess(), RD); + llvm::DINodeArray Annotations = CollectBTFDeclTagAnnotations(ZeroBitField); + return DBuilder.createBitFieldMemberType( + RecordTy, "", File, Line, 0, StorageOffsetInBits, StorageOffsetInBits, + Flags, DebugType, Annotations); +} + llvm::DIType *CGDebugInfo::createFieldType( StringRef name, QualType type, SourceLocation loc, AccessSpecifier AS, uint64_t offsetInBits, uint32_t AlignInBits, llvm::DIFile *tunit, @@ -1624,7 +1689,11 @@ llvm::DIType *FieldType; if (field->isBitField()) { - FieldType = createBitFieldType(field, RecordTy, RD); + llvm::DIDerivedType *BitFieldType; + FieldType = BitFieldType = createBitFieldType(field, RecordTy, RD); + if (llvm::DIType *Separator = + createBitFieldSeparatorIfNeeded(field, BitFieldType, RD)) + elements.push_back(Separator); } else { auto Align = getDeclAlignIfRequired(field, CGM.getContext()); llvm::DINodeArray Annotations = CollectBTFDeclTagAnnotations(field); diff --git a/clang/lib/CodeGen/TargetInfo.h b/clang/lib/CodeGen/TargetInfo.h --- a/clang/lib/CodeGen/TargetInfo.h +++ b/clang/lib/CodeGen/TargetInfo.h @@ -349,6 +349,11 @@ /// as 'used', and having internal linkage. virtual bool shouldEmitStaticExternCAliases() const { return true; } + /// \return true if annonymous zero-sized bitfields should be emitted to + /// correctly distinguish between struct types whose memory layout is the + /// same, but whose layout may differ when used as argument passed by value + virtual bool shouldEmitDWARFBitFieldSeparators() const { return false; } + virtual void setCUDAKernelCallingConvention(const FunctionType *&FT) const {} /// Return the device-side type for the CUDA device builtin surface type. diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -9469,6 +9469,7 @@ llvm::Function *BlockInvokeFunc, llvm::Type *BlockTy) const override; bool shouldEmitStaticExternCAliases() const override; + bool shouldEmitDWARFBitFieldSeparators() const override; void setCUDAKernelCallingConvention(const FunctionType *&FT) const override; }; } @@ -9686,6 +9687,10 @@ return false; } +bool AMDGPUTargetCodeGenInfo::shouldEmitDWARFBitFieldSeparators() const { + return true; +} + void AMDGPUTargetCodeGenInfo::setCUDAKernelCallingConvention( const FunctionType *&FT) const { FT = getABIInfo().getContext().adjustFunctionType( 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,106 @@ +// RUN: %clang_cc1 -triple x86_64-unk-unk -o - -emit-llvm -debug-info-kind=limited %s | FileCheck --check-prefixes NOSEPARATOR,BOTH %s +// RUN: %clang_cc1 -triple amdgcn-unk-unk -o - -emit-llvm -debug-info-kind=limited %s | FileCheck --check-prefixes SEPARATOR,BOTH %s + +struct First { + // BOTH-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]+]]) + // BOTH-DAG: ![[FIRST_ELEMENTS]] = !{![[FIRST_X:[0-9]+]], ![[FIRST_Y:[0-9]+]]} + // BOTH-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) + // BOTH-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 { + // BOTH-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]+]]) + // BOTH-DAG: ![[FIRSTD_ELEMENTS]] = !{![[FIRSTD_X:[0-9]+]], ![[FIRSTD_Y:[0-9]+]]} + // BOTH-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) + // BOTH-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 { + // BOTH-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]+]]) + + // NOSEPARATOR-DAG: ![[SECOND_ELEMENTS]] = !{![[SECOND_X:[0-9]+]], ![[SECOND_Y:[0-9]+]]} + // SEPARATOR-DAG: ![[SECOND_ELEMENTS]] = !{![[SECOND_X:[0-9]+]], ![[SECOND_ZERO:[0-9]+]], ![[SECOND_Y:[0-9]+]]} + + // BOTH-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) + // SEPARATOR-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) + // BOTH-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 { + // BOTH-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]+]]) + + // NOSEPARATOR-DAG: ![[SECONDD_ELEMENTS]] = !{![[SECONDD_X:[0-9]+]], ![[SECONDD_Y:[0-9]+]]} + // SEPARATOR-DAG: ![[SECONDD_ELEMENTS]] = !{![[SECONDD_X:[0-9]+]], ![[SECONDD_ZERO:[0-9]+]], ![[SECONDD_Y:[0-9]+]]} + + // BOTH-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) + // SEPARATOR-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) + // BOTH-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 { + // BOTH-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]+]]) + // BOTH-DAG: ![[LAST_ELEMENTS]] = !{![[LAST_X:[0-9]+]], ![[LAST_Y:[0-9]+]]} + // BOTH-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) + // BOTH-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 { + // BOTH-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]+]]) + + // SEPARATOR-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]+]]} + // NOSEPARATOR-DAG: ![[SEVERAL_ELEMENTS]] = !{![[SEVERAL_X:[0-9]+]], ![[SEVERAL_Y:[0-9]+]], ![[SEVERAL_Z:[0-9]+]]} + + // BOTH-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) + // SEPARATOR-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) + // BOTH-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) + // SEPARATOR-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) + // BOTH-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 { + // BOTH-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]+]]) + // BOTH-DAG: ![[NONE_ELEMENTS]] = !{![[NONE_FIELD:[0-9]+]], ![[NONE_X:[0-9]+]]} + // BOTH-DAG: ![[NONE_FIELD]] = !DIDerivedType(tag: DW_TAG_member, name: "field", scope: ![[NONE]], file: !{{[0-9]+}}, line: {{[0-9]+}}, baseType: !{{[0-9]+}}, size: 32) + // BOTH-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 { + // BOTH-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]+]]) + // BOTH-DAG: ![[MIXED_ELEMENTS]] = !{![[MIXED_FIELD:[0-9]+]], ![[MIXED_X:[0-9]+]], ![[MIXED_Y:[0-9]+]]} + // BOTH-DAG: ![[MIXED_FIELD]] = !DIDerivedType(tag: DW_TAG_member, name: "field", scope: ![[MIXED]], file: !{{[0-9]+}}, line: {{[0-9]+}}, baseType: !{{[0-9]+}}, size: 32) + // BOTH-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) + // BOTH-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; +}