1//===--- lib/CodeGen/DebugLocStream.h - DWARF debug_loc stream --*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#ifndef LLVM_LIB_CODEGEN_ASMPRINTER_DEBUGLOCSTREAM_H
10#define LLVM_LIB_CODEGEN_ASMPRINTER_DEBUGLOCSTREAM_H
11
12#include "ByteStreamer.h"
13#include "llvm/ADT/ArrayRef.h"
14#include "llvm/ADT/SmallVector.h"
15
16namespace llvm {
17
18class AsmPrinter;
19class DbgVariable;
20class DwarfCompileUnit;
21class MCSymbol;
22
23/// Byte stream of .debug_loc entries.
24///
25/// Stores a unified stream of .debug_loc entries.  There's \a List for each
26/// variable/inlined-at pair, and an \a Entry for each \a DebugLocEntry.
27///
28/// FIXME: Do we need all these temp symbols?
29/// FIXME: Why not output directly to the output stream?
30class DebugLocStream {
31public:
32  struct List {
33    DwarfCompileUnit *CU;
34    MCSymbol *Label = nullptr;
35    size_t EntryOffset;
36    List(DwarfCompileUnit *CU, size_t EntryOffset)
37        : CU(CU), EntryOffset(EntryOffset) {}
38  };
39  struct Entry {
40    const MCSymbol *Begin;
41    const MCSymbol *End;
42    size_t ByteOffset;
43    size_t CommentOffset;
44  };
45
46private:
47  SmallVector<List, 4> Lists;
48  SmallVector<Entry, 32> Entries;
49  SmallString<256> DWARFBytes;
50  std::vector<std::string> Comments;
51  MCSymbol *Sym = nullptr;
52
53  /// Only verbose textual output needs comments.  This will be set to
54  /// true for that case, and false otherwise.
55  bool GenerateComments;
56
57public:
58  DebugLocStream(bool GenerateComments) : GenerateComments(GenerateComments) { }
59  size_t getNumLists() const { return Lists.size(); }
60  const List &getList(size_t LI) const { return Lists[LI]; }
61  ArrayRef<List> getLists() const { return Lists; }
62  MCSymbol *getSym() const {
63    return Sym;
64  }
65  void setSym(MCSymbol *Sym) {
66    this->Sym = Sym;
67  }
68
69  class ListBuilder;
70  class EntryBuilder;
71
72private:
73  /// Start a new .debug_loc entry list.
74  ///
75  /// Start a new .debug_loc entry list.  Return the new list's index so it can
76  /// be retrieved later via \a getList().
77  ///
78  /// Until the next call, \a startEntry() will add entries to this list.
79  size_t startList(DwarfCompileUnit *CU) {
80    size_t LI = Lists.size();
81    Lists.emplace_back(CU, Entries.size());
82    return LI;
83  }
84
85  /// Finalize a .debug_loc entry list.
86  ///
87  /// If there are no entries in this list, delete it outright.  Otherwise,
88  /// create a label with \a Asm.
89  ///
90  /// \return false iff the list is deleted.
91  bool finalizeList(AsmPrinter &Asm);
92
93  /// Start a new .debug_loc entry.
94  ///
95  /// Until the next call, bytes added to the stream will be added to this
96  /// entry.
97  void startEntry(const MCSymbol *BeginSym, const MCSymbol *EndSym) {
98    Entries.push_back({BeginSym, EndSym, DWARFBytes.size(), Comments.size()});
99  }
100
101  /// Finalize a .debug_loc entry, deleting if it's empty.
102  void finalizeEntry();
103
104public:
105  BufferByteStreamer getStreamer() {
106    return BufferByteStreamer(DWARFBytes, Comments, GenerateComments);
107  }
108
109  ArrayRef<Entry> getEntries(const List &L) const {
110    size_t LI = getIndex(L);
111    return ArrayRef(Entries).slice(Lists[LI].EntryOffset, getNumEntries(LI));
112  }
113
114  ArrayRef<char> getBytes(const Entry &E) const {
115    size_t EI = getIndex(E);
116    return ArrayRef(DWARFBytes.begin(), DWARFBytes.end())
117        .slice(Entries[EI].ByteOffset, getNumBytes(EI));
118  }
119  ArrayRef<std::string> getComments(const Entry &E) const {
120    size_t EI = getIndex(E);
121    return ArrayRef(Comments).slice(Entries[EI].CommentOffset,
122                                    getNumComments(EI));
123  }
124
125private:
126  size_t getIndex(const List &L) const {
127    assert(&Lists.front() <= &L && &L <= &Lists.back() &&
128           "Expected valid list");
129    return &L - &Lists.front();
130  }
131  size_t getIndex(const Entry &E) const {
132    assert(&Entries.front() <= &E && &E <= &Entries.back() &&
133           "Expected valid entry");
134    return &E - &Entries.front();
135  }
136  size_t getNumEntries(size_t LI) const {
137    if (LI + 1 == Lists.size())
138      return Entries.size() - Lists[LI].EntryOffset;
139    return Lists[LI + 1].EntryOffset - Lists[LI].EntryOffset;
140  }
141  size_t getNumBytes(size_t EI) const {
142    if (EI + 1 == Entries.size())
143      return DWARFBytes.size() - Entries[EI].ByteOffset;
144    return Entries[EI + 1].ByteOffset - Entries[EI].ByteOffset;
145  }
146  size_t getNumComments(size_t EI) const {
147    if (EI + 1 == Entries.size())
148      return Comments.size() - Entries[EI].CommentOffset;
149    return Entries[EI + 1].CommentOffset - Entries[EI].CommentOffset;
150  }
151};
152
153/// Builder for DebugLocStream lists.
154class DebugLocStream::ListBuilder {
155  DebugLocStream &Locs;
156  AsmPrinter &Asm;
157  DbgVariable &V;
158  size_t ListIndex;
159  std::optional<uint8_t> TagOffset;
160
161public:
162  ListBuilder(DebugLocStream &Locs, DwarfCompileUnit &CU, AsmPrinter &Asm,
163              DbgVariable &V)
164      : Locs(Locs), Asm(Asm), V(V), ListIndex(Locs.startList(&CU)),
165        TagOffset(std::nullopt) {}
166
167  void setTagOffset(uint8_t TO) {
168    TagOffset = TO;
169  }
170
171  /// Finalize the list.
172  ///
173  /// If the list is empty, delete it.  Otherwise, finalize it by creating a
174  /// temp symbol in \a Asm and setting up the \a DbgVariable.
175  ~ListBuilder();
176
177  DebugLocStream &getLocs() { return Locs; }
178};
179
180/// Builder for DebugLocStream entries.
181class DebugLocStream::EntryBuilder {
182  DebugLocStream &Locs;
183
184public:
185  EntryBuilder(ListBuilder &List, const MCSymbol *Begin, const MCSymbol *End)
186      : Locs(List.getLocs()) {
187    Locs.startEntry(Begin, End);
188  }
189
190  /// Finalize the entry, deleting it if it's empty.
191  ~EntryBuilder() { Locs.finalizeEntry(); }
192
193  BufferByteStreamer getStreamer() { return Locs.getStreamer(); }
194};
195
196} // namespace llvm
197
198#endif
199