Index: llvm/lib/Demangle/MicrosoftDemangle.cpp =================================================================== --- llvm/lib/Demangle/MicrosoftDemangle.cpp +++ llvm/lib/Demangle/MicrosoftDemangle.cpp @@ -33,11 +33,21 @@ struct AllocatorNode { uint8_t *Buf = nullptr; size_t Used = 0; + size_t Capacity = 0; AllocatorNode *Next = nullptr; }; + void addNode(size_t Capacity) { + AllocatorNode *NewHead = new AllocatorNode; + NewHead->Buf = new uint8_t[Capacity]; + NewHead->Next = Head; + NewHead->Capacity = Capacity; + Head = NewHead; + NewHead->Used = 0; + } + public: - ArenaAllocator() : Head(new AllocatorNode) { Head->Buf = new uint8_t[Unit]; } + ArenaAllocator() { addNode(Unit); } ~ArenaAllocator() { while (Head) { @@ -49,10 +59,25 @@ } } + char *allocUnalignedBuffer(size_t Length) { + uint8_t *Buf = Head->Buf + Head->Used; + + Head->Used += Length; + if (Head->Used > Head->Capacity) { + // It's possible we need a buffer which is larger than our default unit + // size, so we need to be careful to add a node with capacity that is at + // least as large as what we need. + addNode(std::max(Unit, Length)); + Head->Used = Length; + Buf = Head->Buf; + } + + return reinterpret_cast(Buf); + } + template T *alloc(Args &&... ConstructorArgs) { size_t Size = sizeof(T); - assert(Size < Unit); assert(Head && Head->Buf); size_t P = (size_t)Head->Buf + Head->Used; @@ -62,15 +87,12 @@ size_t Adjustment = AlignedP - P; Head->Used += Size + Adjustment; - if (Head->Used < Unit) + if (Head->Used < Head->Capacity) return new (PP) T(std::forward(ConstructorArgs)...); - AllocatorNode *NewHead = new AllocatorNode; - NewHead->Buf = new uint8_t[ArenaAllocator::Unit]; - NewHead->Next = Head; - Head = NewHead; - NewHead->Used = Size; - return new (NewHead->Buf) T(std::forward(ConstructorArgs)...); + addNode(ArenaAllocator::Unit); + Head->Used = Size; + return new (Head->Buf) T(std::forward(ConstructorArgs)...); } private: @@ -386,6 +408,41 @@ } } +static bool startsWithLocalScopePattern(StringView S) { + if (!S.consumeFront('?')) + return false; + if (S.size() < 2) + return false; + + size_t End = S.find('?'); + if (End == StringView::npos) + return false; + StringView Candidate = S.substr(0, End); + if (Candidate.empty()) + return false; + + // \?[0-9]\? + if (Candidate.size() == 1) + return Candidate[0] >= '0' && Candidate[0] <= '9'; + + // If it's not 0-9, then it's an encoded number terminated with an @ + if (Candidate.back() != '@') + return false; + Candidate = Candidate.dropBack(); + + // An encoded number starts with B-P and all subsequent digits are in A-P. + if (Candidate[0] < 'B' || Candidate[0] > 'P') + return false; + Candidate = Candidate.dropFront(); + while (!Candidate.empty()) { + if (Candidate[0] < 'A' || Candidate[0] > 'P') + return false; + Candidate = Candidate.dropFront(); + } + + return true; +} + // Write a function or template parameter list. static void outputParameterList(OutputStream &OS, const ParamList &Params) { if (!Params.Current) { @@ -735,7 +792,8 @@ // You are supposed to call parse() first and then check if error is true. If // it is false, call output() to write the formatted name to the given stream. Symbol *parse(StringView &MangledName); - void output(const Symbol *S, OutputStream &OS); + void output(const Symbol *S, bool Quote, int ScopeIdentifier, + OutputStream &OS); // True if an error occurred. bool Error = false; @@ -763,6 +821,10 @@ int demangleNumber(StringView &MangledName); void memorizeString(StringView s); + + /// Allocate a copy of \p Borrowed into memory that we own. + StringView copyString(StringView Borrowed); + Name *demangleFullyQualifiedTypeName(StringView &MangledName); Name *demangleFullyQualifiedSymbolName(StringView &MangledName); @@ -777,6 +839,7 @@ Name *demangleOperatorName(StringView &MangledName); Name *demangleSimpleName(StringView &MangledName, bool Memorize); Name *demangleAnonymousNamespaceName(StringView &MangledName); + Name *demangleLocallyScopedNamePiece(StringView &MangledName); void demangleOperator(StringView &MangledName, Name *); FuncClass demangleFunctionClass(StringView &MangledName); @@ -813,6 +876,13 @@ }; } // namespace +StringView Demangler::copyString(StringView Borrowed) { + char *Stable = Arena.allocUnalignedBuffer(Borrowed.size() + 1); + std::strcpy(Stable, Borrowed.begin()); + + return {Stable, Borrowed.size()}; +} + // Parser entry point. Symbol *Demangler::parse(StringView &MangledName) { Symbol *S = Arena.alloc(); @@ -956,6 +1026,18 @@ Name *Node = demangleSimpleName(MangledName, false); Node->TemplateParams = demangleTemplateParameterList(MangledName); + + // Render this class template name into a string buffer so that we can + // memorize it for the purpose of back-referencing. + OutputStream OS = OutputStream::create(nullptr, nullptr, 1024); + outputName(OS, Node); + OS << '\0'; + char *Name = OS.getBuffer(); + + StringView Owned = copyString(Name); + memorizeString(Owned); + std::free(Name); + return Node; } @@ -1103,6 +1185,30 @@ return nullptr; } +Name *Demangler::demangleLocallyScopedNamePiece(StringView &MangledName) { + assert(startsWithLocalScopePattern(MangledName)); + + Name *Node = Arena.alloc(); + MangledName.consumeFront('?'); + int ScopeIdentifier = demangleNumber(MangledName); + + // One ? to terminate the number + MangledName.consumeFront('?'); + + assert(!Error); + Symbol *Scope = parse(MangledName); + if (Error) + return nullptr; + + // Render the parent symbol's name into a buffer. + OutputStream OS = OutputStream::create(nullptr, nullptr, 1024); + output(Scope, true, ScopeIdentifier, OS); + char *Result = OS.getBuffer(); + Node->Str = copyString(Result); + std::free(Result); + return Node; +} + // Parses a type name in the form of A@B@C@@ which represents C::B::A. Name *Demangler::demangleFullyQualifiedTypeName(StringView &MangledName) { Name *TypeName = demangleUnqualifiedTypeName(MangledName); @@ -1140,6 +1246,10 @@ } Name *Demangler::demangleUnqualifiedSymbolName(StringView &MangledName) { + if (startsWithDigit(MangledName)) + return demangleBackRefName(MangledName); + if (MangledName.startsWith("?$")) + return demangleClassTemplateName(MangledName); if (MangledName.startsWith('?')) return demangleOperatorName(MangledName); return demangleSimpleName(MangledName, true); @@ -1155,6 +1265,9 @@ if (MangledName.startsWith("?A")) return demangleAnonymousNamespaceName(MangledName); + if (startsWithLocalScopePattern(MangledName)) + return demangleLocallyScopedNamePiece(MangledName); + return demangleSimpleName(MangledName, true); } @@ -1706,7 +1819,8 @@ return {}; } -void Demangler::output(const Symbol *S, OutputStream &OS) { +void Demangler::output(const Symbol *S, bool Quote, int ScopeIdentifier, + OutputStream &OS) { // Converts an AST to a string. // // Converting an AST representing a C++ type to a string is tricky due @@ -1724,10 +1838,19 @@ // the "first half" of type declaration, and outputPost() writes the // "second half". For example, outputPre() writes a return type for a // function and outputPost() writes an parameter list. + if (Quote) + OS << '`'; + Type::outputPre(OS, *S->SymbolType); outputName(OS, S->SymbolName); Type::outputPost(OS, *S->SymbolType); + if (Quote) + OS << '\''; + + if (ScopeIdentifier != 0) + OS << "::`" << ScopeIdentifier << "'"; + // Null terminate the buffer. OS << '\0'; } @@ -1744,6 +1867,6 @@ *Status = llvm::demangle_success; OutputStream OS = OutputStream::create(Buf, N, 1024); - D.output(S, OS); + D.output(S, false, 0, OS); return OS.getBuffer(); } Index: llvm/lib/Demangle/StringView.h =================================================================== --- llvm/lib/Demangle/StringView.h +++ llvm/lib/Demangle/StringView.h @@ -22,6 +22,8 @@ const char *Last; public: + static const size_t npos = ~size_t(0); + template StringView(const char (&Str)[N]) : First(Str), Last(Str + N - 1) {} StringView(const char *First_, const char *Last_) @@ -35,6 +37,17 @@ return StringView(begin() + From, size() - From); } + size_t find(char C, size_t From = 0) const { + size_t FindBegin = std::min(From, size()); + // Avoid calling memchr with nullptr. + if (FindBegin < size()) { + // Just forward to memchr, which is faster than a hand-rolled loop. + if (const void *P = ::memchr(First + FindBegin, C, size() - FindBegin)) + return static_cast(P) - First; + } + return npos; + } + StringView substr(size_t From, size_t To) const { if (To >= size()) To = size() - 1; @@ -49,11 +62,22 @@ return StringView(First + N, Last); } + StringView dropBack(size_t N = 1) const { + if (N >= size()) + N = size(); + return StringView(First, Last - N); + } + char front() const { assert(!empty()); return *begin(); } + char back() const { + assert(!empty()); + return *(end() - 1); + } + char popFront() { assert(!empty()); return *First++; Index: llvm/test/Demangle/ms-mangle.test =================================================================== --- llvm/test/Demangle/ms-mangle.test +++ llvm/test/Demangle/ms-mangle.test @@ -265,18 +265,18 @@ ?s6@PR13182@@3PBQBDB ; CHECK: char const *const *PR13182::s6 -; FIXME: We don't properly support static locals in functions yet. +; FIXME: We don't properly support extern "C" functions yet. ; ?local@?1??extern_c_func@@9@4HA ; FIXME: int `extern_c_func'::`2'::local ; ?local@?1??extern_c_func@@9@4HA ; FIXME: int `extern_c_func'::`2'::local -; ?v@?1??f@@YAHXZ@4U@?1??1@YAHXZ@A -; FIXME: struct `int __cdecl f(void)'::`2':: `int __cdecl f(void)'::`2'::v +?v@?1??f@@YAHXZ@4U@?1??1@YAHXZ@A +; CHECK: struct `int __cdecl f(void)'::`2':: `int __cdecl f(void)'::`2'::v -; ?v@?1???$f@H@@YAHXZ@4U@?1???$f@H@@YAHXZ@A -; FIXME: struct `int __cdecl f(void)'::`2':: `int __cdecl f(void)'::`2'::v +?v@?1???$f@H@@YAHXZ@4U@?1???$f@H@@YAHXZ@A +; CHECK: struct `int __cdecl f(void)'::`2':: `int __cdecl f(void)'::`2'::v ??2OverloadedNewDelete@@SAPAXI@Z ; CHECK: static void * __cdecl OverloadedNewDelete::operator new(unsigned int) @@ -335,8 +335,8 @@ ; ?overloaded_fn@@$$J0YAXXZ ; FIXME-EXTERNC: extern \"C\" void __cdecl overloaded_fn(void) -; ?f@UnnamedType@@YAXQAPAU@S@1@@Z -; FIXME: void __cdecl UnnamedType::f(struct UnnamedType::S:: ** const) +?f@UnnamedType@@YAXQAPAU@S@1@@Z +; CHECK: void __cdecl UnnamedType::f(struct UnnamedType::S:: **const) ?f@UnnamedType@@YAXUT2@S@1@@Z ; CHECK: void __cdecl UnnamedType::f(struct UnnamedType::S::T2) Index: llvm/test/Demangle/ms-nested-scopes.test =================================================================== --- /dev/null +++ llvm/test/Demangle/ms-nested-scopes.test @@ -0,0 +1,140 @@ +; RUN: llvm-undname < %s | FileCheck %s + +; CHECK-NOT: Invalid mangled name + +; Test demangling of function local scope discriminator IDs. +?M@?1??L@@YAHXZ@4HA +; CHECK: int `int __cdecl L(void)'::`2'::M + +?M@?2??L@@YAHXZ@4HA +; CHECK: int `int __cdecl L(void)'::`3'::M + +?M@?3??L@@YAHXZ@4HA +; CHECK: int `int __cdecl L(void)'::`4'::M + +?M@?4??L@@YAHXZ@4HA +; CHECK: int `int __cdecl L(void)'::`5'::M + +?M@?5??L@@YAHXZ@4HA +; CHECK: int `int __cdecl L(void)'::`6'::M + +?M@?6??L@@YAHXZ@4HA +; CHECK: int `int __cdecl L(void)'::`7'::M + +?M@?7??L@@YAHXZ@4HA +; CHECK: int `int __cdecl L(void)'::`8'::M + +?M@?8??L@@YAHXZ@4HA +; CHECK: int `int __cdecl L(void)'::`9'::M + +?M@?9??L@@YAHXZ@4HA +; CHECK: int `int __cdecl L(void)'::`10'::M + +?M@?L@??L@@YAHXZ@4HA +; CHECK: int `int __cdecl L(void)'::`11'::M + +?M@?M@??L@@YAHXZ@4HA +; CHECK: int `int __cdecl L(void)'::`12'::M + +?M@?N@??L@@YAHXZ@4HA +; CHECK: int `int __cdecl L(void)'::`13'::M + +?M@?O@??L@@YAHXZ@4HA +; CHECK: int `int __cdecl L(void)'::`14'::M + +?M@?P@??L@@YAHXZ@4HA +; CHECK: int `int __cdecl L(void)'::`15'::M + +?M@?BA@??L@@YAHXZ@4HA +; CHECK: int `int __cdecl L(void)'::`16'::M + +?M@?BB@??L@@YAHXZ@4HA +; CHECK: int `int __cdecl L(void)'::`17'::M + +?j@?1??L@@YAHXZ@4UJ@@A +; CHECK: struct J `int __cdecl L(void)'::`2'::j + +; Test demangling of name back-references +?NN@0XX@@3HA +; CHECK: int XX::NN::NN + +?MM@0NN@XX@@3HA +; CHECK: int XX::NN::MM::MM + +?NN@MM@0XX@@3HA +; CHECK: int XX::NN::MM::NN + +?OO@0NN@01XX@@3HA +; CHECK: int XX::NN::OO::NN::OO::OO + +?NN@OO@010XX@@3HA +; CHECK: int XX::NN::OO::NN::OO::NN + +; Test demangling of name back-references combined with function local scopes. +?M@?1??0@YAHXZ@4HA +; CHECK: int `int __cdecl M(void)'::`2'::M + +?L@?2??M@0?2??0@YAHXZ@QEAAHXZ@4HA +; CHECK: int `int __cdecl `int __cdecl L(void)'::`3'::L::M(void)'::`3'::L + +?M@?2??0L@?2??1@YAHXZ@QEAAHXZ@4HA +; CHECK: int `int __cdecl `int __cdecl L(void)'::`3'::L::M(void)'::`3'::M + +; Function local scopes of template functions +?M@?1???$L@H@@YAHXZ@4HA +; CHECK: int `int __cdecl L(void)'::`2'::M + +; And member functions of template classes +?SN@?$NS@H@NS@@QEAAHXZ +; CHECK: int __cdecl NS::NS::SN(void) + +?NS@?1??SN@?$NS@H@0@QEAAHXZ@4HA +; CHECK: int `int __cdecl NS::NS::SN(void)'::`2'::NS + +?SN@?1??0?$NS@H@NS@@QEAAHXZ@4HA +; CHECK: int `int __cdecl NS::NS::SN(void)'::`2'::SN + +?NS@?1??SN@?$NS@H@10@QEAAHXZ@4HA +; CHECK: int `int __cdecl NS::SN::NS::SN(void)'::`2'::NS + +?SN@?1??0?$NS@H@0NS@@QEAAHXZ@4HA +; CHECK: int `int __cdecl NS::SN::NS::SN(void)'::`2'::SN + +; Make sure instantiated templates participate in back-referencing. +; In the next 3 examples there should be 3 back-references: +; 0 = X (right most name) +; 1 = C (second from right) +; 2 = C (third from right) +; Make sure all 3 work as expected by having the 4th component take each value +; from 0-2 and confirming it is the right component. +?X@?$C@H@C@0@2HB +; CHECK: static int const X::C::C::X + +?X@?$C@H@C@1@2HB +; CHECK: static int const C::C::C::X + +?X@?$C@H@C@2@2HB +; CHECK: static int const C::C::C::X + +; Putting everything together. + +; namespace A { namespace B { namespace C { namespace B { namespace C { +; template +; struct C { +; int B() { +; static C C; +; static int B = 7; +; static int A = 7; +; return C.B() + B + A; +; } +; }; +; } } } } } + +?C@?1??B@?$C@H@0101A@@QEAAHXZ@4U201013@A +; CHECK: struct A::B::C::B::C::C `int __cdecl A::B::C::B::C::C::B(void)'::`2'::C + +?B@?1??0?$C@H@C@020A@@QEAAHXZ@4HA +; CHECK: int `int __cdecl A::B::C::B::C::C::B(void)'::`2'::B + +?A@?1??B@?$C@H@C@1310@QEAAHXZ@4HA +; CHECK: int `int __cdecl A::B::C::B::C::C::B(void)'::`2'::A