LLVM 22.0.0git
ItaniumDemangle.cpp
Go to the documentation of this file.
1//===------------------------- ItaniumDemangle.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// FIXME: (possibly) incomplete list of features that clang mangles that this
10// file does not yet support:
11// - C++ modules TS
12
15
16#include <cassert>
17#include <cctype>
18#include <cstdio>
19#include <cstdlib>
20#include <cstring>
21#include <exception>
22#include <functional>
23#include <utility>
24
25using namespace llvm;
26using namespace llvm::itanium_demangle;
27
28// <discriminator> := _ <non-negative number> # when number < 10
29// := __ <non-negative number> _ # when number >= 10
30// extension := decimal-digit+ # at the end of string
31const char *itanium_demangle::parse_discriminator(const char *first,
32 const char *last) {
33 // parse but ignore discriminator
34 if (first != last) {
35 if (*first == '_') {
36 const char *t1 = first + 1;
37 if (t1 != last) {
38 if (std::isdigit(*t1))
39 first = t1 + 1;
40 else if (*t1 == '_') {
41 for (++t1; t1 != last && std::isdigit(*t1); ++t1)
42 ;
43 if (t1 != last && *t1 == '_')
44 first = t1 + 1;
45 }
46 }
47 } else if (std::isdigit(*first)) {
48 const char *t1 = first + 1;
49 for (; t1 != last && std::isdigit(*t1); ++t1)
50 ;
51 if (t1 == last)
52 first = last;
53 }
54 }
55 return first;
56}
57
58#ifndef NDEBUG
59namespace {
60struct DumpVisitor {
61 unsigned Depth = 0;
62 bool PendingNewline = false;
63
64 template<typename NodeT> static constexpr bool wantsNewline(const NodeT *) {
65 return true;
66 }
67 static bool wantsNewline(NodeArray A) { return !A.empty(); }
68 static constexpr bool wantsNewline(...) { return false; }
69
70 template<typename ...Ts> static bool anyWantNewline(Ts ...Vs) {
71 for (bool B : {wantsNewline(Vs)...})
72 if (B)
73 return true;
74 return false;
75 }
76
77 void printStr(const char *S) { fprintf(stderr, "%s", S); }
78 void print(std::string_view SV) {
79 fprintf(stderr, "\"%.*s\"", (int)SV.size(), SV.data());
80 }
81 void print(const Node *N) {
82 if (N)
83 N->visit(std::ref(*this));
84 else
85 printStr("<null>");
86 }
87 void print(NodeArray A) {
88 ++Depth;
89 printStr("{");
90 bool First = true;
91 for (const Node *N : A) {
92 if (First)
93 print(N);
94 else
95 printWithComma(N);
96 First = false;
97 }
98 printStr("}");
99 --Depth;
100 }
101
102 // Overload used when T is exactly 'bool', not merely convertible to 'bool'.
103 void print(bool B) { printStr(B ? "true" : "false"); }
104
105 template <class T> std::enable_if_t<std::is_unsigned<T>::value> print(T N) {
106 fprintf(stderr, "%llu", (unsigned long long)N);
107 }
108
109 template <class T> std::enable_if_t<std::is_signed<T>::value> print(T N) {
110 fprintf(stderr, "%lld", (long long)N);
111 }
112
113 void print(ReferenceKind RK) {
114 switch (RK) {
116 return printStr("ReferenceKind::LValue");
118 return printStr("ReferenceKind::RValue");
119 }
120 }
121 void print(FunctionRefQual RQ) {
122 switch (RQ) {
124 return printStr("FunctionRefQual::FrefQualNone");
126 return printStr("FunctionRefQual::FrefQualLValue");
128 return printStr("FunctionRefQual::FrefQualRValue");
129 }
130 }
131 void print(Qualifiers Qs) {
132 if (!Qs) return printStr("QualNone");
133 struct QualName { Qualifiers Q; const char *Name; } Names[] = {
134 {QualConst, "QualConst"},
135 {QualVolatile, "QualVolatile"},
136 {QualRestrict, "QualRestrict"},
137 };
138 for (QualName Name : Names) {
139 if (Qs & Name.Q) {
140 printStr(Name.Name);
141 Qs = Qualifiers(Qs & ~Name.Q);
142 if (Qs) printStr(" | ");
143 }
144 }
145 }
146 void print(SpecialSubKind SSK) {
147 switch (SSK) {
149 return printStr("SpecialSubKind::allocator");
151 return printStr("SpecialSubKind::basic_string");
153 return printStr("SpecialSubKind::string");
155 return printStr("SpecialSubKind::istream");
157 return printStr("SpecialSubKind::ostream");
159 return printStr("SpecialSubKind::iostream");
160 }
161 }
162 void print(TemplateParamKind TPK) {
163 switch (TPK) {
165 return printStr("TemplateParamKind::Type");
167 return printStr("TemplateParamKind::NonType");
169 return printStr("TemplateParamKind::Template");
170 }
171 }
172 void print(Node::Prec P) {
173 switch (P) {
175 return printStr("Node::Prec::Primary");
177 return printStr("Node::Prec::Postfix");
179 return printStr("Node::Prec::Unary");
180 case Node::Prec::Cast:
181 return printStr("Node::Prec::Cast");
183 return printStr("Node::Prec::PtrMem");
185 return printStr("Node::Prec::Multiplicative");
187 return printStr("Node::Prec::Additive");
189 return printStr("Node::Prec::Shift");
191 return printStr("Node::Prec::Spaceship");
193 return printStr("Node::Prec::Relational");
195 return printStr("Node::Prec::Equality");
196 case Node::Prec::And:
197 return printStr("Node::Prec::And");
198 case Node::Prec::Xor:
199 return printStr("Node::Prec::Xor");
200 case Node::Prec::Ior:
201 return printStr("Node::Prec::Ior");
203 return printStr("Node::Prec::AndIf");
204 case Node::Prec::OrIf:
205 return printStr("Node::Prec::OrIf");
207 return printStr("Node::Prec::Conditional");
209 return printStr("Node::Prec::Assign");
211 return printStr("Node::Prec::Comma");
213 return printStr("Node::Prec::Default");
214 }
215 }
216
217 void newLine() {
218 printStr("\n");
219 for (unsigned I = 0; I != Depth; ++I)
220 printStr(" ");
221 PendingNewline = false;
222 }
223
224 template<typename T> void printWithPendingNewline(T V) {
225 print(V);
226 if (wantsNewline(V))
227 PendingNewline = true;
228 }
229
230 template<typename T> void printWithComma(T V) {
231 if (PendingNewline || wantsNewline(V)) {
232 printStr(",");
233 newLine();
234 } else {
235 printStr(", ");
236 }
237
238 printWithPendingNewline(V);
239 }
240
241 struct CtorArgPrinter {
242 DumpVisitor &Visitor;
243
244 template<typename T, typename ...Rest> void operator()(T V, Rest ...Vs) {
245 if (Visitor.anyWantNewline(V, Vs...))
246 Visitor.newLine();
247 Visitor.printWithPendingNewline(V);
248 int PrintInOrder[] = { (Visitor.printWithComma(Vs), 0)..., 0 };
249 (void)PrintInOrder;
250 }
251 };
252
253 template<typename NodeT> void operator()(const NodeT *Node) {
254 Depth += 2;
255 fprintf(stderr, "%s(", itanium_demangle::NodeKind<NodeT>::name());
256 Node->match(CtorArgPrinter{*this});
257 fprintf(stderr, ")");
258 Depth -= 2;
259 }
260
261 void operator()(const ForwardTemplateReference *Node) {
262 Depth += 2;
263 fprintf(stderr, "ForwardTemplateReference(");
264 if (Node->Ref && !Node->Printing) {
265 Node->Printing = true;
266 CtorArgPrinter{*this}(Node->Ref);
267 Node->Printing = false;
268 } else {
269 CtorArgPrinter{*this}(Node->Index);
270 }
271 fprintf(stderr, ")");
272 Depth -= 2;
273 }
274};
275}
276
277void itanium_demangle::Node::dump() const {
278 DumpVisitor V;
279 visit(std::ref(V));
280 V.newLine();
281}
282#endif
283
284namespace {
285class BumpPointerAllocator {
286 struct BlockMeta {
287 BlockMeta* Next;
288 size_t Current;
289 };
290
291 static constexpr size_t AllocSize = 4096;
292 static constexpr size_t UsableAllocSize = AllocSize - sizeof(BlockMeta);
293
294 alignas(long double) char InitialBuffer[AllocSize];
295 BlockMeta* BlockList = nullptr;
296
297 void grow() {
298 char* NewMeta = static_cast<char *>(std::malloc(AllocSize));
299 if (NewMeta == nullptr)
300 std::terminate();
301 BlockList = new (NewMeta) BlockMeta{BlockList, 0};
302 }
303
304 void* allocateMassive(size_t NBytes) {
305 NBytes += sizeof(BlockMeta);
306 BlockMeta* NewMeta = reinterpret_cast<BlockMeta*>(std::malloc(NBytes));
307 if (NewMeta == nullptr)
308 std::terminate();
309 BlockList->Next = new (NewMeta) BlockMeta{BlockList->Next, 0};
310 return static_cast<void*>(NewMeta + 1);
311 }
312
313public:
314 BumpPointerAllocator()
315 : BlockList(new (InitialBuffer) BlockMeta{nullptr, 0}) {}
316
317 void* allocate(size_t N) {
318 N = (N + 15u) & ~15u;
319 if (N + BlockList->Current >= UsableAllocSize) {
320 if (N > UsableAllocSize)
321 return allocateMassive(N);
322 grow();
323 }
324 BlockList->Current += N;
325 return static_cast<void*>(reinterpret_cast<char*>(BlockList + 1) +
326 BlockList->Current - N);
327 }
328
329 void reset() {
330 while (BlockList) {
331 BlockMeta* Tmp = BlockList;
332 BlockList = BlockList->Next;
333 if (reinterpret_cast<char*>(Tmp) != InitialBuffer)
334 std::free(Tmp);
335 }
336 BlockList = new (InitialBuffer) BlockMeta{nullptr, 0};
337 }
338
339 ~BumpPointerAllocator() { reset(); }
340};
341
342class DefaultAllocator {
343 BumpPointerAllocator Alloc;
344
345public:
346 void reset() { Alloc.reset(); }
347
348 template<typename T, typename ...Args> T *makeNode(Args &&...args) {
349 return new (Alloc.allocate(sizeof(T)))
350 T(std::forward<Args>(args)...);
351 }
352
353 void *allocateNodeArray(size_t sz) {
354 return Alloc.allocate(sizeof(Node *) * sz);
355 }
356};
357} // unnamed namespace
358
359//===----------------------------------------------------------------------===//
360// Code beyond this point should not be synchronized with libc++abi.
361//===----------------------------------------------------------------------===//
362
363using Demangler = itanium_demangle::ManglingParser<DefaultAllocator>;
364
365char *llvm::itaniumDemangle(std::string_view MangledName, bool ParseParams) {
366 if (MangledName.empty())
367 return nullptr;
368
369 Demangler Parser(MangledName.data(),
370 MangledName.data() + MangledName.length());
371 Node *AST = Parser.parse(ParseParams);
372 if (!AST)
373 return nullptr;
374
375 OutputBuffer OB;
376 assert(Parser.ForwardTemplateRefs.empty());
377 AST->print(OB);
378 OB += '\0';
379 return OB.getBuffer();
380}
381
383 : RootNode(nullptr), Context(new Demangler{nullptr, nullptr}) {}
384
386 delete static_cast<Demangler *>(Context);
387}
388
391 : RootNode(Other.RootNode), Context(Other.Context) {
392 Other.Context = Other.RootNode = nullptr;
393}
394
397 std::swap(RootNode, Other.RootNode);
398 std::swap(Context, Other.Context);
399 return *this;
400}
401
402// Demangle MangledName into an AST, storing it into this->RootNode.
403bool ItaniumPartialDemangler::partialDemangle(const char *MangledName) {
404 Demangler *Parser = static_cast<Demangler *>(Context);
405 size_t Len = std::strlen(MangledName);
406 Parser->reset(MangledName, MangledName + Len);
407 RootNode = Parser->parse();
408 return RootNode == nullptr;
409}
410static char *printNode(const Node *RootNode, OutputBuffer &OB, size_t *N) {
411 RootNode->print(OB);
412 OB += '\0';
413 if (N != nullptr)
414 *N = OB.getCurrentPosition();
415 return OB.getBuffer();
416}
417
418static char *printNode(const Node *RootNode, char *Buf, size_t *N) {
419 OutputBuffer OB(Buf, N);
420 return printNode(RootNode, OB, N);
421}
422
423char *ItaniumPartialDemangler::getFunctionBaseName(char *Buf, size_t *N) const {
424 if (!isFunction())
425 return nullptr;
426
427 const Node *Name = static_cast<const FunctionEncoding *>(RootNode)->getName();
428
429 while (true) {
430 switch (Name->getKind()) {
431 case Node::KAbiTagAttr:
432 Name = static_cast<const AbiTagAttr *>(Name)->Base;
433 continue;
434 case Node::KModuleEntity:
435 Name = static_cast<const ModuleEntity *>(Name)->Name;
436 continue;
437 case Node::KNestedName:
438 Name = static_cast<const NestedName *>(Name)->Name;
439 continue;
440 case Node::KLocalName:
441 Name = static_cast<const LocalName *>(Name)->Entity;
442 continue;
443 case Node::KNameWithTemplateArgs:
444 Name = static_cast<const NameWithTemplateArgs *>(Name)->Name;
445 continue;
446 default:
447 return printNode(Name, Buf, N);
448 }
449 }
450}
451
453 size_t *N) const {
454 if (!isFunction())
455 return nullptr;
456 const Node *Name = static_cast<const FunctionEncoding *>(RootNode)->getName();
457
458 OutputBuffer OB(Buf, N);
459
460 KeepGoingLocalFunction:
461 while (true) {
462 if (Name->getKind() == Node::KAbiTagAttr) {
463 Name = static_cast<const AbiTagAttr *>(Name)->Base;
464 continue;
465 }
466 if (Name->getKind() == Node::KNameWithTemplateArgs) {
467 Name = static_cast<const NameWithTemplateArgs *>(Name)->Name;
468 continue;
469 }
470 break;
471 }
472
473 if (Name->getKind() == Node::KModuleEntity)
474 Name = static_cast<const ModuleEntity *>(Name)->Name;
475
476 switch (Name->getKind()) {
477 case Node::KNestedName:
478 static_cast<const NestedName *>(Name)->Qual->print(OB);
479 break;
480 case Node::KLocalName: {
481 auto *LN = static_cast<const LocalName *>(Name);
482 LN->Encoding->print(OB);
483 OB += "::";
484 Name = LN->Entity;
485 goto KeepGoingLocalFunction;
486 }
487 default:
488 break;
489 }
490 OB += '\0';
491 if (N != nullptr)
492 *N = OB.getCurrentPosition();
493 return OB.getBuffer();
494}
495
496char *ItaniumPartialDemangler::getFunctionName(char *Buf, size_t *N) const {
497 if (!isFunction())
498 return nullptr;
499 auto *Name = static_cast<FunctionEncoding *>(RootNode)->getName();
500 return printNode(Name, Buf, N);
501}
502
504 size_t *N) const {
505 if (!isFunction())
506 return nullptr;
507 NodeArray Params = static_cast<FunctionEncoding *>(RootNode)->getParams();
508
509 OutputBuffer OB(Buf, N);
510
511 OB += '(';
512 Params.printWithComma(OB);
513 OB += ')';
514 OB += '\0';
515 if (N != nullptr)
516 *N = OB.getCurrentPosition();
517 return OB.getBuffer();
518}
519
521 char *Buf, size_t *N) const {
522 if (!isFunction())
523 return nullptr;
524
525 OutputBuffer OB(Buf, N);
526
527 if (const Node *Ret =
528 static_cast<const FunctionEncoding *>(RootNode)->getReturnType())
529 Ret->print(OB);
530
531 OB += '\0';
532 if (N != nullptr)
533 *N = OB.getCurrentPosition();
534 return OB.getBuffer();
535}
536
537char *ItaniumPartialDemangler::finishDemangle(char *Buf, size_t *N) const {
538 assert(RootNode != nullptr && "must call partialDemangle()");
539 return printNode(static_cast<Node *>(RootNode), Buf, N);
540}
541
543 assert(RootNode != nullptr && "must call partialDemangle()");
544 assert(OB != nullptr && "valid OutputBuffer argument required");
545 return printNode(static_cast<Node *>(RootNode),
546 *static_cast<OutputBuffer *>(OB),
547 /*N=*/nullptr);
548}
549
551 assert(RootNode != nullptr && "must call partialDemangle()");
552 if (!isFunction())
553 return false;
554 auto *E = static_cast<const FunctionEncoding *>(RootNode);
555 return E->getCVQuals() != QualNone || E->getRefQual() != FrefQualNone;
556}
557
559 const Node *N = static_cast<const Node *>(RootNode);
560 while (N) {
561 switch (N->getKind()) {
562 default:
563 return false;
564 case Node::KCtorDtorName:
565 return true;
566
567 case Node::KAbiTagAttr:
568 N = static_cast<const AbiTagAttr *>(N)->Base;
569 break;
570 case Node::KFunctionEncoding:
571 N = static_cast<const FunctionEncoding *>(N)->getName();
572 break;
573 case Node::KLocalName:
574 N = static_cast<const LocalName *>(N)->Entity;
575 break;
576 case Node::KNameWithTemplateArgs:
577 N = static_cast<const NameWithTemplateArgs *>(N)->Name;
578 break;
579 case Node::KNestedName:
580 N = static_cast<const NestedName *>(N)->Name;
581 break;
582 case Node::KModuleEntity:
583 N = static_cast<const ModuleEntity *>(N)->Name;
584 break;
585 }
586 }
587 return false;
588}
589
591 assert(RootNode != nullptr && "must call partialDemangle()");
592 return static_cast<const Node *>(RootNode)->getKind() ==
593 Node::KFunctionEncoding;
594}
595
597 assert(RootNode != nullptr && "must call partialDemangle()");
598 auto K = static_cast<const Node *>(RootNode)->getKind();
599 return K == Node::KSpecialName || K == Node::KCtorVtableSpecialName;
600}
601
603 return !isFunction() && !isSpecialName();
604}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
TemplateParamKind
FunctionRefQual
@ FrefQualLValue
@ FrefQualNone
@ FrefQualRValue
ReferenceKind
SpecialSubKind
Qualifiers
@ QualVolatile
@ QualRestrict
@ QualConst
@ QualNone
static void printNode(raw_ostream &OS, LazyCallGraph::Node &N)
#define I(x, y, z)
Definition MD5.cpp:58
#define T
nvptx lower args
#define P(N)
static StringRef getName(Value *V)
void visit(MachineFunction &MF, MachineBasicBlock &Start, std::function< void(MachineBasicBlock *)> op)
Qualifiers getCVQuals() const
void printWithComma(OutputBuffer &OB) const
void print(OutputBuffer &OB) const
Prec
Operator precedence for expression nodes.
NodeAddr< NodeBase * > Node
Definition RDFGraph.h:381
This is an optimization pass for GlobalISel generic memory operations.
Printable print(const GCNRegPressure &RP, const GCNSubtarget *ST=nullptr, unsigned DynamicVGPRBlockSize=0)
DEMANGLE_ABI char * itaniumDemangle(std::string_view mangled_name, bool ParseParams=true)
Returns a non-NULL pointer to a NUL-terminated C style string that should be explicitly freed,...
@ Other
Any other memory.
Definition ModRef.h:68
@ First
Helpers to iterate all locations in the MemoryEffectsBase class.
Definition ModRef.h:71
void swap(llvm::BitVector &LHS, llvm::BitVector &RHS)
Implement std::swap in terms of BitVector swap.
Definition BitVector.h:869
#define N
DEMANGLE_ABI char * getFunctionParameters(char *Buf, size_t *N) const
Get the parameters for this function.
DEMANGLE_ABI bool isFunction() const
If this symbol describes a function.
DEMANGLE_ABI char * getFunctionBaseName(char *Buf, size_t *N) const
Get the base name of a function.
DEMANGLE_ABI bool isSpecialName() const
If this symbol is a <special-name>.
DEMANGLE_ABI bool partialDemangle(const char *MangledName)
Demangle into an AST.
DEMANGLE_ABI char * getFunctionName(char *Buf, size_t *N) const
Get the entire name of this function.
DEMANGLE_ABI bool hasFunctionQualifiers() const
If this function has any cv or reference qualifiers.
DEMANGLE_ABI char * finishDemangle(char *Buf, size_t *N) const
Just print the entire mangled name into Buf.
DEMANGLE_ABI char * getFunctionReturnType(char *Buf, size_t *N) const
DEMANGLE_ABI ItaniumPartialDemangler & operator=(ItaniumPartialDemangler &&Other)
DEMANGLE_ABI bool isCtorOrDtor() const
If this symbol describes a constructor or destructor.
DEMANGLE_ABI char * getFunctionDeclContextName(char *Buf, size_t *N) const
Get the context name for a function.
DEMANGLE_ABI bool isData() const
If this symbol describes a variable.