1 //===- DWARFLinkerDeclContext.h ---------------------------------*- 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_DWARFLINKER_DWARFLINKERDECLCONTEXT_H 10 #define LLVM_DWARFLINKER_DWARFLINKERDECLCONTEXT_H 11 12 #include "llvm/ADT/DenseMap.h" 13 #include "llvm/ADT/DenseMapInfo.h" 14 #include "llvm/ADT/DenseSet.h" 15 #include "llvm/ADT/StringRef.h" 16 #include "llvm/CodeGen/NonRelocatableStringpool.h" 17 #include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" 18 #include "llvm/DebugInfo/DWARF/DWARFDie.h" 19 #include "llvm/Support/FileSystem.h" 20 #include "llvm/Support/Path.h" 21 #include <atomic> 22 23 namespace llvm { 24 25 class CompileUnit; 26 struct DeclMapInfo; 27 28 /// Small helper that resolves and caches file paths. This helps reduce the 29 /// number of calls to realpath which is expensive. We assume the input are 30 /// files, and cache the realpath of their parent. This way we can quickly 31 /// resolve different files under the same path. 32 class CachedPathResolver { 33 public: 34 /// Resolve a path by calling realpath and cache its result. The returned 35 /// StringRef is interned in the given \p StringPool. resolve(const std::string & Path,NonRelocatableStringpool & StringPool)36 StringRef resolve(const std::string &Path, 37 NonRelocatableStringpool &StringPool) { 38 StringRef FileName = sys::path::filename(Path); 39 StringRef ParentPath = sys::path::parent_path(Path); 40 41 // If the ParentPath has not yet been resolved, resolve and cache it for 42 // future look-ups. 43 if (!ResolvedPaths.count(ParentPath)) { 44 SmallString<256> RealPath; 45 sys::fs::real_path(ParentPath, RealPath); 46 ResolvedPaths.insert( 47 {ParentPath, std::string(RealPath.c_str(), RealPath.size())}); 48 } 49 50 // Join the file name again with the resolved path. 51 SmallString<256> ResolvedPath(ResolvedPaths[ParentPath]); 52 sys::path::append(ResolvedPath, FileName); 53 return StringPool.internString(ResolvedPath); 54 } 55 56 private: 57 StringMap<std::string> ResolvedPaths; 58 }; 59 60 /// A DeclContext is a named program scope that is used for ODR uniquing of 61 /// types. 62 /// 63 /// The set of DeclContext for the ODR-subject parts of a Dwarf link is 64 /// expanded (and uniqued) with each new object file processed. We need to 65 /// determine the context of each DIE in an linked object file to see if the 66 /// corresponding type has already been emitted. 67 /// 68 /// The contexts are conceptually organized as a tree (eg. a function scope is 69 /// contained in a namespace scope that contains other scopes), but 70 /// storing/accessing them in an actual tree is too inefficient: we need to be 71 /// able to very quickly query a context for a given child context by name. 72 /// Storing a StringMap in each DeclContext would be too space inefficient. 73 /// 74 /// The solution here is to give each DeclContext a link to its parent (this 75 /// allows to walk up the tree), but to query the existence of a specific 76 /// DeclContext using a separate DenseMap keyed on the hash of the fully 77 /// qualified name of the context. 78 class DeclContext { 79 public: 80 using Map = DenseSet<DeclContext *, DeclMapInfo>; 81 DeclContext()82 DeclContext() : DefinedInClangModule(0), Parent(*this) {} 83 84 DeclContext(unsigned Hash, uint32_t Line, uint32_t ByteSize, uint16_t Tag, 85 StringRef Name, StringRef File, const DeclContext &Parent, 86 DWARFDie LastSeenDIE = DWARFDie(), unsigned CUId = 0) QualifiedNameHash(Hash)87 : QualifiedNameHash(Hash), Line(Line), ByteSize(ByteSize), Tag(Tag), 88 DefinedInClangModule(0), Name(Name), File(File), Parent(Parent), 89 LastSeenDIE(LastSeenDIE), LastSeenCompileUnitID(CUId) {} 90 getQualifiedNameHash()91 uint32_t getQualifiedNameHash() const { return QualifiedNameHash; } 92 93 bool setLastSeenDIE(CompileUnit &U, const DWARFDie &Die); 94 setHasCanonicalDIE()95 void setHasCanonicalDIE() { HasCanonicalDIE = true; } 96 hasCanonicalDIE()97 bool hasCanonicalDIE() const { return HasCanonicalDIE; } 98 getCanonicalDIEOffset()99 uint32_t getCanonicalDIEOffset() const { return CanonicalDIEOffset; } setCanonicalDIEOffset(uint32_t Offset)100 void setCanonicalDIEOffset(uint32_t Offset) { CanonicalDIEOffset = Offset; } 101 isDefinedInClangModule()102 bool isDefinedInClangModule() const { return DefinedInClangModule; } setDefinedInClangModule(bool Val)103 void setDefinedInClangModule(bool Val) { DefinedInClangModule = Val; } 104 getTag()105 uint16_t getTag() const { return Tag; } 106 107 private: 108 friend DeclMapInfo; 109 110 unsigned QualifiedNameHash = 0; 111 uint32_t Line = 0; 112 uint32_t ByteSize = 0; 113 uint16_t Tag = dwarf::DW_TAG_compile_unit; 114 unsigned DefinedInClangModule : 1; 115 StringRef Name; 116 StringRef File; 117 const DeclContext &Parent; 118 DWARFDie LastSeenDIE; 119 uint32_t LastSeenCompileUnitID = 0; 120 std::atomic<uint32_t> CanonicalDIEOffset = {0}; 121 bool HasCanonicalDIE = false; 122 }; 123 124 /// This class gives a tree-like API to the DenseMap that stores the 125 /// DeclContext objects. It holds the BumpPtrAllocator where these objects will 126 /// be allocated. 127 class DeclContextTree { 128 public: 129 /// Get the child of \a Context described by \a DIE in \a Unit. The 130 /// required strings will be interned in \a StringPool. 131 /// \returns The child DeclContext along with one bit that is set if 132 /// this context is invalid. 133 /// 134 /// An invalid context means it shouldn't be considered for uniquing, but its 135 /// not returning null, because some children of that context might be 136 /// uniquing candidates. 137 /// 138 /// FIXME: The invalid bit along the return value is to emulate some 139 /// dsymutil-classic functionality. 140 PointerIntPair<DeclContext *, 1> getChildDeclContext(DeclContext &Context, 141 const DWARFDie &DIE, 142 CompileUnit &Unit, 143 bool InClangModule); 144 getRoot()145 DeclContext &getRoot() { return Root; } 146 147 private: 148 BumpPtrAllocator Allocator; 149 DeclContext Root; 150 DeclContext::Map Contexts; 151 152 /// Cached resolved paths from the line table. 153 /// The key is <UniqueUnitID, FileIdx>. 154 using ResolvedPathsMap = DenseMap<std::pair<unsigned, unsigned>, StringRef>; 155 ResolvedPathsMap ResolvedPaths; 156 157 /// Helper that resolves and caches fragments of file paths. 158 CachedPathResolver PathResolver; 159 160 /// String pool keeping real path bodies. 161 NonRelocatableStringpool StringPool; 162 163 StringRef getResolvedPath(CompileUnit &CU, unsigned FileNum, 164 const DWARFDebugLine::LineTable &LineTable); 165 }; 166 167 /// Info type for the DenseMap storing the DeclContext pointers. 168 struct DeclMapInfo : private DenseMapInfo<DeclContext *> { 169 using DenseMapInfo<DeclContext *>::getEmptyKey; 170 using DenseMapInfo<DeclContext *>::getTombstoneKey; 171 getHashValueDeclMapInfo172 static unsigned getHashValue(const DeclContext *Ctxt) { 173 return Ctxt->QualifiedNameHash; 174 } 175 isEqualDeclMapInfo176 static bool isEqual(const DeclContext *LHS, const DeclContext *RHS) { 177 if (RHS == getEmptyKey() || RHS == getTombstoneKey()) 178 return RHS == LHS; 179 return LHS->QualifiedNameHash == RHS->QualifiedNameHash && 180 LHS->Line == RHS->Line && LHS->ByteSize == RHS->ByteSize && 181 LHS->Name.data() == RHS->Name.data() && 182 LHS->File.data() == RHS->File.data() && 183 LHS->Parent.QualifiedNameHash == RHS->Parent.QualifiedNameHash; 184 } 185 }; 186 187 } // end namespace llvm 188 189 #endif // LLVM_DWARFLINKER_DWARFLINKERDECLCONTEXT_H 190