DebugInfo: Support debug_loc under fission

Implement debug_loc.dwo, as well as llvm-dwarfdump support for dumping
this section.

Outlined in the DWARF5 spec and http://gcc.gnu.org/wiki/DebugFission the
debug_loc.dwo section has more variation than the standard debug_loc,
allowing 3 different forms of entry (plus the end of list entry). GCC
seems to, and Clang certainly, only use one form, so I've just
implemented dumping support for that for now.

It wasn't immediately obvious that there was a good refactoring to share
the implementation of dumping support between debug_loc and
debug_loc.dwo, so they're separate for now - ideas welcome or I may come
back to it at some point.

As per a comment in the code, we could choose different forms that may
reduce the number of debug_addr entries we emit, but that will require
further study.

llvm-svn: 204697
diff --git a/llvm/lib/CodeGen/AsmPrinter/DIE.cpp b/llvm/lib/CodeGen/AsmPrinter/DIE.cpp
index eb3a622..34f070e 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DIE.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DIE.cpp
@@ -559,13 +559,15 @@
 /// EmitValue - Emit label value.
 ///
 void DIELocList::EmitValue(AsmPrinter *AP, dwarf::Form Form) const {
+  DwarfDebug *DD = AP->getDwarfDebug();
   MCSymbol *Label = AP->GetTempSymbol("debug_loc", Index);
-  MCSymbol *DwarfDebugLocSectionSym = AP->getDwarfDebug()->getDebugLocSym();
 
-  if (AP->MAI->doesDwarfUseRelocationsAcrossSections())
-    AP->EmitSectionOffset(Label, DwarfDebugLocSectionSym);
+  if (DD->useSplitDwarf())
+    AP->EmitLabelDifference(Label, DD->getDebugLocDWOSym(), 4);
+  else if (AP->MAI->doesDwarfUseRelocationsAcrossSections())
+    AP->EmitSectionOffset(Label, DD->getDebugLocSym());
   else
-    AP->EmitLabelDifference(Label, DwarfDebugLocSectionSym, 4);
+    AP->EmitLabelDifference(Label, DD->getDebugLocSym(), 4);
 }
 
 #ifndef NDEBUG
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
index 792a9c4..a9ed9db 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -184,7 +184,7 @@
 
   DwarfInfoSectionSym = DwarfAbbrevSectionSym = DwarfStrSectionSym = 0;
   DwarfDebugRangeSectionSym = DwarfDebugLocSectionSym = DwarfLineSectionSym = 0;
-  DwarfAddrSectionSym = 0;
+  DwarfAddrSectionSym = DwarfDebugLocDWOSectionSym = 0;
   DwarfAbbrevDWOSectionSym = DwarfStrDWOSectionSym = 0;
   FunctionBeginSym = FunctionEndSym = 0;
   CurFn = 0;
@@ -1865,7 +1865,6 @@
 
   DwarfLineSectionSym =
       emitSectionSym(Asm, TLOF.getDwarfLineSection(), "section_line");
-  emitSectionSym(Asm, TLOF.getDwarfLocSection());
   if (GenerateGnuPubSections) {
     DwarfGnuPubNamesSectionSym =
         emitSectionSym(Asm, TLOF.getDwarfGnuPubNamesSection());
@@ -1883,12 +1882,13 @@
         emitSectionSym(Asm, TLOF.getDwarfStrDWOSection(), "skel_string");
     DwarfAddrSectionSym =
         emitSectionSym(Asm, TLOF.getDwarfAddrSection(), "addr_sec");
-  }
+    DwarfDebugLocDWOSectionSym =
+        emitSectionSym(Asm, TLOF.getDwarfLocDWOSection(), "skel_loc");
+  } else
+    DwarfDebugLocSectionSym =
+        emitSectionSym(Asm, TLOF.getDwarfLocSection(), "section_debug_loc");
   DwarfDebugRangeSectionSym =
       emitSectionSym(Asm, TLOF.getDwarfRangesSection(), "debug_range");
-
-  DwarfDebugLocSectionSym =
-      emitSectionSym(Asm, TLOF.getDwarfLocSection(), "section_debug_loc");
 }
 
 // Recursively emits a debug information entry.
@@ -2379,7 +2379,8 @@
 void DwarfDebug::emitDebugLoc() {
   // Start the dwarf loc section.
   Asm->OutStreamer.SwitchSection(
-      Asm->getObjFileLowering().getDwarfLocSection());
+      useSplitDwarf() ? Asm->getObjFileLowering().getDwarfLocDWOSection()
+                      : Asm->getObjFileLowering().getDwarfLocSection());
   unsigned char Size = Asm->getDataLayout().getPointerSize();
   unsigned index = 0;
   for (const auto &DebugLoc : DotDebugLocEntries) {
@@ -2389,7 +2390,16 @@
       // compile unit. This is a hard coded 0 for low_pc when we're emitting
       // ranges, or the DW_AT_low_pc on the compile unit otherwise.
       const DwarfCompileUnit *CU = Entry.getCU();
-      if (CU->getRanges().size() == 1) {
+      if (useSplitDwarf()) {
+        // Just always use start_length for now - at least that's one address
+        // rather than two. We could get fancier and try to, say, reuse an
+        // address we know we've emitted elsewhere (the start of the function?
+        // The start of the CU or CU subrange that encloses this range?)
+        Asm->EmitInt8(dwarf::DW_LLE_start_length_entry);
+        unsigned idx = InfoHolder.getAddrPoolIndex(Entry.getBeginSym());
+        Asm->EmitULEB128(idx);
+        Asm->EmitLabelDifference(Entry.getEndSym(), Entry.getBeginSym(), 4);
+      } else if (CU->getRanges().size() == 1) {
         // Grab the begin symbol from the first range as our base.
         const MCSymbol *Base = CU->getRanges()[0].getStart();
         Asm->EmitLabelDifference(Entry.getBeginSym(), Base, Size);
@@ -2409,8 +2419,12 @@
       // Close the range.
       Asm->OutStreamer.EmitLabel(end);
     }
-    Asm->OutStreamer.EmitIntValue(0, Size);
-    Asm->OutStreamer.EmitIntValue(0, Size);
+    if (useSplitDwarf())
+      Asm->EmitInt8(dwarf::DW_LLE_end_of_list_entry);
+    else {
+      Asm->OutStreamer.EmitIntValue(0, Size);
+      Asm->OutStreamer.EmitIntValue(0, Size);
+    }
     ++index;
   }
 }
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h
index 064c0fa..0b0f0a8 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h
@@ -423,7 +423,7 @@
   MCSymbol *DwarfDebugLocSectionSym, *DwarfLineSectionSym, *DwarfAddrSectionSym;
   MCSymbol *FunctionBeginSym, *FunctionEndSym;
   MCSymbol *DwarfInfoDWOSectionSym, *DwarfAbbrevDWOSectionSym;
-  MCSymbol *DwarfStrDWOSectionSym;
+  MCSymbol *DwarfStrDWOSectionSym, *DwarfDebugLocDWOSectionSym;
   MCSymbol *DwarfGnuPubNamesSectionSym, *DwarfGnuPubTypesSectionSym;
 
   // As an optimization, there is no need to emit an entry in the directory
@@ -756,6 +756,9 @@
   /// Returns the section symbol for the .debug_loc section.
   MCSymbol *getDebugLocSym() const { return DwarfDebugLocSectionSym; }
 
+  /// Returns the section symbol for the .debug_loc section.
+  MCSymbol *getDebugLocDWOSym() const { return DwarfDebugLocDWOSectionSym; }
+
   /// Returns the previous section that was emitted into.
   const MCSection *getPrevSection() const { return PrevSection; }
 
diff --git a/llvm/lib/DebugInfo/DWARFContext.cpp b/llvm/lib/DebugInfo/DWARFContext.cpp
index 0afe6ef..60c5f6a 100644
--- a/llvm/lib/DebugInfo/DWARFContext.cpp
+++ b/llvm/lib/DebugInfo/DWARFContext.cpp
@@ -96,6 +96,11 @@
     getDebugLoc()->dump(OS);
   }
 
+  if (DumpType == DIDT_All || DumpType == DIDT_LocDwo) {
+    OS << "\n.debug_loc.dwo contents:\n";
+    getDebugLocDWO()->dump(OS);
+  }
+
   if (DumpType == DIDT_All || DumpType == DIDT_Frames) {
     OS << "\n.debug_frame contents:\n";
     getDebugFrame()->dump(OS);
@@ -237,6 +242,16 @@
   return Loc.get();
 }
 
+const DWARFDebugLocDWO *DWARFContext::getDebugLocDWO() {
+  if (LocDWO)
+    return LocDWO.get();
+
+  DataExtractor LocData(getLocDWOSection().Data, isLittleEndian(), 0);
+  LocDWO.reset(new DWARFDebugLocDWO());
+  LocDWO->parse(LocData);
+  return LocDWO.get();
+}
+
 const DWARFDebugAranges *DWARFContext::getDebugAranges() {
   if (Aranges)
     return Aranges.get();
@@ -648,6 +663,7 @@
             .Case("debug_gnu_pubtypes", &GnuPubTypesSection)
             .Case("debug_info.dwo", &InfoDWOSection.Data)
             .Case("debug_abbrev.dwo", &AbbrevDWOSection)
+            .Case("debug_loc.dwo", &LocDWOSection.Data)
             .Case("debug_line.dwo", &LineDWOSection.Data)
             .Case("debug_str.dwo", &StringDWOSection)
             .Case("debug_str_offsets.dwo", &StringOffsetDWOSection)
diff --git a/llvm/lib/DebugInfo/DWARFContext.h b/llvm/lib/DebugInfo/DWARFContext.h
index a3bae79..ad6841a 100644
--- a/llvm/lib/DebugInfo/DWARFContext.h
+++ b/llvm/lib/DebugInfo/DWARFContext.h
@@ -42,6 +42,7 @@
   CUVector DWOCUs;
   TUVector DWOTUs;
   std::unique_ptr<DWARFDebugAbbrev> AbbrevDWO;
+  std::unique_ptr<DWARFDebugLocDWO> LocDWO;
 
   DWARFContext(DWARFContext &) LLVM_DELETED_FUNCTION;
   DWARFContext &operator=(DWARFContext &) LLVM_DELETED_FUNCTION;
@@ -148,6 +149,9 @@
   /// Get a pointer to the parsed dwo abbreviations object.
   const DWARFDebugAbbrev *getDebugAbbrevDWO();
 
+  /// Get a pointer to the parsed DebugLoc object.
+  const DWARFDebugLocDWO *getDebugLocDWO();
+
   /// Get a pointer to the parsed DebugAranges object.
   const DWARFDebugAranges *getDebugAranges();
 
@@ -173,6 +177,7 @@
   virtual const TypeSectionMap &getTypesSections() = 0;
   virtual StringRef getAbbrevSection() = 0;
   virtual const Section &getLocSection() = 0;
+  virtual const Section &getLocDWOSection() = 0;
   virtual StringRef getARangeSection() = 0;
   virtual StringRef getDebugFrameSection() = 0;
   virtual const Section &getLineSection() = 0;
@@ -216,6 +221,7 @@
   TypeSectionMap TypesSections;
   StringRef AbbrevSection;
   Section LocSection;
+  Section LocDWOSection;
   StringRef ARangeSection;
   StringRef DebugFrameSection;
   Section LineSection;
@@ -246,6 +252,7 @@
   const TypeSectionMap &getTypesSections() override { return TypesSections; }
   StringRef getAbbrevSection() override { return AbbrevSection; }
   const Section &getLocSection() override { return LocSection; }
+  const Section &getLocDWOSection() override { return LocDWOSection; }
   StringRef getARangeSection() override { return ARangeSection; }
   StringRef getDebugFrameSection() override { return DebugFrameSection; }
   const Section &getLineSection() override { return LineSection; }
diff --git a/llvm/lib/DebugInfo/DWARFDebugLoc.cpp b/llvm/lib/DebugInfo/DWARFDebugLoc.cpp
index 5f0ff02..e4aa5dc 100644
--- a/llvm/lib/DebugInfo/DWARFDebugLoc.cpp
+++ b/llvm/lib/DebugInfo/DWARFDebugLoc.cpp
@@ -11,6 +11,7 @@
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/Format.h"
 #include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/Dwarf.h"
 
 using namespace llvm;
 
@@ -74,3 +75,54 @@
   if (data.isValidOffset(Offset))
     llvm::errs() << "error: failed to consume entire .debug_loc section\n";
 }
+
+void DWARFDebugLocDWO::parse(DataExtractor data) {
+  uint32_t Offset = 0;
+  while (data.isValidOffset(Offset)) {
+    Locations.resize(Locations.size() + 1);
+    LocationList &Loc = Locations.back();
+    Loc.Offset = Offset;
+    dwarf::LocationListEntry Kind;
+    while ((Kind = static_cast<dwarf::LocationListEntry>(
+                data.getU8(&Offset))) != dwarf::DW_LLE_end_of_list_entry) {
+
+      if (Kind != dwarf::DW_LLE_start_length_entry) {
+        llvm::errs() << "error: dumping support for LLE of kind " << (int)Kind
+                     << " not implemented\n";
+        return;
+      }
+
+      Entry E;
+
+      E.Start = data.getULEB128(&Offset);
+      E.Length = data.getU32(&Offset);
+
+      unsigned Bytes = data.getU16(&Offset);
+      // A single location description describing the location of the object...
+      StringRef str = data.getData().substr(Offset, Bytes);
+      Offset += Bytes;
+      E.Loc.resize(str.size());
+      std::copy(str.begin(), str.end(), E.Loc.begin());
+
+      Loc.Entries.push_back(std::move(E));
+    }
+  }
+}
+
+void DWARFDebugLocDWO::dump(raw_ostream &OS) const {
+  for (const LocationList &L : Locations) {
+    OS << format("0x%8.8x: ", L.Offset);
+    const unsigned Indent = 12;
+    for (const Entry &E : L.Entries) {
+      if (&E != L.Entries.begin())
+        OS.indent(Indent);
+      OS << "Beginning address index: " << E.Start << '\n';
+      OS.indent(Indent) << "                 Length: " << E.Length << '\n';
+      OS.indent(Indent) << "   Location description: ";
+      for (unsigned char Loc : E.Loc)
+        OS << format("%2.2x ", Loc);
+      OS << "\n\n";
+    }
+  }
+}
+
diff --git a/llvm/lib/DebugInfo/DWARFDebugLoc.h b/llvm/lib/DebugInfo/DWARFDebugLoc.h
index d31aaaa..663acbb4 100644
--- a/llvm/lib/DebugInfo/DWARFDebugLoc.h
+++ b/llvm/lib/DebugInfo/DWARFDebugLoc.h
@@ -55,6 +55,27 @@
   /// specified address size to interpret the address ranges.
   void parse(DataExtractor data, unsigned AddressSize);
 };
+
+class DWARFDebugLocDWO {
+  struct Entry {
+    uint64_t Start;
+    uint32_t Length;
+    SmallVector<unsigned char, 4> Loc;
+  };
+
+  struct LocationList {
+    unsigned Offset;
+    SmallVector<Entry, 2> Entries;
+  };
+
+  typedef SmallVector<LocationList, 4> LocationLists;
+
+  LocationLists Locations;
+
+public:
+  void parse(DataExtractor data);
+  void dump(raw_ostream &OS) const;
+};
 }
 
 #endif