ItaniumManglingCanonicalizer.cpp 10 KB

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