1 | //===- DWARFLinkerDeclContext.cpp -----------------------------------------===// |
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 | #include "llvm/DWARFLinker/Classic/DWARFLinkerDeclContext.h" |
10 | #include "llvm/DWARFLinker/Classic/DWARFLinkerCompileUnit.h" |
11 | #include "llvm/DebugInfo/DWARF/DWARFContext.h" |
12 | #include "llvm/DebugInfo/DWARF/DWARFDie.h" |
13 | #include "llvm/DebugInfo/DWARF/DWARFUnit.h" |
14 | |
15 | namespace llvm { |
16 | |
17 | using namespace dwarf_linker; |
18 | using namespace dwarf_linker::classic; |
19 | |
20 | /// Set the last DIE/CU a context was seen in and, possibly invalidate the |
21 | /// context if it is ambiguous. |
22 | /// |
23 | /// In the current implementation, we don't handle overloaded functions well, |
24 | /// because the argument types are not taken into account when computing the |
25 | /// DeclContext tree. |
26 | /// |
27 | /// Some of this is mitigated byt using mangled names that do contain the |
28 | /// arguments types, but sometimes (e.g. with function templates) we don't have |
29 | /// that. In that case, just do not unique anything that refers to the contexts |
30 | /// we are not able to distinguish. |
31 | /// |
32 | /// If a context that is not a namespace appears twice in the same CU, we know |
33 | /// it is ambiguous. Make it invalid. |
34 | bool DeclContext::setLastSeenDIE(CompileUnit &U, const DWARFDie &Die) { |
35 | if (LastSeenCompileUnitID == U.getUniqueID()) { |
36 | DWARFUnit &OrigUnit = U.getOrigUnit(); |
37 | uint32_t FirstIdx = OrigUnit.getDIEIndex(D: LastSeenDIE); |
38 | U.getInfo(Idx: FirstIdx).Ctxt = nullptr; |
39 | return false; |
40 | } |
41 | |
42 | LastSeenCompileUnitID = U.getUniqueID(); |
43 | LastSeenDIE = Die; |
44 | return true; |
45 | } |
46 | |
47 | PointerIntPair<DeclContext *, 1> |
48 | DeclContextTree::getChildDeclContext(DeclContext &Context, const DWARFDie &DIE, |
49 | CompileUnit &U, bool InClangModule) { |
50 | unsigned Tag = DIE.getTag(); |
51 | |
52 | // FIXME: dsymutil-classic compat: We should bail out here if we |
53 | // have a specification or an abstract_origin. We will get the |
54 | // parent context wrong here. |
55 | |
56 | switch (Tag) { |
57 | default: |
58 | // By default stop gathering child contexts. |
59 | return PointerIntPair<DeclContext *, 1>(nullptr); |
60 | case dwarf::DW_TAG_module: |
61 | break; |
62 | case dwarf::DW_TAG_compile_unit: |
63 | return PointerIntPair<DeclContext *, 1>(&Context); |
64 | case dwarf::DW_TAG_subprogram: |
65 | // Do not unique anything inside CU local functions. |
66 | if ((Context.getTag() == dwarf::DW_TAG_namespace || |
67 | Context.getTag() == dwarf::DW_TAG_compile_unit) && |
68 | !dwarf::toUnsigned(V: DIE.find(Attr: dwarf::DW_AT_external), Default: 0)) |
69 | return PointerIntPair<DeclContext *, 1>(nullptr); |
70 | [[fallthrough]]; |
71 | case dwarf::DW_TAG_member: |
72 | case dwarf::DW_TAG_namespace: |
73 | case dwarf::DW_TAG_structure_type: |
74 | case dwarf::DW_TAG_class_type: |
75 | case dwarf::DW_TAG_union_type: |
76 | case dwarf::DW_TAG_enumeration_type: |
77 | case dwarf::DW_TAG_typedef: |
78 | // Artificial things might be ambiguous, because they might be created on |
79 | // demand. For example implicitly defined constructors are ambiguous |
80 | // because of the way we identify contexts, and they won't be generated |
81 | // every time everywhere. |
82 | if (dwarf::toUnsigned(V: DIE.find(Attr: dwarf::DW_AT_artificial), Default: 0)) |
83 | return PointerIntPair<DeclContext *, 1>(nullptr); |
84 | break; |
85 | } |
86 | |
87 | StringRef NameRef; |
88 | StringRef FileRef; |
89 | |
90 | if (const char *LinkageName = DIE.getLinkageName()) |
91 | NameRef = StringPool.internString(S: LinkageName); |
92 | else if (const char *ShortName = DIE.getShortName()) |
93 | NameRef = StringPool.internString(S: ShortName); |
94 | |
95 | bool IsAnonymousNamespace = NameRef.empty() && Tag == dwarf::DW_TAG_namespace; |
96 | if (IsAnonymousNamespace) { |
97 | // FIXME: For dsymutil-classic compatibility. I think uniquing within |
98 | // anonymous namespaces is wrong. There is no ODR guarantee there. |
99 | NameRef = "(anonymous namespace)" ; |
100 | } |
101 | |
102 | if (Tag != dwarf::DW_TAG_class_type && Tag != dwarf::DW_TAG_structure_type && |
103 | Tag != dwarf::DW_TAG_union_type && |
104 | Tag != dwarf::DW_TAG_enumeration_type && NameRef.empty()) |
105 | return PointerIntPair<DeclContext *, 1>(nullptr); |
106 | |
107 | unsigned Line = 0; |
108 | unsigned ByteSize = std::numeric_limits<uint32_t>::max(); |
109 | |
110 | if (!InClangModule) { |
111 | // Gather some discriminating data about the DeclContext we will be |
112 | // creating: File, line number and byte size. This shouldn't be necessary, |
113 | // because the ODR is just about names, but given that we do some |
114 | // approximations with overloaded functions and anonymous namespaces, use |
115 | // these additional data points to make the process safer. |
116 | // |
117 | // This is disabled for clang modules, because forward declarations of |
118 | // module-defined types do not have a file and line. |
119 | ByteSize = dwarf::toUnsigned(V: DIE.find(Attr: dwarf::DW_AT_byte_size), |
120 | Default: std::numeric_limits<uint64_t>::max()); |
121 | if (Tag != dwarf::DW_TAG_namespace || IsAnonymousNamespace) { |
122 | if (unsigned FileNum = |
123 | dwarf::toUnsigned(V: DIE.find(Attr: dwarf::DW_AT_decl_file), Default: 0)) { |
124 | if (const auto *LT = U.getOrigUnit().getContext().getLineTableForUnit( |
125 | U: &U.getOrigUnit())) { |
126 | // FIXME: dsymutil-classic compatibility. I'd rather not |
127 | // unique anything in anonymous namespaces, but if we do, then |
128 | // verify that the file and line correspond. |
129 | if (IsAnonymousNamespace) |
130 | FileNum = 1; |
131 | |
132 | if (LT->hasFileAtIndex(FileIndex: FileNum)) { |
133 | Line = dwarf::toUnsigned(V: DIE.find(Attr: dwarf::DW_AT_decl_line), Default: 0); |
134 | // Cache the resolved paths based on the index in the line table, |
135 | // because calling realpath is expensive. |
136 | FileRef = getResolvedPath(CU&: U, FileNum, LineTable: *LT); |
137 | } |
138 | } |
139 | } |
140 | } |
141 | } |
142 | |
143 | if (!Line && NameRef.empty()) |
144 | return PointerIntPair<DeclContext *, 1>(nullptr); |
145 | |
146 | // We hash NameRef, which is the mangled name, in order to get most |
147 | // overloaded functions resolve correctly. |
148 | // |
149 | // Strictly speaking, hashing the Tag is only necessary for a |
150 | // DW_TAG_module, to prevent uniquing of a module and a namespace |
151 | // with the same name. |
152 | // |
153 | // FIXME: dsymutil-classic won't unique the same type presented |
154 | // once as a struct and once as a class. Using the Tag in the fully |
155 | // qualified name hash to get the same effect. |
156 | unsigned Hash = hash_combine(args: Context.getQualifiedNameHash(), args: Tag, args: NameRef); |
157 | |
158 | // FIXME: dsymutil-classic compatibility: when we don't have a name, |
159 | // use the filename. |
160 | if (IsAnonymousNamespace) |
161 | Hash = hash_combine(args: Hash, args: FileRef); |
162 | |
163 | // Now look if this context already exists. |
164 | DeclContext Key(Hash, Line, ByteSize, Tag, NameRef, FileRef, Context); |
165 | auto ContextIter = Contexts.find(V: &Key); |
166 | |
167 | if (ContextIter == Contexts.end()) { |
168 | // The context wasn't found. |
169 | bool Inserted; |
170 | DeclContext *NewContext = |
171 | new (Allocator) DeclContext(Hash, Line, ByteSize, Tag, NameRef, FileRef, |
172 | Context, DIE, U.getUniqueID()); |
173 | std::tie(args&: ContextIter, args&: Inserted) = Contexts.insert(V: NewContext); |
174 | assert(Inserted && "Failed to insert DeclContext" ); |
175 | (void)Inserted; |
176 | } else if (Tag != dwarf::DW_TAG_namespace && |
177 | !(*ContextIter)->setLastSeenDIE(U, Die: DIE)) { |
178 | // The context was found, but it is ambiguous with another context |
179 | // in the same file. Mark it invalid. |
180 | return PointerIntPair<DeclContext *, 1>(*ContextIter, /* IntVal= */ 1); |
181 | } |
182 | |
183 | assert(ContextIter != Contexts.end()); |
184 | // FIXME: dsymutil-classic compatibility. Union types aren't |
185 | // uniques, but their children might be. |
186 | if ((Tag == dwarf::DW_TAG_subprogram && |
187 | Context.getTag() != dwarf::DW_TAG_structure_type && |
188 | Context.getTag() != dwarf::DW_TAG_class_type) || |
189 | (Tag == dwarf::DW_TAG_union_type)) |
190 | return PointerIntPair<DeclContext *, 1>(*ContextIter, /* IntVal= */ 1); |
191 | |
192 | return PointerIntPair<DeclContext *, 1>(*ContextIter); |
193 | } |
194 | |
195 | StringRef |
196 | DeclContextTree::getResolvedPath(CompileUnit &CU, unsigned FileNum, |
197 | const DWARFDebugLine::LineTable &LineTable) { |
198 | std::pair<unsigned, unsigned> Key = {CU.getUniqueID(), FileNum}; |
199 | |
200 | ResolvedPathsMap::const_iterator It = ResolvedPaths.find(Val: Key); |
201 | if (It == ResolvedPaths.end()) { |
202 | std::string FileName; |
203 | bool FoundFileName = LineTable.getFileNameByIndex( |
204 | FileIndex: FileNum, CompDir: CU.getOrigUnit().getCompilationDir(), |
205 | Kind: DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, Result&: FileName); |
206 | (void)FoundFileName; |
207 | assert(FoundFileName && "Must get file name from line table" ); |
208 | |
209 | // Second level of caching, this time based on the file's parent |
210 | // path. |
211 | StringRef ResolvedPath = PathResolver.resolve(Path: FileName, StringPool); |
212 | |
213 | It = ResolvedPaths.insert(KV: std::make_pair(x&: Key, y&: ResolvedPath)).first; |
214 | } |
215 | |
216 | return It->second; |
217 | } |
218 | |
219 | } // namespace llvm |
220 | |