ItaniumManglingCanonicalizer.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. //===----------------- ItaniumManglingCanonicalizer.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. #include "llvm/Support/ItaniumManglingCanonicalizer.h"
  9. #include "llvm/ADT/DenseMap.h"
  10. #include "llvm/ADT/FoldingSet.h"
  11. #include "llvm/ADT/StringRef.h"
  12. #include "llvm/Demangle/ItaniumDemangle.h"
  13. #include "llvm/Support/Allocator.h"
  14. using namespace llvm;
  15. using llvm::itanium_demangle::ForwardTemplateReference;
  16. using llvm::itanium_demangle::Node;
  17. using llvm::itanium_demangle::NodeKind;
  18. using llvm::itanium_demangle::StringView;
  19. namespace {
  20. struct FoldingSetNodeIDBuilder {
  21. llvm::FoldingSetNodeID &ID;
  22. void operator()(const Node *P) { ID.AddPointer(P); }
  23. void operator()(StringView Str) {
  24. ID.AddString(llvm::StringRef(Str.begin(), Str.size()));
  25. }
  26. template <typename T>
  27. std::enable_if_t<std::is_integral<T>::value || std::is_enum<T>::value>
  28. operator()(T V) {
  29. ID.AddInteger((unsigned long long)V);
  30. }
  31. void operator()(itanium_demangle::NodeArray A) {
  32. ID.AddInteger(A.size());
  33. for (const Node *N : A)
  34. (*this)(N);
  35. }
  36. };
  37. template<typename ...T>
  38. void profileCtor(llvm::FoldingSetNodeID &ID, Node::Kind K, T ...V) {
  39. FoldingSetNodeIDBuilder Builder = {ID};
  40. Builder(K);
  41. int VisitInOrder[] = {
  42. (Builder(V), 0) ...,
  43. 0 // Avoid empty array if there are no arguments.
  44. };
  45. (void)VisitInOrder;
  46. }
  47. // FIXME: Convert this to a generic lambda when possible.
  48. template<typename NodeT> struct ProfileSpecificNode {
  49. FoldingSetNodeID &ID;
  50. template<typename ...T> void operator()(T ...V) {
  51. profileCtor(ID, NodeKind<NodeT>::Kind, V...);
  52. }
  53. };
  54. struct ProfileNode {
  55. FoldingSetNodeID &ID;
  56. template<typename NodeT> void operator()(const NodeT *N) {
  57. N->match(ProfileSpecificNode<NodeT>{ID});
  58. }
  59. };
  60. template<> void ProfileNode::operator()(const ForwardTemplateReference *N) {
  61. llvm_unreachable("should never canonicalize a ForwardTemplateReference");
  62. }
  63. void profileNode(llvm::FoldingSetNodeID &ID, const Node *N) {
  64. N->visit(ProfileNode{ID});
  65. }
  66. class FoldingNodeAllocator {
  67. class alignas(alignof(Node *)) NodeHeader : public llvm::FoldingSetNode {
  68. public:
  69. // 'Node' in this context names the injected-class-name of the base class.
  70. itanium_demangle::Node *getNode() {
  71. return reinterpret_cast<itanium_demangle::Node *>(this + 1);
  72. }
  73. void Profile(llvm::FoldingSetNodeID &ID) { profileNode(ID, getNode()); }
  74. };
  75. BumpPtrAllocator RawAlloc;
  76. llvm::FoldingSet<NodeHeader> Nodes;
  77. public:
  78. void reset() {}
  79. template <typename T, typename... Args>
  80. std::pair<Node *, bool> getOrCreateNode(bool CreateNewNodes, Args &&... As) {
  81. // FIXME: Don't canonicalize forward template references for now, because
  82. // they contain state (the resolved template node) that's not known at their
  83. // point of creation.
  84. if (std::is_same<T, ForwardTemplateReference>::value) {
  85. // Note that we don't use if-constexpr here and so we must still write
  86. // this code in a generic form.
  87. return {new (RawAlloc.Allocate(sizeof(T), alignof(T)))
  88. T(std::forward<Args>(As)...),
  89. true};
  90. }
  91. llvm::FoldingSetNodeID ID;
  92. profileCtor(ID, NodeKind<T>::Kind, As...);
  93. void *InsertPos;
  94. if (NodeHeader *Existing = Nodes.FindNodeOrInsertPos(ID, InsertPos))
  95. return {static_cast<T*>(Existing->getNode()), false};
  96. if (!CreateNewNodes)
  97. return {nullptr, true};
  98. static_assert(alignof(T) <= alignof(NodeHeader),
  99. "underaligned node header for specific node kind");
  100. void *Storage =
  101. RawAlloc.Allocate(sizeof(NodeHeader) + sizeof(T), alignof(NodeHeader));
  102. NodeHeader *New = new (Storage) NodeHeader;
  103. T *Result = new (New->getNode()) T(std::forward<Args>(As)...);
  104. Nodes.InsertNode(New, InsertPos);
  105. return {Result, true};
  106. }
  107. template<typename T, typename... Args>
  108. Node *makeNode(Args &&...As) {
  109. return getOrCreateNode<T>(true, std::forward<Args>(As)...).first;
  110. }
  111. void *allocateNodeArray(size_t sz) {
  112. return RawAlloc.Allocate(sizeof(Node *) * sz, alignof(Node *));
  113. }
  114. };
  115. class CanonicalizerAllocator : public FoldingNodeAllocator {
  116. Node *MostRecentlyCreated = nullptr;
  117. Node *TrackedNode = nullptr;
  118. bool TrackedNodeIsUsed = false;
  119. bool CreateNewNodes = true;
  120. llvm::SmallDenseMap<Node*, Node*, 32> Remappings;
  121. template<typename T, typename ...Args> Node *makeNodeSimple(Args &&...As) {
  122. std::pair<Node *, bool> Result =
  123. getOrCreateNode<T>(CreateNewNodes, std::forward<Args>(As)...);
  124. if (Result.second) {
  125. // Node is new. Make a note of that.
  126. MostRecentlyCreated = Result.first;
  127. } else if (Result.first) {
  128. // Node is pre-existing; check if it's in our remapping table.
  129. if (auto *N = Remappings.lookup(Result.first)) {
  130. Result.first = N;
  131. assert(Remappings.find(Result.first) == Remappings.end() &&
  132. "should never need multiple remap steps");
  133. }
  134. if (Result.first == TrackedNode)
  135. TrackedNodeIsUsed = true;
  136. }
  137. return Result.first;
  138. }
  139. /// Helper to allow makeNode to be partially-specialized on T.
  140. template<typename T> struct MakeNodeImpl {
  141. CanonicalizerAllocator &Self;
  142. template<typename ...Args> Node *make(Args &&...As) {
  143. return Self.makeNodeSimple<T>(std::forward<Args>(As)...);
  144. }
  145. };
  146. public:
  147. template<typename T, typename ...Args> Node *makeNode(Args &&...As) {
  148. return MakeNodeImpl<T>{*this}.make(std::forward<Args>(As)...);
  149. }
  150. void reset() { MostRecentlyCreated = nullptr; }
  151. void setCreateNewNodes(bool CNN) { CreateNewNodes = CNN; }
  152. void addRemapping(Node *A, Node *B) {
  153. // Note, we don't need to check whether B is also remapped, because if it
  154. // was we would have already remapped it when building it.
  155. Remappings.insert(std::make_pair(A, B));
  156. }
  157. bool isMostRecentlyCreated(Node *N) const { return MostRecentlyCreated == N; }
  158. void trackUsesOf(Node *N) {
  159. TrackedNode = N;
  160. TrackedNodeIsUsed = false;
  161. }
  162. bool trackedNodeIsUsed() const { return TrackedNodeIsUsed; }
  163. };
  164. /// Convert St3foo to NSt3fooE so that equivalences naming one also affect the
  165. /// other.
  166. template<>
  167. struct CanonicalizerAllocator::MakeNodeImpl<
  168. itanium_demangle::StdQualifiedName> {
  169. CanonicalizerAllocator &Self;
  170. Node *make(Node *Child) {
  171. Node *StdNamespace = Self.makeNode<itanium_demangle::NameType>("std");
  172. if (!StdNamespace)
  173. return nullptr;
  174. return Self.makeNode<itanium_demangle::NestedName>(StdNamespace, Child);
  175. }
  176. };
  177. // FIXME: Also expand built-in substitutions?
  178. using CanonicalizingDemangler =
  179. itanium_demangle::ManglingParser<CanonicalizerAllocator>;
  180. } // namespace
  181. struct ItaniumManglingCanonicalizer::Impl {
  182. CanonicalizingDemangler Demangler = {nullptr, nullptr};
  183. };
  184. ItaniumManglingCanonicalizer::ItaniumManglingCanonicalizer() : P(new Impl) {}
  185. ItaniumManglingCanonicalizer::~ItaniumManglingCanonicalizer() { delete P; }
  186. ItaniumManglingCanonicalizer::EquivalenceError
  187. ItaniumManglingCanonicalizer::addEquivalence(FragmentKind Kind, StringRef First,
  188. StringRef Second) {
  189. auto &Alloc = P->Demangler.ASTAllocator;
  190. Alloc.setCreateNewNodes(true);
  191. auto Parse = [&](StringRef Str) {
  192. P->Demangler.reset(Str.begin(), Str.end());
  193. Node *N = nullptr;
  194. switch (Kind) {
  195. // A <name>, with minor extensions to allow arbitrary namespace and
  196. // template names that can't easily be written as <name>s.
  197. case FragmentKind::Name:
  198. // Very special case: allow "St" as a shorthand for "3std". It's not
  199. // valid as a <name> mangling, but is nonetheless the most natural
  200. // way to name the 'std' namespace.
  201. if (Str.size() == 2 && P->Demangler.consumeIf("St"))
  202. N = P->Demangler.make<itanium_demangle::NameType>("std");
  203. // We permit substitutions to name templates without their template
  204. // arguments. This mostly just falls out, as almost all template names
  205. // are valid as <name>s, but we also want to parse <substitution>s as
  206. // <name>s, even though they're not.
  207. else if (Str.startswith("S"))
  208. // Parse the substitution and optional following template arguments.
  209. N = P->Demangler.parseType();
  210. else
  211. N = P->Demangler.parseName();
  212. break;
  213. // A <type>.
  214. case FragmentKind::Type:
  215. N = P->Demangler.parseType();
  216. break;
  217. // An <encoding>.
  218. case FragmentKind::Encoding:
  219. N = P->Demangler.parseEncoding();
  220. break;
  221. }
  222. // If we have trailing junk, the mangling is invalid.
  223. if (P->Demangler.numLeft() != 0)
  224. N = nullptr;
  225. // If any node was created after N, then we cannot safely remap it because
  226. // it might already be in use by another node.
  227. return std::make_pair(N, Alloc.isMostRecentlyCreated(N));
  228. };
  229. Node *FirstNode, *SecondNode;
  230. bool FirstIsNew, SecondIsNew;
  231. std::tie(FirstNode, FirstIsNew) = Parse(First);
  232. if (!FirstNode)
  233. return EquivalenceError::InvalidFirstMangling;
  234. Alloc.trackUsesOf(FirstNode);
  235. std::tie(SecondNode, SecondIsNew) = Parse(Second);
  236. if (!SecondNode)
  237. return EquivalenceError::InvalidSecondMangling;
  238. // If they're already equivalent, there's nothing to do.
  239. if (FirstNode == SecondNode)
  240. return EquivalenceError::Success;
  241. if (FirstIsNew && !Alloc.trackedNodeIsUsed())
  242. Alloc.addRemapping(FirstNode, SecondNode);
  243. else if (SecondIsNew)
  244. Alloc.addRemapping(SecondNode, FirstNode);
  245. else
  246. return EquivalenceError::ManglingAlreadyUsed;
  247. return EquivalenceError::Success;
  248. }
  249. static ItaniumManglingCanonicalizer::Key
  250. parseMaybeMangledName(CanonicalizingDemangler &Demangler, StringRef Mangling,
  251. bool CreateNewNodes) {
  252. Demangler.ASTAllocator.setCreateNewNodes(CreateNewNodes);
  253. Demangler.reset(Mangling.begin(), Mangling.end());
  254. // Attempt demangling only for names that look like C++ mangled names.
  255. // Otherwise, treat them as extern "C" names. We permit the latter to
  256. // be remapped by (eg)
  257. // encoding 6memcpy 7memmove
  258. // consistent with how they are encoded as local-names inside a C++ mangling.
  259. Node *N;
  260. if (Mangling.startswith("_Z") || Mangling.startswith("__Z") ||
  261. Mangling.startswith("___Z") || Mangling.startswith("____Z"))
  262. N = Demangler.parse();
  263. else
  264. N = Demangler.make<itanium_demangle::NameType>(
  265. StringView(Mangling.data(), Mangling.size()));
  266. return reinterpret_cast<ItaniumManglingCanonicalizer::Key>(N);
  267. }
  268. ItaniumManglingCanonicalizer::Key
  269. ItaniumManglingCanonicalizer::canonicalize(StringRef Mangling) {
  270. return parseMaybeMangledName(P->Demangler, Mangling, true);
  271. }
  272. ItaniumManglingCanonicalizer::Key
  273. ItaniumManglingCanonicalizer::lookup(StringRef Mangling) {
  274. return parseMaybeMangledName(P->Demangler, Mangling, false);
  275. }