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