Path: blob/main/contrib/llvm-project/llvm/lib/ProfileData/ItaniumManglingCanonicalizer.cpp
35233 views
//===----------------- ItaniumManglingCanonicalizer.cpp -------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "llvm/ProfileData/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"1415using namespace llvm;16using llvm::itanium_demangle::ForwardTemplateReference;17using llvm::itanium_demangle::Node;18using llvm::itanium_demangle::NodeKind;1920namespace {21struct FoldingSetNodeIDBuilder {22llvm::FoldingSetNodeID &ID;23void operator()(const Node *P) { ID.AddPointer(P); }24void operator()(std::string_view Str) {25if (Str.empty())26ID.AddString({});27else28ID.AddString(llvm::StringRef(&*Str.begin(), Str.size()));29}30template <typename T>31std::enable_if_t<std::is_integral_v<T> || std::is_enum_v<T>> operator()(T V) {32ID.AddInteger((unsigned long long)V);33}34void operator()(itanium_demangle::NodeArray A) {35ID.AddInteger(A.size());36for (const Node *N : A)37(*this)(N);38}39};4041template<typename ...T>42void profileCtor(llvm::FoldingSetNodeID &ID, Node::Kind K, T ...V) {43FoldingSetNodeIDBuilder Builder = {ID};44Builder(K);45int VisitInOrder[] = {46(Builder(V), 0) ...,470 // Avoid empty array if there are no arguments.48};49(void)VisitInOrder;50}5152// FIXME: Convert this to a generic lambda when possible.53template<typename NodeT> struct ProfileSpecificNode {54FoldingSetNodeID &ID;55template<typename ...T> void operator()(T ...V) {56profileCtor(ID, NodeKind<NodeT>::Kind, V...);57}58};5960struct ProfileNode {61FoldingSetNodeID &ID;62template<typename NodeT> void operator()(const NodeT *N) {63N->match(ProfileSpecificNode<NodeT>{ID});64}65};6667template<> void ProfileNode::operator()(const ForwardTemplateReference *N) {68llvm_unreachable("should never canonicalize a ForwardTemplateReference");69}7071void profileNode(llvm::FoldingSetNodeID &ID, const Node *N) {72N->visit(ProfileNode{ID});73}7475class FoldingNodeAllocator {76class alignas(alignof(Node *)) NodeHeader : public llvm::FoldingSetNode {77public:78// 'Node' in this context names the injected-class-name of the base class.79itanium_demangle::Node *getNode() {80return reinterpret_cast<itanium_demangle::Node *>(this + 1);81}82void Profile(llvm::FoldingSetNodeID &ID) { profileNode(ID, getNode()); }83};8485BumpPtrAllocator RawAlloc;86llvm::FoldingSet<NodeHeader> Nodes;8788public:89void reset() {}9091template <typename T, typename... Args>92std::pair<Node *, bool> getOrCreateNode(bool CreateNewNodes, Args &&... As) {93// FIXME: Don't canonicalize forward template references for now, because94// they contain state (the resolved template node) that's not known at their95// point of creation.96if (std::is_same<T, ForwardTemplateReference>::value) {97// Note that we don't use if-constexpr here and so we must still write98// this code in a generic form.99return {new (RawAlloc.Allocate(sizeof(T), alignof(T)))100T(std::forward<Args>(As)...),101true};102}103104llvm::FoldingSetNodeID ID;105profileCtor(ID, NodeKind<T>::Kind, As...);106107void *InsertPos;108if (NodeHeader *Existing = Nodes.FindNodeOrInsertPos(ID, InsertPos))109return {static_cast<T*>(Existing->getNode()), false};110111if (!CreateNewNodes)112return {nullptr, true};113114static_assert(alignof(T) <= alignof(NodeHeader),115"underaligned node header for specific node kind");116void *Storage =117RawAlloc.Allocate(sizeof(NodeHeader) + sizeof(T), alignof(NodeHeader));118NodeHeader *New = new (Storage) NodeHeader;119T *Result = new (New->getNode()) T(std::forward<Args>(As)...);120Nodes.InsertNode(New, InsertPos);121return {Result, true};122}123124template<typename T, typename... Args>125Node *makeNode(Args &&...As) {126return getOrCreateNode<T>(true, std::forward<Args>(As)...).first;127}128129void *allocateNodeArray(size_t sz) {130return RawAlloc.Allocate(sizeof(Node *) * sz, alignof(Node *));131}132};133134class CanonicalizerAllocator : public FoldingNodeAllocator {135Node *MostRecentlyCreated = nullptr;136Node *TrackedNode = nullptr;137bool TrackedNodeIsUsed = false;138bool CreateNewNodes = true;139llvm::SmallDenseMap<Node*, Node*, 32> Remappings;140141template<typename T, typename ...Args> Node *makeNodeSimple(Args &&...As) {142std::pair<Node *, bool> Result =143getOrCreateNode<T>(CreateNewNodes, std::forward<Args>(As)...);144if (Result.second) {145// Node is new. Make a note of that.146MostRecentlyCreated = Result.first;147} else if (Result.first) {148// Node is pre-existing; check if it's in our remapping table.149if (auto *N = Remappings.lookup(Result.first)) {150Result.first = N;151assert(!Remappings.contains(Result.first) &&152"should never need multiple remap steps");153}154if (Result.first == TrackedNode)155TrackedNodeIsUsed = true;156}157return Result.first;158}159160/// Helper to allow makeNode to be partially-specialized on T.161template<typename T> struct MakeNodeImpl {162CanonicalizerAllocator &Self;163template<typename ...Args> Node *make(Args &&...As) {164return Self.makeNodeSimple<T>(std::forward<Args>(As)...);165}166};167168public:169template<typename T, typename ...Args> Node *makeNode(Args &&...As) {170return MakeNodeImpl<T>{*this}.make(std::forward<Args>(As)...);171}172173void reset() { MostRecentlyCreated = nullptr; }174175void setCreateNewNodes(bool CNN) { CreateNewNodes = CNN; }176177void addRemapping(Node *A, Node *B) {178// Note, we don't need to check whether B is also remapped, because if it179// was we would have already remapped it when building it.180Remappings.insert(std::make_pair(A, B));181}182183bool isMostRecentlyCreated(Node *N) const { return MostRecentlyCreated == N; }184185void trackUsesOf(Node *N) {186TrackedNode = N;187TrackedNodeIsUsed = false;188}189bool trackedNodeIsUsed() const { return TrackedNodeIsUsed; }190};191192// FIXME: Also expand built-in substitutions?193194using CanonicalizingDemangler =195itanium_demangle::ManglingParser<CanonicalizerAllocator>;196} // namespace197198struct ItaniumManglingCanonicalizer::Impl {199CanonicalizingDemangler Demangler = {nullptr, nullptr};200};201202ItaniumManglingCanonicalizer::ItaniumManglingCanonicalizer() : P(new Impl) {}203ItaniumManglingCanonicalizer::~ItaniumManglingCanonicalizer() { delete P; }204205ItaniumManglingCanonicalizer::EquivalenceError206ItaniumManglingCanonicalizer::addEquivalence(FragmentKind Kind, StringRef First,207StringRef Second) {208auto &Alloc = P->Demangler.ASTAllocator;209Alloc.setCreateNewNodes(true);210211auto Parse = [&](StringRef Str) {212P->Demangler.reset(Str.begin(), Str.end());213Node *N = nullptr;214switch (Kind) {215// A <name>, with minor extensions to allow arbitrary namespace and216// template names that can't easily be written as <name>s.217case FragmentKind::Name:218// Very special case: allow "St" as a shorthand for "3std". It's not219// valid as a <name> mangling, but is nonetheless the most natural220// way to name the 'std' namespace.221if (Str.size() == 2 && P->Demangler.consumeIf("St"))222N = P->Demangler.make<itanium_demangle::NameType>("std");223// We permit substitutions to name templates without their template224// arguments. This mostly just falls out, as almost all template names225// are valid as <name>s, but we also want to parse <substitution>s as226// <name>s, even though they're not.227else if (Str.starts_with("S"))228// Parse the substitution and optional following template arguments.229N = P->Demangler.parseType();230else231N = P->Demangler.parseName();232break;233234// A <type>.235case FragmentKind::Type:236N = P->Demangler.parseType();237break;238239// An <encoding>.240case FragmentKind::Encoding:241N = P->Demangler.parseEncoding();242break;243}244245// If we have trailing junk, the mangling is invalid.246if (P->Demangler.numLeft() != 0)247N = nullptr;248249// If any node was created after N, then we cannot safely remap it because250// it might already be in use by another node.251return std::make_pair(N, Alloc.isMostRecentlyCreated(N));252};253254Node *FirstNode, *SecondNode;255bool FirstIsNew, SecondIsNew;256257std::tie(FirstNode, FirstIsNew) = Parse(First);258if (!FirstNode)259return EquivalenceError::InvalidFirstMangling;260261Alloc.trackUsesOf(FirstNode);262std::tie(SecondNode, SecondIsNew) = Parse(Second);263if (!SecondNode)264return EquivalenceError::InvalidSecondMangling;265266// If they're already equivalent, there's nothing to do.267if (FirstNode == SecondNode)268return EquivalenceError::Success;269270if (FirstIsNew && !Alloc.trackedNodeIsUsed())271Alloc.addRemapping(FirstNode, SecondNode);272else if (SecondIsNew)273Alloc.addRemapping(SecondNode, FirstNode);274else275return EquivalenceError::ManglingAlreadyUsed;276277return EquivalenceError::Success;278}279280static ItaniumManglingCanonicalizer::Key281parseMaybeMangledName(CanonicalizingDemangler &Demangler, StringRef Mangling,282bool CreateNewNodes) {283Demangler.ASTAllocator.setCreateNewNodes(CreateNewNodes);284Demangler.reset(Mangling.begin(), Mangling.end());285// Attempt demangling only for names that look like C++ mangled names.286// Otherwise, treat them as extern "C" names. We permit the latter to287// be remapped by (eg)288// encoding 6memcpy 7memmove289// consistent with how they are encoded as local-names inside a C++ mangling.290Node *N;291if (Mangling.starts_with("_Z") || Mangling.starts_with("__Z") ||292Mangling.starts_with("___Z") || Mangling.starts_with("____Z"))293N = Demangler.parse();294else295N = Demangler.make<itanium_demangle::NameType>(296std::string_view(Mangling.data(), Mangling.size()));297return reinterpret_cast<ItaniumManglingCanonicalizer::Key>(N);298}299300ItaniumManglingCanonicalizer::Key301ItaniumManglingCanonicalizer::canonicalize(StringRef Mangling) {302return parseMaybeMangledName(P->Demangler, Mangling, true);303}304305ItaniumManglingCanonicalizer::Key306ItaniumManglingCanonicalizer::lookup(StringRef Mangling) {307return parseMaybeMangledName(P->Demangler, Mangling, false);308}309310311