• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===- DWARFAcceleratorTable.h ----------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #ifndef LLVM_DEBUGINFO_DWARFACCELERATORTABLE_H
11 #define LLVM_DEBUGINFO_DWARFACCELERATORTABLE_H
12 
13 #include "llvm/ADT/DenseSet.h"
14 #include "llvm/ADT/SmallVector.h"
15 #include "llvm/BinaryFormat/Dwarf.h"
16 #include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h"
17 #include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
18 #include <cstdint>
19 #include <utility>
20 
21 namespace llvm {
22 
23 class raw_ostream;
24 class ScopedPrinter;
25 
26 /// The accelerator tables are designed to allow efficient random access
27 /// (using a symbol name as a key) into debug info by providing an index of the
28 /// debug info DIEs. This class implements the common functionality of Apple and
29 /// DWARF 5 accelerator tables.
30 /// TODO: Generalize the rest of the AppleAcceleratorTable interface and move it
31 /// to this class.
32 class DWARFAcceleratorTable {
33 protected:
34   DWARFDataExtractor AccelSection;
35   DataExtractor StringSection;
36 
37 public:
38   /// An abstract class representing a single entry in the accelerator tables.
39   class Entry {
40   protected:
41     SmallVector<DWARFFormValue, 3> Values;
42 
43     Entry() = default;
44 
45     // Make these protected so only (final) subclasses can be copied around.
46     Entry(const Entry &) = default;
47     Entry(Entry &&) = default;
48     Entry &operator=(const Entry &) = default;
49     Entry &operator=(Entry &&) = default;
50     ~Entry() = default;
51 
52 
53   public:
54     /// Returns the Offset of the Compilation Unit associated with this
55     /// Accelerator Entry or None if the Compilation Unit offset is not recorded
56     /// in this Accelerator Entry.
57     virtual Optional<uint64_t> getCUOffset() const = 0;
58 
59     /// Returns the Tag of the Debug Info Entry associated with this
60     /// Accelerator Entry or None if the Tag is not recorded in this
61     /// Accelerator Entry.
62     virtual Optional<dwarf::Tag> getTag() const = 0;
63 
64     /// Returns the raw values of fields in the Accelerator Entry. In general,
65     /// these can only be interpreted with the help of the metadata in the
66     /// owning Accelerator Table.
getValues()67     ArrayRef<DWARFFormValue> getValues() const { return Values; }
68   };
69 
DWARFAcceleratorTable(const DWARFDataExtractor & AccelSection,DataExtractor StringSection)70   DWARFAcceleratorTable(const DWARFDataExtractor &AccelSection,
71                         DataExtractor StringSection)
72       : AccelSection(AccelSection), StringSection(StringSection) {}
73   virtual ~DWARFAcceleratorTable();
74 
75   virtual llvm::Error extract() = 0;
76   virtual void dump(raw_ostream &OS) const = 0;
77 
78   DWARFAcceleratorTable(const DWARFAcceleratorTable &) = delete;
79   void operator=(const DWARFAcceleratorTable &) = delete;
80 };
81 
82 /// This implements the Apple accelerator table format, a precursor of the
83 /// DWARF 5 accelerator table format.
84 class AppleAcceleratorTable : public DWARFAcceleratorTable {
85   struct Header {
86     uint32_t Magic;
87     uint16_t Version;
88     uint16_t HashFunction;
89     uint32_t BucketCount;
90     uint32_t HashCount;
91     uint32_t HeaderDataLength;
92 
93     void dump(ScopedPrinter &W) const;
94   };
95 
96   struct HeaderData {
97     using AtomType = uint16_t;
98     using Form = dwarf::Form;
99 
100     uint32_t DIEOffsetBase;
101     SmallVector<std::pair<AtomType, Form>, 3> Atoms;
102 
103     Optional<uint64_t> extractOffset(Optional<DWARFFormValue> Value) const;
104   };
105 
106   struct Header Hdr;
107   struct HeaderData HdrData;
108   bool IsValid = false;
109 
110   /// Returns true if we should continue scanning for entries or false if we've
111   /// reached the last (sentinel) entry of encountered a parsing error.
112   bool dumpName(ScopedPrinter &W, SmallVectorImpl<DWARFFormValue> &AtomForms,
113                 uint32_t *DataOffset) const;
114 
115 public:
116   /// Apple-specific implementation of an Accelerator Entry.
117   class Entry final : public DWARFAcceleratorTable::Entry {
118     const HeaderData *HdrData = nullptr;
119 
120     Entry(const HeaderData &Data);
121     Entry() = default;
122 
123     void extract(const AppleAcceleratorTable &AccelTable, uint32_t *Offset);
124 
125   public:
126     Optional<uint64_t> getCUOffset() const override;
127 
128     /// Returns the Section Offset of the Debug Info Entry associated with this
129     /// Accelerator Entry or None if the DIE offset is not recorded in this
130     /// Accelerator Entry. The returned offset is relative to the start of the
131     /// Section containing the DIE.
132     Optional<uint64_t> getDIESectionOffset() const;
133 
134     Optional<dwarf::Tag> getTag() const override;
135 
136     /// Returns the value of the Atom in this Accelerator Entry, if the Entry
137     /// contains such Atom.
138     Optional<DWARFFormValue> lookup(HeaderData::AtomType Atom) const;
139 
140     friend class AppleAcceleratorTable;
141     friend class ValueIterator;
142   };
143 
144   class ValueIterator : public std::iterator<std::input_iterator_tag, Entry> {
145     const AppleAcceleratorTable *AccelTable = nullptr;
146     Entry Current;           ///< The current entry.
147     unsigned DataOffset = 0; ///< Offset into the section.
148     unsigned Data = 0; ///< Current data entry.
149     unsigned NumData = 0; ///< Number of data entries.
150 
151     /// Advance the iterator.
152     void Next();
153   public:
154     /// Construct a new iterator for the entries at \p DataOffset.
155     ValueIterator(const AppleAcceleratorTable &AccelTable, unsigned DataOffset);
156     /// End marker.
157     ValueIterator() = default;
158 
159     const Entry &operator*() const { return Current; }
160     ValueIterator &operator++() { Next(); return *this; }
161     ValueIterator operator++(int) {
162       ValueIterator I = *this;
163       Next();
164       return I;
165     }
166     friend bool operator==(const ValueIterator &A, const ValueIterator &B) {
167       return A.NumData == B.NumData && A.DataOffset == B.DataOffset;
168     }
169     friend bool operator!=(const ValueIterator &A, const ValueIterator &B) {
170       return !(A == B);
171     }
172   };
173 
AppleAcceleratorTable(const DWARFDataExtractor & AccelSection,DataExtractor StringSection)174   AppleAcceleratorTable(const DWARFDataExtractor &AccelSection,
175                         DataExtractor StringSection)
176       : DWARFAcceleratorTable(AccelSection, StringSection) {}
177 
178   llvm::Error extract() override;
179   uint32_t getNumBuckets();
180   uint32_t getNumHashes();
181   uint32_t getSizeHdr();
182   uint32_t getHeaderDataLength();
183 
184   /// Return the Atom description, which can be used to interpret the raw values
185   /// of the Accelerator Entries in this table.
186   ArrayRef<std::pair<HeaderData::AtomType, HeaderData::Form>> getAtomsDesc();
187   bool validateForms();
188 
189   /// Return information related to the DWARF DIE we're looking for when
190   /// performing a lookup by name.
191   ///
192   /// \param HashDataOffset an offset into the hash data table
193   /// \returns <DieOffset, DieTag>
194   /// DieOffset is the offset into the .debug_info section for the DIE
195   /// related to the input hash data offset.
196   /// DieTag is the tag of the DIE
197   std::pair<uint32_t, dwarf::Tag> readAtoms(uint32_t &HashDataOffset);
198   void dump(raw_ostream &OS) const override;
199 
200   /// Look up all entries in the accelerator table matching \c Key.
201   iterator_range<ValueIterator> equal_range(StringRef Key) const;
202 };
203 
204 /// .debug_names section consists of one or more units. Each unit starts with a
205 /// header, which is followed by a list of compilation units, local and foreign
206 /// type units.
207 ///
208 /// These may be followed by an (optional) hash lookup table, which consists of
209 /// an array of buckets and hashes similar to the apple tables above. The only
210 /// difference is that the hashes array is 1-based, and consequently an empty
211 /// bucket is denoted by 0 and not UINT32_MAX.
212 ///
213 /// Next is the name table, which consists of an array of names and array of
214 /// entry offsets. This is different from the apple tables, which store names
215 /// next to the actual entries.
216 ///
217 /// The structure of the entries is described by an abbreviations table, which
218 /// comes after the name table. Unlike the apple tables, which have a uniform
219 /// entry structure described in the header, each .debug_names entry may have
220 /// different index attributes (DW_IDX_???) attached to it.
221 ///
222 /// The last segment consists of a list of entries, which is a 0-terminated list
223 /// referenced by the name table and interpreted with the help of the
224 /// abbreviation table.
225 class DWARFDebugNames : public DWARFAcceleratorTable {
226   /// The fixed-size part of a Dwarf 5 Name Index header
227   struct HeaderPOD {
228     uint32_t UnitLength;
229     uint16_t Version;
230     uint16_t Padding;
231     uint32_t CompUnitCount;
232     uint32_t LocalTypeUnitCount;
233     uint32_t ForeignTypeUnitCount;
234     uint32_t BucketCount;
235     uint32_t NameCount;
236     uint32_t AbbrevTableSize;
237     uint32_t AugmentationStringSize;
238   };
239 
240 public:
241   class NameIndex;
242   class NameIterator;
243   class ValueIterator;
244 
245   /// Dwarf 5 Name Index header.
246   struct Header : public HeaderPOD {
247     SmallString<8> AugmentationString;
248 
249     Error extract(const DWARFDataExtractor &AS, uint32_t *Offset);
250     void dump(ScopedPrinter &W) const;
251   };
252 
253   /// Index attribute and its encoding.
254   struct AttributeEncoding {
255     dwarf::Index Index;
256     dwarf::Form Form;
257 
AttributeEncodingAttributeEncoding258     constexpr AttributeEncoding(dwarf::Index Index, dwarf::Form Form)
259         : Index(Index), Form(Form) {}
260 
261     friend bool operator==(const AttributeEncoding &LHS,
262                            const AttributeEncoding &RHS) {
263       return LHS.Index == RHS.Index && LHS.Form == RHS.Form;
264     }
265   };
266 
267   /// Abbreviation describing the encoding of Name Index entries.
268   struct Abbrev {
269     uint32_t Code;  ///< Abbreviation code
270     dwarf::Tag Tag; ///< Dwarf Tag of the described entity.
271     std::vector<AttributeEncoding> Attributes; ///< List of index attributes.
272 
AbbrevAbbrev273     Abbrev(uint32_t Code, dwarf::Tag Tag,
274            std::vector<AttributeEncoding> Attributes)
275         : Code(Code), Tag(Tag), Attributes(std::move(Attributes)) {}
276 
277     void dump(ScopedPrinter &W) const;
278   };
279 
280   /// DWARF v5-specific implementation of an Accelerator Entry.
281   class Entry final : public DWARFAcceleratorTable::Entry {
282     const NameIndex *NameIdx;
283     const Abbrev *Abbr;
284 
285     Entry(const NameIndex &NameIdx, const Abbrev &Abbr);
286 
287   public:
288     Optional<uint64_t> getCUOffset() const override;
getTag()289     Optional<dwarf::Tag> getTag() const override { return tag(); }
290 
291     /// Returns the Index into the Compilation Unit list of the owning Name
292     /// Index or None if this Accelerator Entry does not have an associated
293     /// Compilation Unit. It is up to the user to verify that the returned Index
294     /// is valid in the owning NameIndex (or use getCUOffset(), which will
295     /// handle that check itself). Note that entries in NameIndexes which index
296     /// just a single Compilation Unit are implicitly associated with that unit,
297     /// so this function will return 0 even without an explicit
298     /// DW_IDX_compile_unit attribute.
299     Optional<uint64_t> getCUIndex() const;
300 
301     /// .debug_names-specific getter, which always succeeds (DWARF v5 index
302     /// entries always have a tag).
tag()303     dwarf::Tag tag() const { return Abbr->Tag; }
304 
305     /// Returns the Offset of the DIE within the containing CU or TU.
306     Optional<uint64_t> getDIEUnitOffset() const;
307 
308     /// Return the Abbreviation that can be used to interpret the raw values of
309     /// this Accelerator Entry.
getAbbrev()310     const Abbrev &getAbbrev() const { return *Abbr; }
311 
312     /// Returns the value of the Index Attribute in this Accelerator Entry, if
313     /// the Entry contains such Attribute.
314     Optional<DWARFFormValue> lookup(dwarf::Index Index) const;
315 
316     void dump(ScopedPrinter &W) const;
317 
318     friend class NameIndex;
319     friend class ValueIterator;
320   };
321 
322   /// Error returned by NameIndex::getEntry to report it has reached the end of
323   /// the entry list.
324   class SentinelError : public ErrorInfo<SentinelError> {
325   public:
326     static char ID;
327 
log(raw_ostream & OS)328     void log(raw_ostream &OS) const override { OS << "Sentinel"; }
329     std::error_code convertToErrorCode() const override;
330   };
331 
332 private:
333   /// DenseMapInfo for struct Abbrev.
334   struct AbbrevMapInfo {
335     static Abbrev getEmptyKey();
336     static Abbrev getTombstoneKey();
getHashValueAbbrevMapInfo337     static unsigned getHashValue(uint32_t Code) {
338       return DenseMapInfo<uint32_t>::getHashValue(Code);
339     }
getHashValueAbbrevMapInfo340     static unsigned getHashValue(const Abbrev &Abbr) {
341       return getHashValue(Abbr.Code);
342     }
isEqualAbbrevMapInfo343     static bool isEqual(uint32_t LHS, const Abbrev &RHS) {
344       return LHS == RHS.Code;
345     }
isEqualAbbrevMapInfo346     static bool isEqual(const Abbrev &LHS, const Abbrev &RHS) {
347       return LHS.Code == RHS.Code;
348     }
349   };
350 
351 public:
352   /// A single entry in the Name Table (Dwarf 5 sect. 6.1.1.4.6) of the Name
353   /// Index.
354   class NameTableEntry {
355     DataExtractor StrData;
356 
357     uint32_t Index;
358     uint32_t StringOffset;
359     uint32_t EntryOffset;
360 
361   public:
NameTableEntry(const DataExtractor & StrData,uint32_t Index,uint32_t StringOffset,uint32_t EntryOffset)362     NameTableEntry(const DataExtractor &StrData, uint32_t Index,
363                    uint32_t StringOffset, uint32_t EntryOffset)
364         : StrData(StrData), Index(Index), StringOffset(StringOffset),
365           EntryOffset(EntryOffset) {}
366 
367     /// Return the index of this name in the parent Name Index.
getIndex()368     uint32_t getIndex() const { return Index; }
369 
370     /// Returns the offset of the name of the described entities.
getStringOffset()371     uint32_t getStringOffset() const { return StringOffset; }
372 
373     /// Return the string referenced by this name table entry or nullptr if the
374     /// string offset is not valid.
getString()375     const char *getString() const {
376       uint32_t Off = StringOffset;
377       return StrData.getCStr(&Off);
378     }
379 
380     /// Returns the offset of the first Entry in the list.
getEntryOffset()381     uint32_t getEntryOffset() const { return EntryOffset; }
382   };
383 
384   /// Represents a single accelerator table within the Dwarf 5 .debug_names
385   /// section.
386   class NameIndex {
387     DenseSet<Abbrev, AbbrevMapInfo> Abbrevs;
388     struct Header Hdr;
389     const DWARFDebugNames &Section;
390 
391     // Base of the whole unit and of various important tables, as offsets from
392     // the start of the section.
393     uint32_t Base;
394     uint32_t CUsBase;
395     uint32_t BucketsBase;
396     uint32_t HashesBase;
397     uint32_t StringOffsetsBase;
398     uint32_t EntryOffsetsBase;
399     uint32_t EntriesBase;
400 
401     void dumpCUs(ScopedPrinter &W) const;
402     void dumpLocalTUs(ScopedPrinter &W) const;
403     void dumpForeignTUs(ScopedPrinter &W) const;
404     void dumpAbbreviations(ScopedPrinter &W) const;
405     bool dumpEntry(ScopedPrinter &W, uint32_t *Offset) const;
406     void dumpName(ScopedPrinter &W, const NameTableEntry &NTE,
407                   Optional<uint32_t> Hash) const;
408     void dumpBucket(ScopedPrinter &W, uint32_t Bucket) const;
409 
410     Expected<AttributeEncoding> extractAttributeEncoding(uint32_t *Offset);
411 
412     Expected<std::vector<AttributeEncoding>>
413     extractAttributeEncodings(uint32_t *Offset);
414 
415     Expected<Abbrev> extractAbbrev(uint32_t *Offset);
416 
417   public:
NameIndex(const DWARFDebugNames & Section,uint32_t Base)418     NameIndex(const DWARFDebugNames &Section, uint32_t Base)
419         : Section(Section), Base(Base) {}
420 
421     /// Reads offset of compilation unit CU. CU is 0-based.
422     uint32_t getCUOffset(uint32_t CU) const;
getCUCount()423     uint32_t getCUCount() const { return Hdr.CompUnitCount; }
424 
425     /// Reads offset of local type unit TU, TU is 0-based.
426     uint32_t getLocalTUOffset(uint32_t TU) const;
getLocalTUCount()427     uint32_t getLocalTUCount() const { return Hdr.LocalTypeUnitCount; }
428 
429     /// Reads signature of foreign type unit TU. TU is 0-based.
430     uint64_t getForeignTUSignature(uint32_t TU) const;
getForeignTUCount()431     uint32_t getForeignTUCount() const { return Hdr.ForeignTypeUnitCount; }
432 
433     /// Reads an entry in the Bucket Array for the given Bucket. The returned
434     /// value is a (1-based) index into the Names, StringOffsets and
435     /// EntryOffsets arrays. The input Bucket index is 0-based.
436     uint32_t getBucketArrayEntry(uint32_t Bucket) const;
getBucketCount()437     uint32_t getBucketCount() const { return Hdr.BucketCount; }
438 
439     /// Reads an entry in the Hash Array for the given Index. The input Index
440     /// is 1-based.
441     uint32_t getHashArrayEntry(uint32_t Index) const;
442 
443     /// Reads an entry in the Name Table for the given Index. The Name Table
444     /// consists of two arrays -- String Offsets and Entry Offsets. The returned
445     /// offsets are relative to the starts of respective sections. Input Index
446     /// is 1-based.
447     NameTableEntry getNameTableEntry(uint32_t Index) const;
448 
getNameCount()449     uint32_t getNameCount() const { return Hdr.NameCount; }
450 
getAbbrevs()451     const DenseSet<Abbrev, AbbrevMapInfo> &getAbbrevs() const {
452       return Abbrevs;
453     }
454 
455     Expected<Entry> getEntry(uint32_t *Offset) const;
456 
457     /// Look up all entries in this Name Index matching \c Key.
458     iterator_range<ValueIterator> equal_range(StringRef Key) const;
459 
begin()460     NameIterator begin() const { return NameIterator(this, 1); }
end()461     NameIterator end() const { return NameIterator(this, getNameCount() + 1); }
462 
463     llvm::Error extract();
getUnitOffset()464     uint32_t getUnitOffset() const { return Base; }
getNextUnitOffset()465     uint32_t getNextUnitOffset() const { return Base + 4 + Hdr.UnitLength; }
466     void dump(ScopedPrinter &W) const;
467 
468     friend class DWARFDebugNames;
469   };
470 
471   class ValueIterator : public std::iterator<std::input_iterator_tag, Entry> {
472 
473     /// The Name Index we are currently iterating through. The implementation
474     /// relies on the fact that this can also be used as an iterator into the
475     /// "NameIndices" vector in the Accelerator section.
476     const NameIndex *CurrentIndex = nullptr;
477 
478     /// Whether this is a local iterator (searches in CurrentIndex only) or not
479     /// (searches all name indices).
480     bool IsLocal;
481 
482     Optional<Entry> CurrentEntry;
483     unsigned DataOffset = 0; ///< Offset into the section.
484     std::string Key;         ///< The Key we are searching for.
485     Optional<uint32_t> Hash; ///< Hash of Key, if it has been computed.
486 
487     bool getEntryAtCurrentOffset();
488     Optional<uint32_t> findEntryOffsetInCurrentIndex();
489     bool findInCurrentIndex();
490     void searchFromStartOfCurrentIndex();
491     void next();
492 
493     /// Set the iterator to the "end" state.
setEnd()494     void setEnd() { *this = ValueIterator(); }
495 
496   public:
497     /// Create a "begin" iterator for looping over all entries in the
498     /// accelerator table matching Key. The iterator will run through all Name
499     /// Indexes in the section in sequence.
500     ValueIterator(const DWARFDebugNames &AccelTable, StringRef Key);
501 
502     /// Create a "begin" iterator for looping over all entries in a specific
503     /// Name Index. Other indices in the section will not be visited.
504     ValueIterator(const NameIndex &NI, StringRef Key);
505 
506     /// End marker.
507     ValueIterator() = default;
508 
509     const Entry &operator*() const { return *CurrentEntry; }
510     ValueIterator &operator++() {
511       next();
512       return *this;
513     }
514     ValueIterator operator++(int) {
515       ValueIterator I = *this;
516       next();
517       return I;
518     }
519 
520     friend bool operator==(const ValueIterator &A, const ValueIterator &B) {
521       return A.CurrentIndex == B.CurrentIndex && A.DataOffset == B.DataOffset;
522     }
523     friend bool operator!=(const ValueIterator &A, const ValueIterator &B) {
524       return !(A == B);
525     }
526   };
527 
528   class NameIterator {
529 
530     /// The Name Index we are iterating through.
531     const NameIndex *CurrentIndex;
532 
533     /// The current name in the Name Index.
534     uint32_t CurrentName;
535 
next()536     void next() {
537       assert(CurrentName <= CurrentIndex->getNameCount());
538       ++CurrentName;
539     }
540 
541   public:
542     using iterator_category = std::input_iterator_tag;
543     using value_type = NameTableEntry;
544     using difference_type = uint32_t;
545     using pointer = NameTableEntry *;
546     using reference = NameTableEntry; // We return entries by value.
547 
548     /// Creates an iterator whose initial position is name CurrentName in
549     /// CurrentIndex.
NameIterator(const NameIndex * CurrentIndex,uint32_t CurrentName)550     NameIterator(const NameIndex *CurrentIndex, uint32_t CurrentName)
551         : CurrentIndex(CurrentIndex), CurrentName(CurrentName) {}
552 
553     NameTableEntry operator*() const {
554       return CurrentIndex->getNameTableEntry(CurrentName);
555     }
556     NameIterator &operator++() {
557       next();
558       return *this;
559     }
560     NameIterator operator++(int) {
561       NameIterator I = *this;
562       next();
563       return I;
564     }
565 
566     friend bool operator==(const NameIterator &A, const NameIterator &B) {
567       return A.CurrentIndex == B.CurrentIndex && A.CurrentName == B.CurrentName;
568     }
569     friend bool operator!=(const NameIterator &A, const NameIterator &B) {
570       return !(A == B);
571     }
572   };
573 
574 private:
575   SmallVector<NameIndex, 0> NameIndices;
576   DenseMap<uint32_t, const NameIndex *> CUToNameIndex;
577 
578 public:
DWARFDebugNames(const DWARFDataExtractor & AccelSection,DataExtractor StringSection)579   DWARFDebugNames(const DWARFDataExtractor &AccelSection,
580                   DataExtractor StringSection)
581       : DWARFAcceleratorTable(AccelSection, StringSection) {}
582 
583   llvm::Error extract() override;
584   void dump(raw_ostream &OS) const override;
585 
586   /// Look up all entries in the accelerator table matching \c Key.
587   iterator_range<ValueIterator> equal_range(StringRef Key) const;
588 
589   using const_iterator = SmallVector<NameIndex, 0>::const_iterator;
begin()590   const_iterator begin() const { return NameIndices.begin(); }
end()591   const_iterator end() const { return NameIndices.end(); }
592 
593   /// Return the Name Index covering the compile unit at CUOffset, or nullptr if
594   /// there is no Name Index covering that unit.
595   const NameIndex *getCUNameIndex(uint32_t CUOffset);
596 };
597 
598 } // end namespace llvm
599 
600 #endif // LLVM_DEBUGINFO_DWARFACCELERATORTABLE_H
601