224 lines · cpp
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-exception6//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 15namespace llvm {16 17using namespace dwarf_linker;18using namespace dwarf_linker::classic;19 20/// Set the last DIE/CU a context was seen in and, possibly invalidate the21/// 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 the25/// DeclContext tree.26///27/// Some of this is mitigated byt using mangled names that do contain the28/// arguments types, but sometimes (e.g. with function templates) we don't have29/// that. In that case, just do not unique anything that refers to the contexts30/// we are not able to distinguish.31///32/// If a context that is not a namespace appears twice in the same CU, we know33/// it is ambiguous. Make it invalid.34bool DeclContext::setLastSeenDIE(CompileUnit &U, const DWARFDie &Die) {35 if (LastSeenCompileUnitID == U.getUniqueID()) {36 DWARFUnit &OrigUnit = U.getOrigUnit();37 uint32_t FirstIdx = OrigUnit.getDIEIndex(LastSeenDIE);38 U.getInfo(FirstIdx).Ctxt = nullptr;39 return false;40 }41 42 LastSeenCompileUnitID = U.getUniqueID();43 LastSeenDIE = Die;44 return true;45}46 47PointerIntPair<DeclContext *, 1>48DeclContextTree::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 we53 // have a specification or an abstract_origin. We will get the54 // 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(DIE.find(dwarf::DW_AT_external), 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 on79 // demand. For example implicitly defined constructors are ambiguous80 // because of the way we identify contexts, and they won't be generated81 // every time everywhere.82 if (dwarf::toUnsigned(DIE.find(dwarf::DW_AT_artificial), 0))83 return PointerIntPair<DeclContext *, 1>(nullptr);84 break;85 }86 87 StringRef Name = DIE.getShortName();88 StringRef NameForUniquing;89 StringRef FileRef;90 91 if (const char *LinkageName = DIE.getLinkageName())92 NameForUniquing = StringPool.internString(LinkageName);93 else if (!Name.empty())94 NameForUniquing = StringPool.internString(Name);95 96 bool IsAnonymousNamespace =97 NameForUniquing.empty() && Tag == dwarf::DW_TAG_namespace;98 if (IsAnonymousNamespace) {99 // FIXME: For dsymutil-classic compatibility. I think uniquing within100 // anonymous namespaces is wrong. There is no ODR guarantee there.101 NameForUniquing = "(anonymous namespace)";102 }103 104 if (Tag != dwarf::DW_TAG_class_type && Tag != dwarf::DW_TAG_structure_type &&105 Tag != dwarf::DW_TAG_union_type &&106 Tag != dwarf::DW_TAG_enumeration_type && NameForUniquing.empty())107 return PointerIntPair<DeclContext *, 1>(nullptr);108 109 unsigned Line = 0;110 unsigned ByteSize = std::numeric_limits<uint32_t>::max();111 112 if (!InClangModule) {113 // Gather some discriminating data about the DeclContext we will be114 // creating: File, line number and byte size. This shouldn't be necessary,115 // because the ODR is just about names, but given that we do some116 // approximations with overloaded functions and anonymous namespaces, use117 // these additional data points to make the process safer.118 //119 // This is disabled for clang modules, because forward declarations of120 // module-defined types do not have a file and line.121 ByteSize = dwarf::toUnsigned(DIE.find(dwarf::DW_AT_byte_size),122 std::numeric_limits<uint64_t>::max());123 if (Tag != dwarf::DW_TAG_namespace || IsAnonymousNamespace) {124 if (unsigned FileNum =125 dwarf::toUnsigned(DIE.find(dwarf::DW_AT_decl_file), 0)) {126 if (const auto *LT = U.getOrigUnit().getContext().getLineTableForUnit(127 &U.getOrigUnit())) {128 // FIXME: dsymutil-classic compatibility. I'd rather not129 // unique anything in anonymous namespaces, but if we do, then130 // verify that the file and line correspond.131 if (IsAnonymousNamespace)132 FileNum = 1;133 134 if (LT->hasFileAtIndex(FileNum)) {135 Line = dwarf::toUnsigned(DIE.find(dwarf::DW_AT_decl_line), 0);136 // Cache the resolved paths based on the index in the line table,137 // because calling realpath is expensive.138 FileRef = getResolvedPath(U, FileNum, *LT);139 }140 }141 }142 }143 }144 145 if (!Line && NameForUniquing.empty())146 return PointerIntPair<DeclContext *, 1>(nullptr);147 148 // We hash NameForUniquing, which is the mangled name, in order to get most149 // overloaded functions resolve correctly.150 //151 // Strictly speaking, hashing the Tag is only necessary for a152 // DW_TAG_module, to prevent uniquing of a module and a namespace153 // with the same name.154 //155 // FIXME: dsymutil-classic won't unique the same type presented156 // once as a struct and once as a class. Using the Tag in the fully157 // qualified name hash to get the same effect.158 unsigned Hash =159 hash_combine(Context.getQualifiedNameHash(), Tag, NameForUniquing);160 161 // FIXME: dsymutil-classic compatibility: when we don't have a name,162 // use the filename.163 if (IsAnonymousNamespace)164 Hash = hash_combine(Hash, FileRef);165 166 // Now look if this context already exists.167 DeclContext Key(Hash, Line, ByteSize, Tag, Name, NameForUniquing, FileRef,168 Context);169 auto ContextIter = Contexts.find(&Key);170 171 if (ContextIter == Contexts.end()) {172 // The context wasn't found.173 bool Inserted;174 DeclContext *NewContext = new (Allocator)175 DeclContext(Hash, Line, ByteSize, Tag, Name, NameForUniquing, FileRef,176 Context, DIE, U.getUniqueID());177 std::tie(ContextIter, Inserted) = Contexts.insert(NewContext);178 assert(Inserted && "Failed to insert DeclContext");179 (void)Inserted;180 } else if (Tag != dwarf::DW_TAG_namespace &&181 !(*ContextIter)->setLastSeenDIE(U, DIE)) {182 // The context was found, but it is ambiguous with another context183 // in the same file. Mark it invalid.184 return PointerIntPair<DeclContext *, 1>(*ContextIter, /* IntVal= */ 1);185 }186 187 assert(ContextIter != Contexts.end());188 // FIXME: dsymutil-classic compatibility. Union types aren't189 // uniques, but their children might be.190 if ((Tag == dwarf::DW_TAG_subprogram &&191 Context.getTag() != dwarf::DW_TAG_structure_type &&192 Context.getTag() != dwarf::DW_TAG_class_type) ||193 (Tag == dwarf::DW_TAG_union_type))194 return PointerIntPair<DeclContext *, 1>(*ContextIter, /* IntVal= */ 1);195 196 return PointerIntPair<DeclContext *, 1>(*ContextIter);197}198 199StringRef200DeclContextTree::getResolvedPath(CompileUnit &CU, unsigned FileNum,201 const DWARFDebugLine::LineTable &LineTable) {202 std::pair<unsigned, unsigned> Key = {CU.getUniqueID(), FileNum};203 204 ResolvedPathsMap::const_iterator It = ResolvedPaths.find(Key);205 if (It == ResolvedPaths.end()) {206 std::string FileName;207 bool FoundFileName = LineTable.getFileNameByIndex(208 FileNum, CU.getOrigUnit().getCompilationDir(),209 DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, FileName);210 (void)FoundFileName;211 assert(FoundFileName && "Must get file name from line table");212 213 // Second level of caching, this time based on the file's parent214 // path.215 StringRef ResolvedPath = PathResolver.resolve(FileName, StringPool);216 217 It = ResolvedPaths.insert(std::make_pair(Key, ResolvedPath)).first;218 }219 220 return It->second;221}222 223} // namespace llvm224