[dsymutil] Don't mark forward declarations as canonical.

This patch completes the work done by Frederic Riss to addresses
dsymutil incorrectly considering forward declaration as canonical during
uniquing. This resulted in references to the forward declaration even
after the definition was encountered.

In addition to the test provided by Alexander Shaposhnikov in D29609, I
added another test to cover several scenarios that were mentioned in his
conversation with Fred. We now also check that uniquing still occurs
after the definition was encountered.

For more context please refer to D29609

Differential revision: https://reviews.llvm.org/D37127

llvm-svn: 312274
This commit is contained in:
Jonas Devlieghere 2017-08-31 20:22:31 +00:00
parent c2774a549b
commit d9063658c8
9 changed files with 323 additions and 17 deletions

View File

@ -0,0 +1,127 @@
/* Compile with:
for FILE in `seq 3`; do
clang -g -c X86/odr-fwd-declaration.cpp -DFILE$FILE -o Inputs/odr-fwd-declaration/$FILE.o
done
*/
// RUN: llvm-dsymutil -f -oso-prepend-path=%p/../Inputs/odr-fwd-declaration -y %p/dummy-debug-map.map -o - | llvm-dwarfdump -debug-dump=info - | FileCheck %s
#ifdef FILE1
# 1 "Header.h" 1
typedef struct S *Sptr;
typedef Sptr *Sptrptr;
# 3 "Source1.cpp" 2
void foo() {
Sptrptr ptr1 = 0;
}
// First we confirm that the typedefs reference the forward declaration of the
// struct S.
//
// CHECK: TAG_variable
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_name{{.*}} "ptr1"
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_type{{.*}}0x{{0*}}[[TYPEDEF1:[a-f0-9]*]]
//
// CHECK: [[TYPEDEF1]]:{{.*}}TAG_typedef
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_type{{.*}}0x{{0*}}[[PTR1:[a-f0-9]*]]
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_name{{.*}} "Sptrptr"
//
// CHECK: [[PTR1]]:{{.*}}TAG_pointer_type
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_type{{.*}}0x{{0*}}[[TYPEDEF2:[a-f0-9]*]]
//
// CHECK: [[TYPEDEF2]]:{{.*}}TAG_typedef
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_type{{.*}}0x{{0*}}[[PTR2:[a-f0-9]*]]
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_name{{.*}} "Sptr"
//
// CHECK: [[PTR2]]:{{.*}}TAG_pointer_type
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_type{{.*}}0x{{0*}}[[FWDSTRUCT:[a-f0-9]*]]
//
// CHECK: [[FWDSTRUCT]]:{{.*}}TAG_structure_type
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_name{{.*}} "S"
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_declaration
// CHECK-NOT AT_byte_size
#elif defined(FILE2)
# 1 "Header.h" 1
typedef struct S *Sptr;
typedef Sptr *Sptrptr;
# 3 "Source2.cpp" 2
struct S {
int field;
};
void bar() {
Sptrptr ptr2 = 0;
}
// Next we confirm that the typedefs reference the definition rather than the
// previous declaration of S.
//
// CHECK: TAG_variable
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_name{{.*}} "ptr2"
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_type{{.*}}0x{{0*}}[[TYPEDEF3:[a-f0-9]*]]
//
// CHECK: [[TYPEDEF3]]:{{.*}}TAG_typedef
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_type{{.*}}0x{{0*}}[[PTR3:[a-f0-9]*]]
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_name{{.*}} "Sptrptr"
//
// CHECK: [[PTR3]]:{{.*}}TAG_pointer_type
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_type{{.*}}0x{{0*}}[[TYPEDEF4:[a-f0-9]*]]
//
// CHECK: [[TYPEDEF4]]:{{.*}}TAG_typedef
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_type{{.*}}0x{{0*}}[[PTR4:[a-f0-9]*]]
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_name{{.*}} "Sptr"
//
// CHECK: [[PTR4]]:{{.*}}TAG_pointer_type
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_type{{.*}}0x{{0*}}[[STRUCT:[a-f0-9]*]]
//
// CHECK: [[STRUCT]]:{{.*}}TAG_structure_type
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_name{{.*}} "S"
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK-NOT: AT_declaration
// CHECK: AT_byte_size
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: TAG_member
#elif defined(FILE3)
# 1 "Header.h" 1
typedef struct S *Sptr;
typedef Sptr *Sptrptr;
# 3 "Source1.cpp" 2
void foo() {
Sptrptr ptr1 = 0;
}
// Finally we confirm that uniquing is not broken and the same typedef is
// referenced by ptr1.
//
// CHECK: TAG_variable
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_name{{.*}} "ptr1"
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_type{{.*}}0x{{0*}}[[TYPEDEF3]]
// CHECK-NOT: TAG_typedef
// CHECK-NOT: TAG_pointer
// CHECK-NOT: TAG_structure_type
#else
#error "You must define which file you generate"
#endif

View File

@ -0,0 +1,151 @@
/* Compile with:
for FILE in `seq 3`; do
clang -g -c X86/odr-fwd-declaration2.cpp -DFILE$FILE -o Inputs/odr-fwd-declaration2/$FILE.o
done
*/
// RUN: llvm-dsymutil -f -oso-prepend-path=%p/../Inputs/odr-fwd-declaration2 -y %p/dummy-debug-map.map -o - | llvm-dwarfdump -debug-dump=info - | FileCheck %s
#ifdef FILE1
# 1 "Header.h" 1
struct A {
struct B;
B *bPtr;
B &bRef;
int B::*bPtrToField;
};
# 3 "Source1.cpp" 2
void foo() {
A *ptr1 = 0;
}
// First we confirm that bPtr, bRef and bPtrToField reference the forward
// declaration of the struct B.
//
// CHECK: DW_TAG_member
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_name{{.*}} "bPtr"
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_type{{.*}}0x{{0*}}[[PTR1:[a-f0-9]*]]
//
// CHECK: [[STRUCT1:[a-f0-9]*]]:{{.*}}TAG_structure_type
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_name{{.*}} "B"
// CHECK-NOT: AT_byte_size
// CHECK: DW_AT_declaration
//
// CHECK: DW_TAG_member
// CHECK: AT_name{{.*}} "bRef"
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_type{{.*}}0x{{0*}}[[REF1:[a-f0-9]*]]
//
// CHECK: TAG_member
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_name{{.*}} "bPtrToField"
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_type{{.*}}0x{{0*}}[[PTRTOMEMBER1:[a-f0-9]*]]
//
// CHECK: [[PTR1]]:{{.*}}TAG_pointer_type
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_type{{.*}}0x{{0*}}[[STRUCT1]]
//
// CHECK: [[REF1]]:{{.*}}TAG_reference_type
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_type{{.*}}0x{{0*}}[[STRUCT1]]
//
// CHECK: [[PTRTOMEMBER1]]:{{.*}}TAG_ptr_to_member_type
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_containing_type{{.*}}0x{{0*}}[[STRUCT1]]
#elif defined(FILE2)
# 1 "Header.h" 1
struct A {
struct B;
B *bPtr;
B &bRef;
int B::*bPtrToField;
};
# 3 "Source2.cpp" 2
struct A::B {
int x;
};
void bar() {
A *ptr2 = 0;
}
// Next we confirm that bPtr, bRef and bPtrToField reference the definition of
// B, rather than its declaration.
//
// CHECK: [[STRUCTA:[a-f0-9]*]]:{{.*}}TAG_structure_type
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_name{{.*}} "A"
// CHECK-NOT: AT_byte_size
// CHECK: DW_AT_byte_size
//
// CHECK: DW_TAG_member
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_name{{.*}} "bPtr"
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_type{{.*}}0x{{0*}}[[PTR2:[a-f0-9]*]]
//
// CHECK: [[STRUCT2:[a-f0-9]*]]:{{.*}}TAG_structure_type
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_name{{.*}} "B"
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: DW_AT_byte_size
//
// CHECK: DW_TAG_member
// CHECK: AT_name{{.*}} "bRef"
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_type{{.*}}0x{{0*}}[[REF2:[a-f0-9]*]]
//
// CHECK: TAG_member
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_name{{.*}} "bPtrToField"
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_type{{.*}}0x{{0*}}[[PTRTOMEMBER2:[a-f0-9]*]]
//
// CHECK: [[PTR2]]:{{.*}}TAG_pointer_type
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_type{{.*}}0x{{0*}}[[STRUCT2]]
//
// CHECK: [[REF2]]:{{.*}}TAG_reference_type
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_type{{.*}}0x{{0*}}[[STRUCT2]]
//
// CHECK: [[PTRTOMEMBER2]]:{{.*}}TAG_ptr_to_member_type
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_containing_type{{.*}}0x{{0*}}[[STRUCT2:[a-f0-9]*]]
#elif defined(FILE3)
# 1 "Header.h" 1
struct A {
struct B;
B *bPtr;
B &bRef;
int B::*bPtrToField;
};
# 3 "Source2.cpp" 2
struct A::B {
int x;
};
void bar() {
A *ptr2 = 0;
}
// Finally we confirm that uniquing isn't broken by checking that further
// references to 'struct A' point to its now complete definition.
//
// CHECK: TAG_variable
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_name{{.*}} "ptr2"
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_type{{.*}}0x{{0*}}[[PTR3:[a-f0-9]*]]
//
// CHECK: [[PTR3]]:{{.*}}TAG_pointer_type
// CHECK-NOT: {{DW_TAG|NULL}}
// CHECK: AT_type{{.*}}0x{{0*}}[[STRUCTA]]
#else
#error "You must define which file you generate"
#endif

View File

@ -185,13 +185,14 @@ class CompileUnit {
public:
/// Information gathered about a DIE in the object file.
struct DIEInfo {
int64_t AddrAdjust; ///< Address offset to apply to the described entity.
DeclContext *Ctxt; ///< ODR Declaration context.
DIE *Clone; ///< Cloned version of that DIE.
uint32_t ParentIdx; ///< The index of this DIE's parent.
bool Keep : 1; ///< Is the DIE part of the linked output?
bool InDebugMap : 1;///< Was this DIE's entity found in the map?
bool Prune : 1; ///< Is this a pure forward declaration we can strip?
int64_t AddrAdjust; ///< Address offset to apply to the described entity.
DeclContext *Ctxt; ///< ODR Declaration context.
DIE *Clone; ///< Cloned version of that DIE.
uint32_t ParentIdx; ///< The index of this DIE's parent.
bool Keep : 1; ///< Is the DIE part of the linked output?
bool InDebugMap : 1; ///< Was this DIE's entity found in the map?
bool Prune : 1; ///< Is this a pure forward declaration we can strip?
bool Incomplete : 1; ///< Does DIE transitively refer an incomplete decl?
};
CompileUnit(DWARFUnit &OrigUnit, unsigned ID, bool CanUseODR,
@ -1198,8 +1199,9 @@ private:
/// @{
/// Recursively walk the \p DIE tree and look for DIEs to
/// keep. Store that information in \p CU's DIEInfo.
void lookForDIEsToKeep(RelocationManager &RelocMgr,
const DWARFDie &DIE,
///
/// The return value indicates whether the DIE is incomplete.
bool lookForDIEsToKeep(RelocationManager &RelocMgr, const DWARFDie &DIE,
const DebugMapObject &DMO, CompileUnit &CU,
unsigned Flags);
@ -2196,6 +2198,11 @@ void DwarfLinker::keepDIEAndDependencies(RelocationManager &RelocMgr,
DWARFUnit &Unit = CU.getOrigUnit();
MyInfo.Keep = true;
// We're looking for incomplete types.
MyInfo.Incomplete = Die.getTag() != dwarf::DW_TAG_subprogram &&
Die.getTag() != dwarf::DW_TAG_member &&
dwarf::toUnsigned(Die.find(dwarf::DW_AT_declaration), 0);
// First mark all the parent chain as kept.
unsigned AncestorIdx = MyInfo.ParentIdx;
while (!CU.getInfo(AncestorIdx).Keep) {
@ -2211,7 +2218,7 @@ void DwarfLinker::keepDIEAndDependencies(RelocationManager &RelocMgr,
const auto *Abbrev = Die.getAbbreviationDeclarationPtr();
uint32_t Offset = Die.getOffset() + getULEB128Size(Abbrev->getCode());
// Mark all DIEs referenced through atttributes as kept.
// Mark all DIEs referenced through attributes as kept.
for (const auto &AttrSpec : Abbrev->attributes()) {
DWARFFormValue Val(AttrSpec.Form);
@ -2251,6 +2258,16 @@ void DwarfLinker::keepDIEAndDependencies(RelocationManager &RelocMgr,
unsigned ODRFlag = UseODR ? TF_ODR : 0;
lookForDIEsToKeep(RelocMgr, RefDie, DMO, *ReferencedCU,
TF_Keep | TF_DependencyWalk | ODRFlag);
// The incomplete property is propagated if the current DIE is complete
// but references an incomplete DIE.
if (Info.Incomplete && !MyInfo.Incomplete &&
(Die.getTag() == dwarf::DW_TAG_typedef ||
Die.getTag() == dwarf::DW_TAG_member ||
Die.getTag() == dwarf::DW_TAG_reference_type ||
Die.getTag() == dwarf::DW_TAG_ptr_to_member_type ||
Die.getTag() == dwarf::DW_TAG_pointer_type))
MyInfo.Incomplete = true;
}
}
}
@ -2267,7 +2284,9 @@ void DwarfLinker::keepDIEAndDependencies(RelocationManager &RelocMgr,
/// also called, but during these dependency walks the file order is
/// not respected. The TF_DependencyWalk flag tells us which kind of
/// traversal we are currently doing.
void DwarfLinker::lookForDIEsToKeep(RelocationManager &RelocMgr,
///
/// The return value indicates whether the DIE is incomplete.
bool DwarfLinker::lookForDIEsToKeep(RelocationManager &RelocMgr,
const DWARFDie &Die,
const DebugMapObject &DMO, CompileUnit &CU,
unsigned Flags) {
@ -2275,13 +2294,13 @@ void DwarfLinker::lookForDIEsToKeep(RelocationManager &RelocMgr,
CompileUnit::DIEInfo &MyInfo = CU.getInfo(Idx);
bool AlreadyKept = MyInfo.Keep;
if (MyInfo.Prune)
return;
return true;
// If the Keep flag is set, we are marking a required DIE's
// dependencies. If our target is already marked as kept, we're all
// set.
if ((Flags & TF_DependencyWalk) && AlreadyKept)
return;
return MyInfo.Incomplete;
// We must not call shouldKeepDIE while called from keepDIEAndDependencies,
// because it would screw up the relocation finding logic.
@ -2303,10 +2322,19 @@ void DwarfLinker::lookForDIEsToKeep(RelocationManager &RelocMgr,
Flags &= ~TF_ParentWalk;
if (!Die.hasChildren() || (Flags & TF_ParentWalk))
return;
return MyInfo.Incomplete;
for (auto Child: Die.children())
lookForDIEsToKeep(RelocMgr, Child, DMO, CU, Flags);
bool Incomplete = false;
for (auto Child : Die.children()) {
Incomplete |= lookForDIEsToKeep(RelocMgr, Child, DMO, CU, Flags);
// If any of the members are incomplete we propagate the incompleteness.
if (!MyInfo.Incomplete && Incomplete &&
(Die.getTag() == dwarf::DW_TAG_structure_type ||
Die.getTag() == dwarf::DW_TAG_class_type))
MyInfo.Incomplete = true;
}
return MyInfo.Incomplete;
}
/// Assign an abbreviation numer to \p Abbrev.
@ -2716,7 +2744,7 @@ DIE *DwarfLinker::DIECloner::cloneDIE(
assert(Die->getTag() == InputDIE.getTag());
Die->setOffset(OutOffset);
if ((Unit.hasODR() || Unit.isClangModule()) &&
if ((Unit.hasODR() || Unit.isClangModule()) && !Info.Incomplete &&
Die->getTag() != dwarf::DW_TAG_namespace && Info.Ctxt &&
Info.Ctxt != Unit.getInfo(Info.ParentIdx).Ctxt &&
!Info.Ctxt->getCanonicalDIEOffset()) {