diff options
Diffstat (limited to 'llvm/lib/Support/ItaniumManglingCanonicalizer.cpp')
-rw-r--r-- | llvm/lib/Support/ItaniumManglingCanonicalizer.cpp | 322 |
1 files changed, 322 insertions, 0 deletions
diff --git a/llvm/lib/Support/ItaniumManglingCanonicalizer.cpp b/llvm/lib/Support/ItaniumManglingCanonicalizer.cpp new file mode 100644 index 000000000000..da6514f7170b --- /dev/null +++ b/llvm/lib/Support/ItaniumManglingCanonicalizer.cpp @@ -0,0 +1,322 @@ +//===----------------- ItaniumManglingCanonicalizer.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/Support/ItaniumManglingCanonicalizer.h" + +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Demangle/ItaniumDemangle.h" +#include "llvm/Support/Allocator.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/StringRef.h" + +using namespace llvm; +using llvm::itanium_demangle::ForwardTemplateReference; +using llvm::itanium_demangle::Node; +using llvm::itanium_demangle::NodeKind; +using llvm::itanium_demangle::StringView; + +namespace { +struct FoldingSetNodeIDBuilder { + llvm::FoldingSetNodeID &ID; + void operator()(const Node *P) { ID.AddPointer(P); } + void operator()(StringView Str) { + ID.AddString(llvm::StringRef(Str.begin(), Str.size())); + } + template<typename T> + typename std::enable_if<std::is_integral<T>::value || + std::is_enum<T>::value>::type + operator()(T V) { + ID.AddInteger((unsigned long long)V); + } + void operator()(itanium_demangle::NodeOrString NS) { + if (NS.isNode()) { + ID.AddInteger(0); + (*this)(NS.asNode()); + } else if (NS.isString()) { + ID.AddInteger(1); + (*this)(NS.asString()); + } else { + ID.AddInteger(2); + } + } + void operator()(itanium_demangle::NodeArray A) { + ID.AddInteger(A.size()); + for (const Node *N : A) + (*this)(N); + } +}; + +template<typename ...T> +void profileCtor(llvm::FoldingSetNodeID &ID, Node::Kind K, T ...V) { + FoldingSetNodeIDBuilder Builder = {ID}; + Builder(K); + int VisitInOrder[] = { + (Builder(V), 0) ..., + 0 // Avoid empty array if there are no arguments. + }; + (void)VisitInOrder; +} + +// FIXME: Convert this to a generic lambda when possible. +template<typename NodeT> struct ProfileSpecificNode { + FoldingSetNodeID &ID; + template<typename ...T> void operator()(T ...V) { + profileCtor(ID, NodeKind<NodeT>::Kind, V...); + } +}; + +struct ProfileNode { + FoldingSetNodeID &ID; + template<typename NodeT> void operator()(const NodeT *N) { + N->match(ProfileSpecificNode<NodeT>{ID}); + } +}; + +template<> void ProfileNode::operator()(const ForwardTemplateReference *N) { + llvm_unreachable("should never canonicalize a ForwardTemplateReference"); +} + +void profileNode(llvm::FoldingSetNodeID &ID, const Node *N) { + N->visit(ProfileNode{ID}); +} + +class FoldingNodeAllocator { + class alignas(alignof(Node *)) NodeHeader : public llvm::FoldingSetNode { + public: + // 'Node' in this context names the injected-class-name of the base class. + itanium_demangle::Node *getNode() { + return reinterpret_cast<itanium_demangle::Node *>(this + 1); + } + void Profile(llvm::FoldingSetNodeID &ID) { profileNode(ID, getNode()); } + }; + + BumpPtrAllocator RawAlloc; + llvm::FoldingSet<NodeHeader> Nodes; + +public: + void reset() {} + + template <typename T, typename... Args> + std::pair<Node *, bool> getOrCreateNode(bool CreateNewNodes, Args &&... As) { + // FIXME: Don't canonicalize forward template references for now, because + // they contain state (the resolved template node) that's not known at their + // point of creation. + if (std::is_same<T, ForwardTemplateReference>::value) { + // Note that we don't use if-constexpr here and so we must still write + // this code in a generic form. + return {new (RawAlloc.Allocate(sizeof(T), alignof(T))) + T(std::forward<Args>(As)...), + true}; + } + + llvm::FoldingSetNodeID ID; + profileCtor(ID, NodeKind<T>::Kind, As...); + + void *InsertPos; + if (NodeHeader *Existing = Nodes.FindNodeOrInsertPos(ID, InsertPos)) + return {static_cast<T*>(Existing->getNode()), false}; + + if (!CreateNewNodes) + return {nullptr, true}; + + static_assert(alignof(T) <= alignof(NodeHeader), + "underaligned node header for specific node kind"); + void *Storage = + RawAlloc.Allocate(sizeof(NodeHeader) + sizeof(T), alignof(NodeHeader)); + NodeHeader *New = new (Storage) NodeHeader; + T *Result = new (New->getNode()) T(std::forward<Args>(As)...); + Nodes.InsertNode(New, InsertPos); + return {Result, true}; + } + + template<typename T, typename... Args> + Node *makeNode(Args &&...As) { + return getOrCreateNode<T>(true, std::forward<Args>(As)...).first; + } + + void *allocateNodeArray(size_t sz) { + return RawAlloc.Allocate(sizeof(Node *) * sz, alignof(Node *)); + } +}; + +class CanonicalizerAllocator : public FoldingNodeAllocator { + Node *MostRecentlyCreated = nullptr; + Node *TrackedNode = nullptr; + bool TrackedNodeIsUsed = false; + bool CreateNewNodes = true; + llvm::SmallDenseMap<Node*, Node*, 32> Remappings; + + template<typename T, typename ...Args> Node *makeNodeSimple(Args &&...As) { + std::pair<Node *, bool> Result = + getOrCreateNode<T>(CreateNewNodes, std::forward<Args>(As)...); + if (Result.second) { + // Node is new. Make a note of that. + MostRecentlyCreated = Result.first; + } else if (Result.first) { + // Node is pre-existing; check if it's in our remapping table. + if (auto *N = Remappings.lookup(Result.first)) { + Result.first = N; + assert(Remappings.find(Result.first) == Remappings.end() && + "should never need multiple remap steps"); + } + if (Result.first == TrackedNode) + TrackedNodeIsUsed = true; + } + return Result.first; + } + + /// Helper to allow makeNode to be partially-specialized on T. + template<typename T> struct MakeNodeImpl { + CanonicalizerAllocator &Self; + template<typename ...Args> Node *make(Args &&...As) { + return Self.makeNodeSimple<T>(std::forward<Args>(As)...); + } + }; + +public: + template<typename T, typename ...Args> Node *makeNode(Args &&...As) { + return MakeNodeImpl<T>{*this}.make(std::forward<Args>(As)...); + } + + void reset() { MostRecentlyCreated = nullptr; } + + void setCreateNewNodes(bool CNN) { CreateNewNodes = CNN; } + + void addRemapping(Node *A, Node *B) { + // Note, we don't need to check whether B is also remapped, because if it + // was we would have already remapped it when building it. + Remappings.insert(std::make_pair(A, B)); + } + + bool isMostRecentlyCreated(Node *N) const { return MostRecentlyCreated == N; } + + void trackUsesOf(Node *N) { + TrackedNode = N; + TrackedNodeIsUsed = false; + } + bool trackedNodeIsUsed() const { return TrackedNodeIsUsed; } +}; + +/// Convert St3foo to NSt3fooE so that equivalences naming one also affect the +/// other. +template<> +struct CanonicalizerAllocator::MakeNodeImpl< + itanium_demangle::StdQualifiedName> { + CanonicalizerAllocator &Self; + Node *make(Node *Child) { + Node *StdNamespace = Self.makeNode<itanium_demangle::NameType>("std"); + if (!StdNamespace) + return nullptr; + return Self.makeNode<itanium_demangle::NestedName>(StdNamespace, Child); + } +}; + +// FIXME: Also expand built-in substitutions? + +using CanonicalizingDemangler = + itanium_demangle::ManglingParser<CanonicalizerAllocator>; +} + +struct ItaniumManglingCanonicalizer::Impl { + CanonicalizingDemangler Demangler = {nullptr, nullptr}; +}; + +ItaniumManglingCanonicalizer::ItaniumManglingCanonicalizer() : P(new Impl) {} +ItaniumManglingCanonicalizer::~ItaniumManglingCanonicalizer() { delete P; } + +ItaniumManglingCanonicalizer::EquivalenceError +ItaniumManglingCanonicalizer::addEquivalence(FragmentKind Kind, StringRef First, + StringRef Second) { + auto &Alloc = P->Demangler.ASTAllocator; + Alloc.setCreateNewNodes(true); + + auto Parse = [&](StringRef Str) { + P->Demangler.reset(Str.begin(), Str.end()); + Node *N = nullptr; + switch (Kind) { + // A <name>, with minor extensions to allow arbitrary namespace and + // template names that can't easily be written as <name>s. + case FragmentKind::Name: + // Very special case: allow "St" as a shorthand for "3std". It's not + // valid as a <name> mangling, but is nonetheless the most natural + // way to name the 'std' namespace. + if (Str.size() == 2 && P->Demangler.consumeIf("St")) + N = P->Demangler.make<itanium_demangle::NameType>("std"); + // We permit substitutions to name templates without their template + // arguments. This mostly just falls out, as almost all template names + // are valid as <name>s, but we also want to parse <substitution>s as + // <name>s, even though they're not. + else if (Str.startswith("S")) + // Parse the substitution and optional following template arguments. + N = P->Demangler.parseType(); + else + N = P->Demangler.parseName(); + break; + + // A <type>. + case FragmentKind::Type: + N = P->Demangler.parseType(); + break; + + // An <encoding>. + case FragmentKind::Encoding: + N = P->Demangler.parseEncoding(); + break; + } + + // If we have trailing junk, the mangling is invalid. + if (P->Demangler.numLeft() != 0) + N = nullptr; + + // If any node was created after N, then we cannot safely remap it because + // it might already be in use by another node. + return std::make_pair(N, Alloc.isMostRecentlyCreated(N)); + }; + + Node *FirstNode, *SecondNode; + bool FirstIsNew, SecondIsNew; + + std::tie(FirstNode, FirstIsNew) = Parse(First); + if (!FirstNode) + return EquivalenceError::InvalidFirstMangling; + + Alloc.trackUsesOf(FirstNode); + std::tie(SecondNode, SecondIsNew) = Parse(Second); + if (!SecondNode) + return EquivalenceError::InvalidSecondMangling; + + // If they're already equivalent, there's nothing to do. + if (FirstNode == SecondNode) + return EquivalenceError::Success; + + if (FirstIsNew && !Alloc.trackedNodeIsUsed()) + Alloc.addRemapping(FirstNode, SecondNode); + else if (SecondIsNew) + Alloc.addRemapping(SecondNode, FirstNode); + else + return EquivalenceError::ManglingAlreadyUsed; + + return EquivalenceError::Success; +} + +ItaniumManglingCanonicalizer::Key +ItaniumManglingCanonicalizer::canonicalize(StringRef Mangling) { + P->Demangler.ASTAllocator.setCreateNewNodes(true); + P->Demangler.reset(Mangling.begin(), Mangling.end()); + return reinterpret_cast<Key>(P->Demangler.parse()); +} + +ItaniumManglingCanonicalizer::Key +ItaniumManglingCanonicalizer::lookup(StringRef Mangling) { + P->Demangler.ASTAllocator.setCreateNewNodes(false); + P->Demangler.reset(Mangling.begin(), Mangling.end()); + return reinterpret_cast<Key>(P->Demangler.parse()); +} |