aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/llvm12/lib/DWARFLinker/DWARFLinkerDeclContext.cpp
blob: d9b3c4235b4de5386cde804b7ae8bef40f234c74 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
//===- DWARFLinkerDeclContext.cpp -----------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "llvm/DWARFLinker/DWARFLinkerDeclContext.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/DWARF/DWARFDie.h"
#include "llvm/DebugInfo/DWARF/DWARFUnit.h"

namespace llvm {

/// Set the last DIE/CU a context was seen in and, possibly invalidate the
/// context if it is ambiguous.
///
/// In the current implementation, we don't handle overloaded functions well,
/// because the argument types are not taken into account when computing the
/// DeclContext tree.
///
/// Some of this is mitigated byt using mangled names that do contain the
/// arguments types, but sometimes (e.g. with function templates) we don't have
/// that. In that case, just do not unique anything that refers to the contexts
/// we are not able to distinguish.
///
/// If a context that is not a namespace appears twice in the same CU, we know
/// it is ambiguous. Make it invalid.
bool DeclContext::setLastSeenDIE(CompileUnit &U, const DWARFDie &Die) {
  if (LastSeenCompileUnitID == U.getUniqueID()) {
    DWARFUnit &OrigUnit = U.getOrigUnit();
    uint32_t FirstIdx = OrigUnit.getDIEIndex(LastSeenDIE);
    U.getInfo(FirstIdx).Ctxt = nullptr;
    return false;
  }

  LastSeenCompileUnitID = U.getUniqueID();
  LastSeenDIE = Die;
  return true;
}

PointerIntPair<DeclContext *, 1>
DeclContextTree::getChildDeclContext(DeclContext &Context, const DWARFDie &DIE,
                                     CompileUnit &U, bool InClangModule) {
  unsigned Tag = DIE.getTag();

  // FIXME: dsymutil-classic compat: We should bail out here if we
  // have a specification or an abstract_origin. We will get the
  // parent context wrong here.

  switch (Tag) {
  default:
    // By default stop gathering child contexts.
    return PointerIntPair<DeclContext *, 1>(nullptr);
  case dwarf::DW_TAG_module:
    break;
  case dwarf::DW_TAG_compile_unit:
    return PointerIntPair<DeclContext *, 1>(&Context);
  case dwarf::DW_TAG_subprogram:
    // Do not unique anything inside CU local functions.
    if ((Context.getTag() == dwarf::DW_TAG_namespace ||
         Context.getTag() == dwarf::DW_TAG_compile_unit) &&
        !dwarf::toUnsigned(DIE.find(dwarf::DW_AT_external), 0))
      return PointerIntPair<DeclContext *, 1>(nullptr);
    LLVM_FALLTHROUGH;
  case dwarf::DW_TAG_member:
  case dwarf::DW_TAG_namespace:
  case dwarf::DW_TAG_structure_type:
  case dwarf::DW_TAG_class_type:
  case dwarf::DW_TAG_union_type:
  case dwarf::DW_TAG_enumeration_type:
  case dwarf::DW_TAG_typedef:
    // Artificial things might be ambiguous, because they might be created on
    // demand. For example implicitly defined constructors are ambiguous
    // because of the way we identify contexts, and they won't be generated
    // every time everywhere.
    if (dwarf::toUnsigned(DIE.find(dwarf::DW_AT_artificial), 0))
      return PointerIntPair<DeclContext *, 1>(nullptr);
    break;
  }

  StringRef NameRef;
  StringRef FileRef;

  if (const char *LinkageName = DIE.getLinkageName())
    NameRef = StringPool.internString(LinkageName);
  else if (const char *ShortName = DIE.getShortName())
    NameRef = StringPool.internString(ShortName);

  bool IsAnonymousNamespace = NameRef.empty() && Tag == dwarf::DW_TAG_namespace;
  if (IsAnonymousNamespace) {
    // FIXME: For dsymutil-classic compatibility. I think uniquing within
    // anonymous namespaces is wrong. There is no ODR guarantee there.
    NameRef = "(anonymous namespace)";
  }

  if (Tag != dwarf::DW_TAG_class_type && Tag != dwarf::DW_TAG_structure_type &&
      Tag != dwarf::DW_TAG_union_type &&
      Tag != dwarf::DW_TAG_enumeration_type && NameRef.empty())
    return PointerIntPair<DeclContext *, 1>(nullptr);

  unsigned Line = 0;
  unsigned ByteSize = std::numeric_limits<uint32_t>::max();

  if (!InClangModule) {
    // Gather some discriminating data about the DeclContext we will be
    // creating: File, line number and byte size. This shouldn't be necessary,
    // because the ODR is just about names, but given that we do some
    // approximations with overloaded functions and anonymous namespaces, use
    // these additional data points to make the process safer.
    //
    // This is disabled for clang modules, because forward declarations of
    // module-defined types do not have a file and line.
    ByteSize = dwarf::toUnsigned(DIE.find(dwarf::DW_AT_byte_size),
                                 std::numeric_limits<uint64_t>::max());
    if (Tag != dwarf::DW_TAG_namespace || IsAnonymousNamespace) {
      if (unsigned FileNum =
              dwarf::toUnsigned(DIE.find(dwarf::DW_AT_decl_file), 0)) {
        if (const auto *LT = U.getOrigUnit().getContext().getLineTableForUnit(
                &U.getOrigUnit())) {
          // FIXME: dsymutil-classic compatibility. I'd rather not
          // unique anything in anonymous namespaces, but if we do, then
          // verify that the file and line correspond.
          if (IsAnonymousNamespace)
            FileNum = 1;

          if (LT->hasFileAtIndex(FileNum)) {
            Line = dwarf::toUnsigned(DIE.find(dwarf::DW_AT_decl_line), 0);
            // Cache the resolved paths based on the index in the line table,
            // because calling realpath is expensive.
            FileRef = getResolvedPath(U, FileNum, *LT);
          }
        }
      }
    }
  }

  if (!Line && NameRef.empty())
    return PointerIntPair<DeclContext *, 1>(nullptr);

  // We hash NameRef, which is the mangled name, in order to get most
  // overloaded functions resolve correctly.
  //
  // Strictly speaking, hashing the Tag is only necessary for a
  // DW_TAG_module, to prevent uniquing of a module and a namespace
  // with the same name.
  //
  // FIXME: dsymutil-classic won't unique the same type presented
  // once as a struct and once as a class. Using the Tag in the fully
  // qualified name hash to get the same effect.
  unsigned Hash = hash_combine(Context.getQualifiedNameHash(), Tag, NameRef);

  // FIXME: dsymutil-classic compatibility: when we don't have a name,
  // use the filename.
  if (IsAnonymousNamespace)
    Hash = hash_combine(Hash, FileRef);

  // Now look if this context already exists.
  DeclContext Key(Hash, Line, ByteSize, Tag, NameRef, FileRef, Context);
  auto ContextIter = Contexts.find(&Key);

  if (ContextIter == Contexts.end()) {
    // The context wasn't found.
    bool Inserted;
    DeclContext *NewContext =
        new (Allocator) DeclContext(Hash, Line, ByteSize, Tag, NameRef, FileRef,
                                    Context, DIE, U.getUniqueID());
    std::tie(ContextIter, Inserted) = Contexts.insert(NewContext);
    assert(Inserted && "Failed to insert DeclContext");
    (void)Inserted;
  } else if (Tag != dwarf::DW_TAG_namespace &&
             !(*ContextIter)->setLastSeenDIE(U, DIE)) {
    // The context was found, but it is ambiguous with another context
    // in the same file. Mark it invalid.
    return PointerIntPair<DeclContext *, 1>(*ContextIter, /* Invalid= */ 1);
  }

  assert(ContextIter != Contexts.end());
  // FIXME: dsymutil-classic compatibility. Union types aren't
  // uniques, but their children might be.
  if ((Tag == dwarf::DW_TAG_subprogram &&
       Context.getTag() != dwarf::DW_TAG_structure_type &&
       Context.getTag() != dwarf::DW_TAG_class_type) ||
      (Tag == dwarf::DW_TAG_union_type))
    return PointerIntPair<DeclContext *, 1>(*ContextIter, /* Invalid= */ 1);

  return PointerIntPair<DeclContext *, 1>(*ContextIter);
}

StringRef
DeclContextTree::getResolvedPath(CompileUnit &CU, unsigned FileNum,
                                 const DWARFDebugLine::LineTable &LineTable) {
  std::pair<unsigned, unsigned> Key = {CU.getUniqueID(), FileNum};

  ResolvedPathsMap::const_iterator It = ResolvedPaths.find(Key);
  if (It == ResolvedPaths.end()) {
    std::string FileName;
    bool FoundFileName = LineTable.getFileNameByIndex(
        FileNum, CU.getOrigUnit().getCompilationDir(),
        DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, FileName);
    (void)FoundFileName;
    assert(FoundFileName && "Must get file name from line table");

    // Second level of caching, this time based on the file's parent
    // path.
    StringRef ResolvedPath = PathResolver.resolve(FileName, StringPool);

    It = ResolvedPaths.insert(std::make_pair(Key, ResolvedPath)).first;
  }

  return It->second;
}

} // namespace llvm