1 //===----------------- ItaniumManglingCanonicalizer.cpp -------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
17 using llvm::itanium_demangle::ForwardTemplateReference
;
18 using llvm::itanium_demangle::Node
;
19 using llvm::itanium_demangle::NodeKind
;
22 struct FoldingSetNodeIDBuilder
{
23 llvm::FoldingSetNodeID
&ID
;
24 void operator()(const Node
*P
) { ID
.AddPointer(P
); }
25 void operator()(std::string_view Str
) {
29 ID
.AddString(llvm::StringRef(&*Str
.begin(), Str
.size()));
32 std::enable_if_t
<std::is_integral_v
<T
> || std::is_enum_v
<T
>> operator()(T V
) {
33 ID
.AddInteger((unsigned long long)V
);
35 void operator()(itanium_demangle::NodeArray A
) {
36 ID
.AddInteger(A
.size());
37 for (const Node
*N
: A
)
42 template<typename
...T
>
43 void profileCtor(llvm::FoldingSetNodeID
&ID
, Node::Kind K
, T
...V
) {
44 FoldingSetNodeIDBuilder Builder
= {ID
};
46 int VisitInOrder
[] = {
48 0 // Avoid empty array if there are no arguments.
53 // FIXME: Convert this to a generic lambda when possible.
54 template<typename NodeT
> struct ProfileSpecificNode
{
56 template<typename
...T
> void operator()(T
...V
) {
57 profileCtor(ID
, NodeKind
<NodeT
>::Kind
, V
...);
63 template<typename NodeT
> void operator()(const NodeT
*N
) {
64 N
->match(ProfileSpecificNode
<NodeT
>{ID
});
68 template<> void ProfileNode::operator()(const ForwardTemplateReference
*N
) {
69 llvm_unreachable("should never canonicalize a ForwardTemplateReference");
72 void profileNode(llvm::FoldingSetNodeID
&ID
, const Node
*N
) {
73 N
->visit(ProfileNode
{ID
});
76 class FoldingNodeAllocator
{
77 class alignas(alignof(Node
*)) NodeHeader
: public llvm::FoldingSetNode
{
79 // 'Node' in this context names the injected-class-name of the base class.
80 itanium_demangle::Node
*getNode() {
81 return reinterpret_cast<itanium_demangle::Node
*>(this + 1);
83 void Profile(llvm::FoldingSetNodeID
&ID
) { profileNode(ID
, getNode()); }
86 BumpPtrAllocator RawAlloc
;
87 llvm::FoldingSet
<NodeHeader
> Nodes
;
92 template <typename T
, typename
... Args
>
93 std::pair
<Node
*, bool> getOrCreateNode(bool CreateNewNodes
, Args
&&... As
) {
94 // FIXME: Don't canonicalize forward template references for now, because
95 // they contain state (the resolved template node) that's not known at their
97 if (std::is_same
<T
, ForwardTemplateReference
>::value
) {
98 // Note that we don't use if-constexpr here and so we must still write
99 // this code in a generic form.
100 return {new (RawAlloc
.Allocate(sizeof(T
), alignof(T
)))
101 T(std::forward
<Args
>(As
)...),
105 llvm::FoldingSetNodeID ID
;
106 profileCtor(ID
, NodeKind
<T
>::Kind
, As
...);
109 if (NodeHeader
*Existing
= Nodes
.FindNodeOrInsertPos(ID
, InsertPos
))
110 return {static_cast<T
*>(Existing
->getNode()), false};
113 return {nullptr, true};
115 static_assert(alignof(T
) <= alignof(NodeHeader
),
116 "underaligned node header for specific node kind");
118 RawAlloc
.Allocate(sizeof(NodeHeader
) + sizeof(T
), alignof(NodeHeader
));
119 NodeHeader
*New
= new (Storage
) NodeHeader
;
120 T
*Result
= new (New
->getNode()) T(std::forward
<Args
>(As
)...);
121 Nodes
.InsertNode(New
, InsertPos
);
122 return {Result
, true};
125 template<typename T
, typename
... Args
>
126 Node
*makeNode(Args
&&...As
) {
127 return getOrCreateNode
<T
>(true, std::forward
<Args
>(As
)...).first
;
130 void *allocateNodeArray(size_t sz
) {
131 return RawAlloc
.Allocate(sizeof(Node
*) * sz
, alignof(Node
*));
135 class CanonicalizerAllocator
: public FoldingNodeAllocator
{
136 Node
*MostRecentlyCreated
= nullptr;
137 Node
*TrackedNode
= nullptr;
138 bool TrackedNodeIsUsed
= false;
139 bool CreateNewNodes
= true;
140 llvm::SmallDenseMap
<Node
*, Node
*, 32> Remappings
;
142 template<typename T
, typename
...Args
> Node
*makeNodeSimple(Args
&&...As
) {
143 std::pair
<Node
*, bool> Result
=
144 getOrCreateNode
<T
>(CreateNewNodes
, std::forward
<Args
>(As
)...);
146 // Node is new. Make a note of that.
147 MostRecentlyCreated
= Result
.first
;
148 } else if (Result
.first
) {
149 // Node is pre-existing; check if it's in our remapping table.
150 if (auto *N
= Remappings
.lookup(Result
.first
)) {
152 assert(!Remappings
.contains(Result
.first
) &&
153 "should never need multiple remap steps");
155 if (Result
.first
== TrackedNode
)
156 TrackedNodeIsUsed
= true;
161 /// Helper to allow makeNode to be partially-specialized on T.
162 template<typename T
> struct MakeNodeImpl
{
163 CanonicalizerAllocator
&Self
;
164 template<typename
...Args
> Node
*make(Args
&&...As
) {
165 return Self
.makeNodeSimple
<T
>(std::forward
<Args
>(As
)...);
170 template<typename T
, typename
...Args
> Node
*makeNode(Args
&&...As
) {
171 return MakeNodeImpl
<T
>{*this}.make(std::forward
<Args
>(As
)...);
174 void reset() { MostRecentlyCreated
= nullptr; }
176 void setCreateNewNodes(bool CNN
) { CreateNewNodes
= CNN
; }
178 void addRemapping(Node
*A
, Node
*B
) {
179 // Note, we don't need to check whether B is also remapped, because if it
180 // was we would have already remapped it when building it.
181 Remappings
.insert(std::make_pair(A
, B
));
184 bool isMostRecentlyCreated(Node
*N
) const { return MostRecentlyCreated
== N
; }
186 void trackUsesOf(Node
*N
) {
188 TrackedNodeIsUsed
= false;
190 bool trackedNodeIsUsed() const { return TrackedNodeIsUsed
; }
193 // FIXME: Also expand built-in substitutions?
195 using CanonicalizingDemangler
=
196 itanium_demangle::ManglingParser
<CanonicalizerAllocator
>;
199 struct ItaniumManglingCanonicalizer::Impl
{
200 CanonicalizingDemangler Demangler
= {nullptr, nullptr};
203 ItaniumManglingCanonicalizer::ItaniumManglingCanonicalizer() : P(new Impl
) {}
204 ItaniumManglingCanonicalizer::~ItaniumManglingCanonicalizer() { delete P
; }
206 ItaniumManglingCanonicalizer::EquivalenceError
207 ItaniumManglingCanonicalizer::addEquivalence(FragmentKind Kind
, StringRef First
,
209 auto &Alloc
= P
->Demangler
.ASTAllocator
;
210 Alloc
.setCreateNewNodes(true);
212 auto Parse
= [&](StringRef Str
) {
213 P
->Demangler
.reset(Str
.begin(), Str
.end());
216 // A <name>, with minor extensions to allow arbitrary namespace and
217 // template names that can't easily be written as <name>s.
218 case FragmentKind::Name
:
219 // Very special case: allow "St" as a shorthand for "3std". It's not
220 // valid as a <name> mangling, but is nonetheless the most natural
221 // way to name the 'std' namespace.
222 if (Str
.size() == 2 && P
->Demangler
.consumeIf("St"))
223 N
= P
->Demangler
.make
<itanium_demangle::NameType
>("std");
224 // We permit substitutions to name templates without their template
225 // arguments. This mostly just falls out, as almost all template names
226 // are valid as <name>s, but we also want to parse <substitution>s as
227 // <name>s, even though they're not.
228 else if (Str
.startswith("S"))
229 // Parse the substitution and optional following template arguments.
230 N
= P
->Demangler
.parseType();
232 N
= P
->Demangler
.parseName();
236 case FragmentKind::Type
:
237 N
= P
->Demangler
.parseType();
241 case FragmentKind::Encoding
:
242 N
= P
->Demangler
.parseEncoding();
246 // If we have trailing junk, the mangling is invalid.
247 if (P
->Demangler
.numLeft() != 0)
250 // If any node was created after N, then we cannot safely remap it because
251 // it might already be in use by another node.
252 return std::make_pair(N
, Alloc
.isMostRecentlyCreated(N
));
255 Node
*FirstNode
, *SecondNode
;
256 bool FirstIsNew
, SecondIsNew
;
258 std::tie(FirstNode
, FirstIsNew
) = Parse(First
);
260 return EquivalenceError::InvalidFirstMangling
;
262 Alloc
.trackUsesOf(FirstNode
);
263 std::tie(SecondNode
, SecondIsNew
) = Parse(Second
);
265 return EquivalenceError::InvalidSecondMangling
;
267 // If they're already equivalent, there's nothing to do.
268 if (FirstNode
== SecondNode
)
269 return EquivalenceError::Success
;
271 if (FirstIsNew
&& !Alloc
.trackedNodeIsUsed())
272 Alloc
.addRemapping(FirstNode
, SecondNode
);
273 else if (SecondIsNew
)
274 Alloc
.addRemapping(SecondNode
, FirstNode
);
276 return EquivalenceError::ManglingAlreadyUsed
;
278 return EquivalenceError::Success
;
281 static ItaniumManglingCanonicalizer::Key
282 parseMaybeMangledName(CanonicalizingDemangler
&Demangler
, StringRef Mangling
,
283 bool CreateNewNodes
) {
284 Demangler
.ASTAllocator
.setCreateNewNodes(CreateNewNodes
);
285 Demangler
.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 to
288 // be remapped by (eg)
289 // encoding 6memcpy 7memmove
290 // consistent with how they are encoded as local-names inside a C++ mangling.
292 if (Mangling
.startswith("_Z") || Mangling
.startswith("__Z") ||
293 Mangling
.startswith("___Z") || Mangling
.startswith("____Z"))
294 N
= Demangler
.parse();
296 N
= Demangler
.make
<itanium_demangle::NameType
>(
297 std::string_view(Mangling
.data(), Mangling
.size()));
298 return reinterpret_cast<ItaniumManglingCanonicalizer::Key
>(N
);
301 ItaniumManglingCanonicalizer::Key
302 ItaniumManglingCanonicalizer::canonicalize(StringRef Mangling
) {
303 return parseMaybeMangledName(P
->Demangler
, Mangling
, true);
306 ItaniumManglingCanonicalizer::Key
307 ItaniumManglingCanonicalizer::lookup(StringRef Mangling
) {
308 return parseMaybeMangledName(P
->Demangler
, Mangling
, false);