DebugInfo: Partial implementation of DWARF type units.
authorDavid Blaikie <dblaikie@gmail.com>
Tue, 19 Nov 2013 23:08:21 +0000 (23:08 +0000)
committerDavid Blaikie <dblaikie@gmail.com>
Tue, 19 Nov 2013 23:08:21 +0000 (23:08 +0000)
Emit DW_TAG_type_units into the debug_info section using compile unit
headers. This is bogus/unusable by debuggers, but testable and provides
more isolated review.

Subsequent patches will include support for type unit headers and
emission into the debug_types section, as well as comdat grouping the
types based on their hash. Also the CompileUnit type will be renamed
'Unit' and relevant portions pulled out into respective CompileUnit and
TypeUnit types.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@195166 91177308-0d34-0410-b5e6-96231b3b80d8

lib/CodeGen/AsmPrinter/DIE.cpp
lib/CodeGen/AsmPrinter/DIE.h
lib/CodeGen/AsmPrinter/DIEHash.cpp
lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
lib/CodeGen/AsmPrinter/DwarfDebug.cpp
lib/CodeGen/AsmPrinter/DwarfDebug.h
test/DebugInfo/X86/generate-odr-hash.ll

index 69444285f4ec4691429c388174260f784187f557..c6b765a5f0a4d257045e9f3da1031784b167a73f 100644 (file)
@@ -114,18 +114,19 @@ DIE::~DIE() {
 
 /// Climb up the parent chain to get the compile unit DIE to which this DIE
 /// belongs.
-const DIE *DIE::getCompileUnit() const {
-  const DIE *Cu = getCompileUnitOrNull();
+const DIE *DIE::getUnit() const {
+  const DIE *Cu = getUnitOrNull();
   assert(Cu && "We should not have orphaned DIEs.");
   return Cu;
 }
 
 /// Climb up the parent chain to get the compile unit DIE this DIE belongs
 /// to. Return NULL if DIE is not added to an owner yet.
-const DIE *DIE::getCompileUnitOrNull() const {
+const DIE *DIE::getUnitOrNull() const {
   const DIE *p = this;
   while (p) {
-    if (p->getTag() == dwarf::DW_TAG_compile_unit)
+    if (p->getTag() == dwarf::DW_TAG_compile_unit ||
+        p->getTag() == dwarf::DW_TAG_type_unit)
       return p;
     p = p->getParent();
   }
@@ -227,6 +228,7 @@ void DIEInteger::EmitValue(AsmPrinter *Asm, dwarf::Form Form) const {
   case dwarf::DW_FORM_ref4:  // Fall thru
   case dwarf::DW_FORM_data4: Size = 4; break;
   case dwarf::DW_FORM_ref8:  // Fall thru
+  case dwarf::DW_FORM_ref_sig8:  // Fall thru
   case dwarf::DW_FORM_data8: Size = 8; break;
   case dwarf::DW_FORM_GNU_str_index: Asm->EmitULEB128(Integer); return;
   case dwarf::DW_FORM_GNU_addr_index: Asm->EmitULEB128(Integer); return;
@@ -253,6 +255,7 @@ unsigned DIEInteger::SizeOf(AsmPrinter *AP, dwarf::Form Form) const {
   case dwarf::DW_FORM_ref4:  // Fall thru
   case dwarf::DW_FORM_data4: return sizeof(int32_t);
   case dwarf::DW_FORM_ref8:  // Fall thru
+  case dwarf::DW_FORM_ref_sig8:  // Fall thru
   case dwarf::DW_FORM_data8: return sizeof(int64_t);
   case dwarf::DW_FORM_GNU_str_index: return MCAsmInfo::getULEB128Size(Integer);
   case dwarf::DW_FORM_GNU_addr_index: return MCAsmInfo::getULEB128Size(Integer);
index 28a2e8e38a012a75f5de9e1b40be796b6490711b..0574a98536bbf717fe82f4b7cb1488b932c64b07 100644 (file)
@@ -146,12 +146,12 @@ namespace llvm {
     const std::vector<DIE *> &getChildren() const { return Children; }
     const SmallVectorImpl<DIEValue*> &getValues() const { return Values; }
     DIE *getParent() const { return Parent; }
-    /// Climb up the parent chain to get the compile unit DIE this DIE belongs
-    /// to.
-    const DIE *getCompileUnit() const;
-    /// Similar to getCompileUnit, returns null when DIE is not added to an
+    /// Climb up the parent chain to get the compile or type unit DIE this DIE
+    /// belongs to.
+    const DIE *getUnit() const;
+    /// Similar to getUnit, returns null when DIE is not added to an
     /// owner yet.
-    const DIE *getCompileUnitOrNull() const;
+    const DIE *getUnitOrNull() const;
     void setOffset(unsigned O) { Offset = O; }
     void setSize(unsigned S) { Size = S; }
 
index 95eca90ef04639bf99e8375a4d7b88f1656e7a06..d76af5380413ba1be52790d4480a198fbf129536 100644 (file)
@@ -92,10 +92,12 @@ void DIEHash::addParentContext(const DIE &Parent) {
   // outermost such construct...
   SmallVector<const DIE *, 1> Parents;
   const DIE *Cur = &Parent;
-  while (Cur->getTag() != dwarf::DW_TAG_compile_unit) {
+  while (Cur->getParent()) {
     Parents.push_back(Cur);
     Cur = Cur->getParent();
   }
+  assert(Cur->getTag() == dwarf::DW_TAG_compile_unit ||
+         Cur->getTag() == dwarf::DW_TAG_type_unit);
 
   // Reverse iterate over our list to go from the outermost construct to the
   // innermost.
index fbd7ce822830953a943e892c702589c42128e255..2489002f7fde3cd2039d126f3ac43894033e6faa 100644 (file)
 #include "llvm/Target/TargetMachine.h"
 #include "llvm/Target/TargetLoweringObjectFile.h"
 #include "llvm/Target/TargetRegisterInfo.h"
+#include "llvm/Support/CommandLine.h"
 
 using namespace llvm;
 
+static cl::opt<bool> GenerateTypeUnits("generate-type-units", cl::Hidden,
+                                       cl::desc("Generate DWARF4 type units."),
+                                       cl::init(false));
+
 /// CompileUnit - Compile unit constructor.
 CompileUnit::CompileUnit(unsigned UID, DIE *D, DICompileUnit Node,
                          AsmPrinter *A, DwarfDebug *DW, DwarfUnits *DWU)
     : UniqueID(UID), Node(Node), CUDie(D), Asm(A), DD(DW), DU(DWU),
-      IndexTyDie(0), DebugInfoOffset(0) {
+      IndexTyDie(0), Language(Node.getLanguage()) {
   DIEIntegerOne = new (DIEValueAllocator) DIEInteger(1);
   insertDIE(Node, D);
 }
 
+CompileUnit::CompileUnit(unsigned UID, DIE *D, uint16_t Language, AsmPrinter *A,
+                         DwarfDebug *DD, DwarfUnits *DU)
+    : UniqueID(UID), Node(NULL), CUDie(D), Asm(A), DD(DD), DU(DU),
+      IndexTyDie(0), Language(Language) {
+  DIEIntegerOne = new (DIEValueAllocator) DIEInteger(1);
+}
+
 /// ~CompileUnit - Destructor for compile unit.
 CompileUnit::~CompileUnit() {
   for (unsigned j = 0, M = DIEBlocks.size(); j < M; ++j)
@@ -102,7 +114,9 @@ int64_t CompileUnit::getDefaultLowerBound() const {
 static bool isShareableAcrossCUs(DIDescriptor D) {
   // When the MDNode can be part of the type system, the DIE can be
   // shared across CUs.
-  return D.isType() || (D.isSubprogram() && !DISubprogram(D).isDefinition());
+  return (D.isType() ||
+          (D.isSubprogram() && !DISubprogram(D).isDefinition())) &&
+         !GenerateTypeUnits;
 }
 
 /// getDIE - Returns the debug information entry map slot for the
@@ -281,8 +295,8 @@ void CompileUnit::addDIEEntry(DIE *Die, dwarf::Attribute Attribute,
 
 void CompileUnit::addDIEEntry(DIE *Die, dwarf::Attribute Attribute,
                               DIEEntry *Entry) {
-  const DIE *DieCU = Die->getCompileUnitOrNull();
-  const DIE *EntryCU = Entry->getEntry()->getCompileUnitOrNull();
+  const DIE *DieCU = Die->getUnitOrNull();
+  const DIE *EntryCU = Entry->getEntry()->getUnitOrNull();
   if (!DieCU)
     // We assume that Die belongs to this CU, if it is not linked to any CU yet.
     DieCU = getCUDie();
@@ -871,6 +885,22 @@ DIE *CompileUnit::getOrCreateContextDIE(DIScope Context) {
   return getDIE(Context);
 }
 
+DIE *CompileUnit::createTypeDIE(DICompositeType Ty) {
+  DIE *ContextDIE = getOrCreateContextDIE(resolve(Ty.getContext()));
+
+  DIE *TyDIE = getDIE(Ty);
+  if (TyDIE)
+    return TyDIE;
+
+  // Create new type.
+  TyDIE = createAndAddDIE(Ty.getTag(), *ContextDIE, Ty);
+
+  constructTypeDIEImpl(*TyDIE, Ty);
+
+  updateAcceleratorTables(Ty, TyDIE);
+  return TyDIE;
+}
+
 /// getOrCreateTypeDIE - Find existing DIE or create new DIE for the
 /// given DIType.
 DIE *CompileUnit::getOrCreateTypeDIE(const MDNode *TyNode) {
@@ -1112,6 +1142,9 @@ static bool isTypeUnitScoped(DIType Ty, const DwarfDebug *DD) {
 
 /// Return true if the type should be split out into a type unit.
 static bool shouldCreateTypeUnit(DICompositeType CTy, const DwarfDebug *DD) {
+  if (!GenerateTypeUnits)
+    return false;
+
   uint16_t Tag = CTy.getTag();
 
   switch (Tag) {
@@ -1130,7 +1163,16 @@ static bool shouldCreateTypeUnit(DICompositeType CTy, const DwarfDebug *DD) {
 
 /// constructTypeDIE - Construct type DIE from DICompositeType.
 void CompileUnit::constructTypeDIE(DIE &Buffer, DICompositeType CTy) {
-  // Get core information.
+  // If this is a type applicable to a type unit it then add it to the
+  // list of types we'll compute a hash for later.
+  if (shouldCreateTypeUnit(CTy, DD))
+    DD->addTypeUnitType(&Buffer, CTy);
+  else
+    constructTypeDIEImpl(Buffer, CTy);
+}
+
+void CompileUnit::constructTypeDIEImpl(DIE &Buffer, DICompositeType CTy) {
+  // Add name if not anonymous or intermediate type.
   StringRef Name = CTy.getName();
 
   uint64_t Size = CTy.getSizeInBits() >> 3;
@@ -1296,10 +1338,6 @@ void CompileUnit::constructTypeDIE(DIE &Buffer, DICompositeType CTy) {
       addUInt(&Buffer, dwarf::DW_AT_APPLE_runtime_class, dwarf::DW_FORM_data1,
               RLang);
   }
-  // If this is a type applicable to a type unit it then add it to the
-  // list of types we'll compute a hash for later.
-  if (shouldCreateTypeUnit(CTy, DD))
-    DD->addTypeUnitType(&Buffer);
 }
 
 /// constructTemplateTypeParameterDIE - Construct new DIE for the given
@@ -1510,7 +1548,6 @@ static const ConstantExpr *getMergedGlobalExpr(const Value *V) {
 
 /// createGlobalVariableDIE - create global variable DIE.
 void CompileUnit::createGlobalVariableDIE(DIGlobalVariable GV) {
-
   // Check for pre-existence.
   if (getDIE(GV))
     return;
index 02f4206e94998a1c8f803d504d424a109c52bbcc..3a0c6be0acf51b6ca62298a60e2a54f43be3beda 100644 (file)
@@ -93,14 +93,18 @@ class CompileUnit {
   // DIEIntegerOne - A preallocated DIEValue because 1 is used frequently.
   DIEInteger *DIEIntegerOne;
 
+  uint16_t Language;
+
 public:
   CompileUnit(unsigned UID, DIE *D, DICompileUnit CU, AsmPrinter *A,
               DwarfDebug *DW, DwarfUnits *DWU);
+  CompileUnit(unsigned UID, DIE *D, uint16_t Language, AsmPrinter *A,
+              DwarfDebug *DW, DwarfUnits *DWU);
   ~CompileUnit();
 
   // Accessors.
   unsigned getUniqueID() const { return UniqueID; }
-  uint16_t getLanguage() const { return Node.getLanguage(); }
+  uint16_t getLanguage() const { return Language; }
   DICompileUnit getNode() const { return Node; }
   DIE *getCUDie() const { return CUDie.get(); }
   const StringMap<DIE *> &getGlobalNames() const { return GlobalNames; }
@@ -310,6 +314,9 @@ public:
   /// given DIType.
   DIE *getOrCreateTypeDIE(const MDNode *N);
 
+  /// getOrCreateContextDIE - Get context owner's DIE.
+  DIE *createTypeDIE(DICompositeType Ty);
+
   /// getOrCreateContextDIE - Get context owner's DIE.
   DIE *getOrCreateContextDIE(DIScope Context);
 
@@ -328,6 +335,10 @@ public:
   DIE *createAndAddDIE(unsigned Tag, DIE &Parent,
                        DIDescriptor N = DIDescriptor());
 
+  /// constructTypeDIEImpl - Construct type DIE that is not a type unit
+  /// reference from a DICompositeType.
+  void constructTypeDIEImpl(DIE &Buffer, DICompositeType CTy);
+
   /// Compute the size of a header for this unit, not including the initial
   /// length field.
   unsigned getHeaderSize() const {
index 35f76047dba6e80fdf40231db09086f1ef4404d8..dca2196214fe77c882ae6d139a6e868b58859ce2 100644 (file)
@@ -1051,22 +1051,6 @@ void DwarfDebug::finalizeModuleInfo() {
   // Attach DW_AT_inline attribute with inlined subprogram DIEs.
   computeInlinedDIEs();
 
-  // Split out type units and conditionally add an ODR tag to the split
-  // out type.
-  // FIXME: Do type splitting.
-  for (unsigned i = 0, e = TypeUnits.size(); i != e; ++i) {
-    DIE *Die = TypeUnits[i];
-    DIEHash Hash;
-    // If we've requested ODR hashes and it's applicable for an ODR hash then
-    // add the ODR signature now.
-    // FIXME: This should be added onto the type unit, not the type, but this
-    // works as an intermediate stage.
-    if (GenerateODRHash && shouldAddODRHash(CUMap.begin()->second, Die))
-      CUMap.begin()->second->addUInt(Die, dwarf::DW_AT_GNU_odr_signature,
-                                     dwarf::DW_FORM_data8,
-                                     Hash.computeDIEODRSignature(*Die));
-  }
-
   // Handle anything that needs to be done on a per-cu basis.
   for (DenseMap<const MDNode *, CompileUnit *>::iterator CUI = CUMap.begin(),
                                                          CUE = CUMap.end();
@@ -2071,7 +2055,7 @@ void DwarfDebug::emitDIE(DIE *Die, ArrayRef<DIEAbbrev *> Abbrevs) {
         // For DW_FORM_ref_addr, output the offset from beginning of debug info
         // section. Origin->getOffset() returns the offset from start of the
         // compile unit.
-        CompileUnit *CU = CUDieMap.lookup(Origin->getCompileUnit());
+        CompileUnit *CU = CUDieMap.lookup(Origin->getUnit());
         assert(CU && "CUDie should belong to a CU.");
         Addr += CU->getDebugInfoOffset();
         if (Asm->MAI->doesDwarfUseRelocationsAcrossSections())
@@ -2083,7 +2067,7 @@ void DwarfDebug::emitDIE(DIE *Die, ArrayRef<DIEAbbrev *> Abbrevs) {
                                          DIEEntry::getRefAddrSize(Asm));
       } else {
         // Make sure Origin belong to the same CU.
-        assert(Die->getCompileUnit() == Origin->getCompileUnit() &&
+        assert(Die->getUnit() == Origin->getUnit() &&
                "The referenced DIE should belong to the same CU in ref4");
         Asm->EmitInt32(Addr);
       }
@@ -3063,3 +3047,57 @@ void DwarfDebug::emitDebugStrDWO() {
   InfoHolder.emitStrings(Asm->getObjFileLowering().getDwarfStrDWOSection(),
                          OffSec, StrSym);
 }
+
+void DwarfDebug::addTypeUnitType(DIE *RefDie, DICompositeType CTy) {
+  DenseMap<const MDNode*, std::pair<uint64_t, SmallVectorImpl<DIE*>* > >::iterator I = TypeUnits.find(CTy);
+  SmallVector<DIE *, 8> References;
+  References.push_back(RefDie);
+  if (I != TypeUnits.end()) {
+    if (I->second.second) {
+      I->second.second->push_back(RefDie);
+      return;
+    }
+  } else {
+    DIE *UnitDie = new DIE(dwarf::DW_TAG_type_unit);
+    CompileUnit *NewCU =
+        new CompileUnit(GlobalCUIndexCount++, UnitDie,
+                        dwarf::DW_LANG_C_plus_plus, Asm, this, &InfoHolder);
+    CUDieMap.insert(std::make_pair(UnitDie, NewCU));
+    NewCU->addUInt(UnitDie, dwarf::DW_AT_language, dwarf::DW_FORM_data2,
+                   dwarf::DW_LANG_C_plus_plus);
+
+    // Register the type in the TypeUnits map with a vector of references to be
+    // populated whenever a reference is required.
+    I = TypeUnits.insert(std::make_pair(CTy, std::make_pair(0, &References)))
+            .first;
+
+    // Construct the type, this may, recursively, require more type units that
+    // may in turn require this type again - in which case they will add DIEs to
+    // the References vector.
+    DIE *Die = NewCU->createTypeDIE(CTy);
+
+    if (GenerateODRHash && shouldAddODRHash(NewCU, Die))
+      NewCU->addUInt(UnitDie, dwarf::DW_AT_GNU_odr_signature,
+                     dwarf::DW_FORM_data8,
+                     DIEHash().computeDIEODRSignature(*Die));
+    // FIXME: This won't handle circularly referential structures, as the DIE
+    // may have references to other DIEs still under construction and missing
+    // their signature. Hashing should walk through the signatures to their
+    // referenced type, or possibly walk the precomputed hashes of related types
+    // at the end.
+    uint64_t Signature = DIEHash().computeTypeSignature(*Die);
+
+    // Remove the References vector and add the type hash.
+    I->second.first = Signature;
+    I->second.second = NULL;
+
+
+    InfoHolder.addUnit(NewCU);
+  }
+
+  // Populate all the signatures.
+  for (unsigned i = 0, e = References.size(); i != e; ++i) {
+    CUMap.begin()->second->addUInt(References[i], dwarf::DW_AT_signature,
+                                   dwarf::DW_FORM_ref_sig8, I->second.first);
+  }
+}
index cebac39a19b0383b176adc057d504dfb4165f9f2..c3b1299731774a37064e3ab6cfd4f29d8e8c8212 100644 (file)
@@ -37,6 +37,7 @@ class MachineFrameInfo;
 class MachineModuleInfo;
 class MachineOperand;
 class MCAsmInfo;
+class MCObjectFileInfo;
 class DIEAbbrev;
 class DIE;
 class DIEBlock;
@@ -443,7 +444,7 @@ class DwarfDebug {
   ImportedEntityMap ScopesWithImportedEntities;
 
   // Holder for types that are going to be extracted out into a type unit.
-  std::vector<DIE *> TypeUnits;
+  DenseMap<const MDNode *, std::pair<uint64_t, SmallVectorImpl<DIE*>* > > TypeUnits;
 
   // Whether to emit the pubnames/pubtypes sections.
   bool HasDwarfPubSections;
@@ -695,7 +696,7 @@ public:
 
   /// \brief Add a DIE to the set of types that we're going to pull into
   /// type units.
-  void addTypeUnitType(DIE *Die) { TypeUnits.push_back(Die); }
+  void addTypeUnitType(DIE *Die, DICompositeType CTy);
 
   /// \brief Add a label so that arange data can be generated for it.
   void addArangeLabel(SymbolCU SCU) { ArangeLabels.push_back(SCU); }
index c3075f2f0d85383d83484e732da66c3a3378892e..853d4a2047e11d4720ac0c55e90abae2ee52ba45 100644 (file)
@@ -1,6 +1,6 @@
 ; REQUIRES: object-emission
 
-; RUN: llc %s -o %t -filetype=obj -O0 -generate-odr-hash -mtriple=x86_64-unknown-linux-gnu
+; RUN: llc %s -o %t -filetype=obj -O0 -generate-type-units -generate-odr-hash -mtriple=x86_64-unknown-linux-gnu
 ; RUN: llvm-dwarfdump -debug-dump=info %t | FileCheck %s
 ;
 ; Generated from:
 ; wombat wom;
 
 ; Check that we generate a hash for bar and the value.
+; CHECK-LABEL: DW_AT_GNU_odr_signature [DW_FORM_data8] (0x200520c0d5b90eff)
 ; CHECK: DW_TAG_structure_type
 ; CHECK-NEXT: debug_str{{.*}}"bar"
-; CHECK: DW_AT_GNU_odr_signature [DW_FORM_data8] (0x200520c0d5b90eff)
+
+; Check that we generate a hash for fluffy and the value.
+; CHECK-LABEL: DW_AT_GNU_odr_signature [DW_FORM_data8]   (0x9a0124d5a0c21c52)
 ; CHECK: DW_TAG_namespace
 ; CHECK-NEXT: debug_str{{.*}}"echidna"
 ; CHECK: DW_TAG_namespace
 ; CHECK-NEXT: debug_str{{.*}}"mongoose"
 ; CHECK: DW_TAG_class_type
 ; CHECK-NEXT: debug_str{{.*}}"fluffy"
-; CHECK: DW_AT_GNU_odr_signature [DW_FORM_data8]   (0x9a0124d5a0c21c52)
 
 ; We emit no hash for walrus since the type is contained in an anonymous
 ; namespace and won't violate any ODR-ness.
+; CHECK: DW_TAG_type_unit
+; CHECK-NOT: NULL
+; CHECK-NOT: DW_AT_GNU_odr_signature
 ; CHECK: DW_TAG_structure_type
 ; CHECK-NEXT: debug_str{{.*}}"walrus"
 ; CHECK-NEXT: DW_AT_byte_size
 ; CHECK-NEXT: DW_AT_decl_file
 ; CHECK-NEXT: DW_AT_decl_line
-; CHECK-NOT: DW_AT_GNU_odr_signature
 ; CHECK: DW_TAG_subprogram
 
+
 ; Check that we generate a hash for wombat and the value, but not for the
 ; anonymous type contained within.
+; CHECK: DW_TAG_type_unit
 ; CHECK: DW_TAG_structure_type
-; CHECK-NEXT: debug_str{{.*}}wombat
-; CHECK: DW_AT_GNU_odr_signature [DW_FORM_data8] (0x685bcc220141e9d7)
+; The signature for the outer 'wombat' type - this can be FileChecked once the
+; type units are moved to their own section with the full type unit header
+; including the signature
+; CHECK: DW_AT_signature [DW_FORM_ref_sig8] (0x73776f130648b986)
 ; CHECK: DW_TAG_structure_type
-; CHECK-NEXT: DW_AT_byte_size
-; CHECK-NEXT: DW_AT_decl_file
-; CHECK-NEXT: DW_AT_decl_line
+; CHECK-NOT: DW_AT_name
+; CHECK-NOT: DW_AT_GNU_odr_signature
 ; CHECK: DW_TAG_member
 ; CHECK-NEXT: debug_str{{.*}}"a"
-
-; Check that we don't generate a hash for baz.
+; CHECK-LABEL: DW_AT_GNU_odr_signature [DW_FORM_data8] (0x685bcc220141e9d7)
 ; CHECK: DW_TAG_structure_type
-; CHECK-NEXT: debug_str{{.*}}"baz"
-; CHECK-NOT: DW_AT_GNU_odr_signature
+; CHECK-NEXT: debug_str{{.*}}"wombat"
 
 %struct.bar = type { i8 }
 %"class.echidna::capybara::mongoose::fluffy" = type { i32, i32 }