<div dir="ltr">Yea, you are right.  I'll fix it for now but eventually I want to migrate to the one you added to the Itanium Demangler.</div><br><div class="gmail_quote"><div dir="ltr">On Fri, Jul 20, 2018 at 10:48 AM Erik Pilkington <<a href="mailto:erik.pilkington@gmail.com">erik.pilkington@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><br>
<br>
On 7/20/18 10:27 AM, Zachary Turner via llvm-commits wrote:<br>
> Author: zturner<br>
> Date: Fri Jul 20 10:27:48 2018<br>
> New Revision: 337584<br>
><br>
> URL: <a href="http://llvm.org/viewvc/llvm-project?rev=337584&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project?rev=337584&view=rev</a><br>
> Log:<br>
> Add a Microsoft Demangler.<br>
><br>
> This adds initial support for a demangling library (LLVMDemangle)<br>
> and tool (llvm-undname) for demangling Microsoft names.  This<br>
> doesn't cover 100% of cases and there are some known limitations<br>
> which I intend to address in followup patches, at least until such<br>
> time that we have (near) 100% test coverage matching up with all<br>
> of the test cases in clang/test/CodeGenCXX/mangle-ms-*.<br>
><br>
> Differential Revision: <a href="https://reviews.llvm.org/D49552" rel="noreferrer" target="_blank">https://reviews.llvm.org/D49552</a><br>
><br>
> Added:<br>
>      llvm/trunk/lib/Demangle/MicrosoftDemangle.cpp<br>
>      llvm/trunk/test/Demangle/ms-basic.test<br>
>      llvm/trunk/test/Demangle/ms-mangle.test<br>
>      llvm/trunk/test/Demangle/ms-windows.test<br>
>      llvm/trunk/tools/llvm-undname/<br>
>      llvm/trunk/tools/llvm-undname/CMakeLists.txt<br>
>      llvm/trunk/tools/llvm-undname/LLVMBuild.txt<br>
>      llvm/trunk/tools/llvm-undname/llvm-undname.cpp<br>
> Modified:<br>
>      llvm/trunk/include/llvm/Demangle/Demangle.h<br>
>      llvm/trunk/lib/Demangle/CMakeLists.txt<br>
>      llvm/trunk/tools/LLVMBuild.txt<br>
><br>
> Modified: llvm/trunk/include/llvm/Demangle/Demangle.h<br>
> URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Demangle/Demangle.h?rev=337584&r1=337583&r2=337584&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Demangle/Demangle.h?rev=337584&r1=337583&r2=337584&view=diff</a><br>
> ==============================================================================<br>
> --- llvm/trunk/include/llvm/Demangle/Demangle.h (original)<br>
> +++ llvm/trunk/include/llvm/Demangle/Demangle.h Fri Jul 20 10:27:48 2018<br>
> @@ -16,7 +16,7 @@ namespace llvm {<br>
>   /// The mangled_name is demangled into buf and returned. If the buffer is not<br>
>   /// large enough, realloc is used to expand it.<br>
>   ///<br>
> -/// The *status will be set to a value from the enumeration<br>
> +/// The *status will be set to a value from the following enumeration<br>
>   enum : int {<br>
>     demangle_unknown_error = -4,<br>
>     demangle_invalid_args = -3,<br>
> @@ -27,6 +27,8 @@ enum : int {<br>
>   <br>
>   char *itaniumDemangle(const char *mangled_name, char *buf, size_t *n,<br>
>                         int *status);<br>
> +char *microsoftDemangle(const char *mangled_name, char *buf, size_t *n,<br>
> +                        int *status);<br>
>   <br>
>   /// "Partial" demangler. This supports demangling a string into an AST<br>
>   /// (typically an intermediate stage in itaniumDemangle) and querying certain<br>
><br>
> Modified: llvm/trunk/lib/Demangle/CMakeLists.txt<br>
> URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Demangle/CMakeLists.txt?rev=337584&r1=337583&r2=337584&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Demangle/CMakeLists.txt?rev=337584&r1=337583&r2=337584&view=diff</a><br>
> ==============================================================================<br>
> --- llvm/trunk/lib/Demangle/CMakeLists.txt (original)<br>
> +++ llvm/trunk/lib/Demangle/CMakeLists.txt Fri Jul 20 10:27:48 2018<br>
> @@ -1,6 +1,7 @@<br>
>   add_llvm_library(LLVMDemangle<br>
>     ItaniumDemangle.cpp<br>
> -<br>
> +  MicrosoftDemangle.cpp<br>
> +<br>
>     ADDITIONAL_HEADER_DIRS<br>
>     "${LLVM_MAIN_INCLUDE_DIR}/llvm/Demangle"<br>
>   )<br>
><br>
> Added: llvm/trunk/lib/Demangle/MicrosoftDemangle.cpp<br>
> URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Demangle/MicrosoftDemangle.cpp?rev=337584&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Demangle/MicrosoftDemangle.cpp?rev=337584&view=auto</a><br>
> ==============================================================================<br>
> --- llvm/trunk/lib/Demangle/MicrosoftDemangle.cpp (added)<br>
> +++ llvm/trunk/lib/Demangle/MicrosoftDemangle.cpp Fri Jul 20 10:27:48 2018<br>
> @@ -0,0 +1,1533 @@<br>
> +//===- MicrosoftDemangle.cpp ----------------------------------------------===//<br>
> +//<br>
> +//                     The LLVM Compiler Infrastructure<br>
> +//<br>
> +// This file is dual licensed under the MIT and the University of Illinois Open<br>
> +// Source Licenses. See LICENSE.TXT for details.<br>
> +//<br>
> +//===----------------------------------------------------------------------===//<br>
> +//<br>
> +// This file defines a demangler for MSVC-style mangled symbols.<br>
> +//<br>
> +// This file has no dependencies on the rest of LLVM so that it can be<br>
> +// easily reused in other programs such as libcxxabi.<br>
> +//<br>
> +//===----------------------------------------------------------------------===//<br>
> +<br>
> +#include "llvm/Demangle/Demangle.h"<br>
> +<br>
> +#include "Compiler.h"<br>
> +#include "StringView.h"<br>
> +#include "Utility.h"<br>
> +<br>
> +#include <cctype><br>
> +<br>
> +// This memory allocator is extremely fast, but it doesn't call dtors<br>
> +// for allocated objects. That means you can't use STL containers<br>
> +// (such as std::vector) with this allocator. But it pays off --<br>
> +// the demangler is 3x faster with this allocator compared to one with<br>
> +// STL containers.<br>
> +namespace {<br>
> +class ArenaAllocator {<br>
> +  struct AllocatorNode {<br>
> +    uint8_t *Buf = nullptr;<br>
> +    size_t Used = 0;<br>
> +    AllocatorNode *Next = nullptr;<br>
> +  };<br>
> +<br>
> +public:<br>
> +  ArenaAllocator() : Head(new AllocatorNode) { Head->Buf = new uint8_t[Unit]; }<br>
> +<br>
> +  ~ArenaAllocator() {<br>
> +    while (Head) {<br>
> +      assert(Head->Buf);<br>
> +      delete[] Head->Buf;<br>
> +      Head = Head->Next;<br>
> +    }<br>
> +  }<br>
> +<br>
> +  void *alloc(size_t Size) {<br>
> +    assert(Size < Unit);<br>
> +    assert(Head && Head->Buf);<br>
> +<br>
> +    uint8_t *P = Head->Buf + Head->Used;<br>
> +    Head->Used += Size;<br>
Don't you need to handle alignment here?<br>
> +    if (Head->Used < Unit)<br>
> +      return P;<br>
> +<br>
> +    AllocatorNode *NewHead = new AllocatorNode;<br>
> +    NewHead->Buf = new uint8_t[ArenaAllocator::Unit];<br>
> +    NewHead->Next = Head;<br>
> +    Head = NewHead;<br>
> +    NewHead->Used = Size;<br>
> +    return NewHead->Buf;<br>
> +  }<br>
> +<br>
> +private:<br>
> +  static constexpr size_t Unit = 4096;<br>
> +<br>
> +  AllocatorNode *Head = nullptr;<br>
> +};<br>
> +} // namespace<br>
> +<br>
> +static bool startsWithDigit(StringView S) {<br>
> +  return !S.empty() && std::isdigit(S.front());<br>
> +}<br>
> +<br>
> +// Writes a space if the last token does not end with a punctuation.<br>
> +static void outputSpaceIfNecessary(OutputStream &OS) {<br>
> +  if (OS.empty())<br>
> +    return;<br>
> +<br>
> +  char C = OS.back();<br>
> +  if (isalnum(C) || C == '>')<br>
> +    OS << " ";<br>
> +}<br>
> +<br>
> +void *operator new(size_t Size, ArenaAllocator &A) { return A.alloc(Size); }<br>
> +<br>
> +// Storage classes<br>
> +enum Qualifiers : uint8_t {<br>
> +  Q_None = 0,<br>
> +  Q_Const = 1 << 0,<br>
> +  Q_Volatile = 1 << 1,<br>
> +  Q_Far = 1 << 2,<br>
> +  Q_Huge = 1 << 3,<br>
> +  Q_Unaligned = 1 << 4,<br>
> +  Q_Restrict = 1 << 5,<br>
> +  Q_Pointer64 = 1 << 6<br>
> +};<br>
> +<br>
> +enum class StorageClass : uint8_t {<br>
> +  None,<br>
> +  PrivateStatic,<br>
> +  ProtectedStatic,<br>
> +  PublicStatic,<br>
> +  Global,<br>
> +  FunctionLocalStatic<br>
> +};<br>
> +<br>
> +enum class QualifierMangleMode { Drop, Mangle, Result };<br>
> +enum class QualifierMangleLocation { Member, NonMember, Detect };<br>
> +<br>
> +// Calling conventions<br>
> +enum class CallingConv : uint8_t {<br>
> +  None,<br>
> +  Cdecl,<br>
> +  Pascal,<br>
> +  Thiscall,<br>
> +  Stdcall,<br>
> +  Fastcall,<br>
> +  Clrcall,<br>
> +  Eabi,<br>
> +  Vectorcall,<br>
> +  Regcall,<br>
> +};<br>
> +<br>
> +enum class ReferenceKind : uint8_t { None, LValueRef, RValueRef };<br>
> +<br>
> +// Types<br>
> +enum class PrimTy : uint8_t {<br>
> +  Unknown,<br>
> +  None,<br>
> +  Function,<br>
> +  Ptr,<br>
> +  Ref,<br>
> +  Array,<br>
> +<br>
> +  Struct,<br>
> +  Union,<br>
> +  Class,<br>
> +  Enum,<br>
> +<br>
> +  Void,<br>
> +  Bool,<br>
> +  Char,<br>
> +  Schar,<br>
> +  Uchar,<br>
> +  Short,<br>
> +  Ushort,<br>
> +  Int,<br>
> +  Uint,<br>
> +  Long,<br>
> +  Ulong,<br>
> +  Int64,<br>
> +  Uint64,<br>
> +  Wchar,<br>
> +  Float,<br>
> +  Double,<br>
> +  Ldouble,<br>
> +};<br>
> +<br>
> +// Function classes<br>
> +enum FuncClass : uint8_t {<br>
> +  Public = 1 << 0,<br>
> +  Protected = 1 << 1,<br>
> +  Private = 1 << 2,<br>
> +  Global = 1 << 3,<br>
> +  Static = 1 << 4,<br>
> +  Virtual = 1 << 5,<br>
> +  FFar = 1 << 6,<br>
> +};<br>
> +<br>
> +namespace {<br>
> +<br>
> +struct Type;<br>
> +<br>
> +// Represents a list of parameters (template params or function arguments.<br>
> +// It's represented as a linked list.<br>
> +struct ParamList {<br>
> +  Type *Current = nullptr;<br>
> +<br>
> +  ParamList *Next = nullptr;<br>
> +};<br>
> +<br>
> +// The type class. Mangled symbols are first parsed and converted to<br>
> +// this type and then converted to string.<br>
> +struct Type {<br>
> +  virtual ~Type() {}<br>
> +<br>
> +  virtual Type *clone(ArenaAllocator &Arena) const;<br>
> +<br>
> +  // Write the "first half" of a given type.  This is a static functions to<br>
> +  // give the code a chance to do processing that is common to a subset of<br>
> +  // subclasses<br>
> +  static void outputPre(OutputStream &OS, Type &Ty);<br>
> +<br>
> +  // Write the "second half" of a given type.  This is a static functions to<br>
> +  // give the code a chance to do processing that is common to a subset of<br>
> +  // subclasses<br>
> +  static void outputPost(OutputStream &OS, Type &Ty);<br>
> +<br>
> +  virtual void outputPre(OutputStream &OS);<br>
> +  virtual void outputPost(OutputStream &OS);<br>
> +<br>
> +  // Primitive type such as Int.<br>
> +  PrimTy Prim = PrimTy::Unknown;<br>
> +<br>
> +  Qualifiers Quals = Q_None;<br>
> +  StorageClass Storage = StorageClass::None; // storage class<br>
> +};<br>
> +<br>
> +// Represents an identifier which may be a template.<br>
> +struct Name {<br>
> +  // Name read from an MangledName string.<br>
> +  StringView Str;<br>
> +<br>
> +  // Overloaded operators are represented as special BackReferences in mangled<br>
> +  // symbols. If this is an operator name, "op" has an operator name (e.g.<br>
> +  // ">>"). Otherwise, empty.<br>
> +  StringView Operator;<br>
> +<br>
> +  // Template parameters. Null if not a template.<br>
> +  ParamList TemplateParams;<br>
> +<br>
> +  // Nested BackReferences (e.g. "A::B::C") are represented as a linked list.<br>
> +  Name *Next = nullptr;<br>
> +};<br>
> +<br>
> +struct PointerType : public Type {<br>
> +  Type *clone(ArenaAllocator &Arena) const override;<br>
> +  void outputPre(OutputStream &OS);<br>
> +  void outputPost(OutputStream &OS) override;<br>
> +<br>
> +  bool isMemberPointer() const { return false; }<br>
> +<br>
> +  // Represents a type X in "a pointer to X", "a reference to X",<br>
> +  // "an array of X", or "a function returning X".<br>
> +  Type *Pointee = nullptr;<br>
> +};<br>
> +<br>
> +struct FunctionType : public Type {<br>
> +  Type *clone(ArenaAllocator &Arena) const override;<br>
> +  void outputPre(OutputStream &OS);<br>
> +  void outputPost(OutputStream &OS);<br>
> +<br>
> +  Type *ReturnType = nullptr;<br>
> +  // If this is a reference, the type of reference.<br>
> +  ReferenceKind RefKind;<br>
> +<br>
> +  CallingConv CallConvention;<br>
> +  FuncClass FunctionClass;<br>
> +<br>
> +  ParamList Params;<br>
> +};<br>
> +<br>
> +struct UdtType : public Type {<br>
> +  Type *clone(ArenaAllocator &Arena) const override;<br>
> +  void outputPre(OutputStream &OS) override;<br>
> +<br>
> +  Name *UdtName = nullptr;<br>
> +};<br>
> +<br>
> +struct ArrayType : public Type {<br>
> +  Type *clone(ArenaAllocator &Arena) const override;<br>
> +  void outputPre(OutputStream &OS) override;<br>
> +  void outputPost(OutputStream &OS) override;<br>
> +<br>
> +  // Either NextDimension or ElementType will be valid.<br>
> +  ArrayType *NextDimension = nullptr;<br>
> +  uint32_t ArrayDimension = 0;<br>
> +<br>
> +  Type *ElementType = nullptr;<br>
> +};<br>
> +<br>
> +} // namespace<br>
> +<br>
> +static void outputCallingConvention(OutputStream &OS, CallingConv CC) {<br>
> +  outputSpaceIfNecessary(OS);<br>
> +<br>
> +  switch (CC) {<br>
> +  case CallingConv::Cdecl:<br>
> +    OS << "__cdecl";<br>
> +    break;<br>
> +  case CallingConv::Fastcall:<br>
> +    OS << "__fastcall";<br>
> +    break;<br>
> +  case CallingConv::Pascal:<br>
> +    OS << "__pascal";<br>
> +    break;<br>
> +  case CallingConv::Regcall:<br>
> +    OS << "__regcall";<br>
> +    break;<br>
> +  case CallingConv::Stdcall:<br>
> +    OS << "__stdcall";<br>
> +    break;<br>
> +  case CallingConv::Thiscall:<br>
> +    OS << "__thiscall";<br>
> +    break;<br>
> +  case CallingConv::Eabi:<br>
> +    OS << "__eabi";<br>
> +    break;<br>
> +  case CallingConv::Vectorcall:<br>
> +    OS << "__vectorcall";<br>
> +    break;<br>
> +  case CallingConv::Clrcall:<br>
> +    OS << "__clrcall";<br>
> +    break;<br>
> +  default:<br>
> +    break;<br>
> +  }<br>
> +}<br>
> +<br>
> +// Write a function or template parameter list.<br>
> +static void outputParameterList(OutputStream &OS, const ParamList &Params) {<br>
> +  const ParamList *Head = &Params;<br>
> +  while (Head) {<br>
> +    Type::outputPre(OS, *Head->Current);<br>
> +    Type::outputPost(OS, *Head->Current);<br>
> +<br>
> +    Head = Head->Next;<br>
> +<br>
> +    if (Head)<br>
> +      OS << ", ";<br>
> +  }<br>
> +}<br>
> +<br>
> +static void outputTemplateParams(OutputStream &OS, const Name &TheName) {<br>
> +  if (!TheName.TemplateParams.Current)<br>
> +    return;<br>
> +<br>
> +  OS << "<";<br>
> +  outputParameterList(OS, TheName.TemplateParams);<br>
> +  OS << ">";<br>
> +}<br>
> +<br>
> +static void outputName(OutputStream &OS, const Name *TheName) {<br>
> +  if (!TheName)<br>
> +    return;<br>
> +<br>
> +  outputSpaceIfNecessary(OS);<br>
> +<br>
> +  // Print out namespaces or outer class BackReferences.<br>
> +  for (; TheName->Next; TheName = TheName->Next) {<br>
> +    OS << TheName->Str;<br>
> +    outputTemplateParams(OS, *TheName);<br>
> +    OS << "::";<br>
> +  }<br>
> +<br>
> +  // Print out a regular name.<br>
> +  if (TheName->Operator.empty()) {<br>
> +    OS << TheName->Str;<br>
> +    outputTemplateParams(OS, *TheName);<br>
> +    return;<br>
> +  }<br>
> +<br>
> +  // Print out ctor or dtor.<br>
> +  if (TheName->Operator == "ctor" || TheName->Operator == "dtor") {<br>
> +    OS << TheName->Str;<br>
> +    outputTemplateParams(OS, *TheName);<br>
> +    OS << "::";<br>
> +    if (TheName->Operator == "dtor")<br>
> +      OS << "~";<br>
> +    OS << TheName->Str;<br>
> +    outputTemplateParams(OS, *TheName);<br>
> +    return;<br>
> +  }<br>
> +<br>
> +  // Print out an overloaded operator.<br>
> +  if (!TheName->Str.empty())<br>
> +    OS << TheName->Str << "::";<br>
> +  OS << "operator" << TheName->Operator;<br>
> +}<br>
> +<br>
> +namespace {<br>
> +<br>
> +Type *Type::clone(ArenaAllocator &Arena) const {<br>
> +  return new (Arena) Type(*this);<br>
> +}<br>
> +<br>
> +// Write the "first half" of a given type.<br>
> +void Type::outputPre(OutputStream &OS, Type &Ty) {<br>
> +  // Function types require custom handling of const and static so we<br>
> +  // handle them separately.  All other types use the same decoration<br>
> +  // for these modifiers, so handle them here in common code.<br>
> +  if (Ty.Prim == PrimTy::Function) {<br>
> +    Ty.outputPre(OS);<br>
> +    return;<br>
> +  }<br>
> +<br>
> +  switch (Ty.Storage) {<br>
> +  case StorageClass::PrivateStatic:<br>
> +  case StorageClass::PublicStatic:<br>
> +  case StorageClass::ProtectedStatic:<br>
> +    OS << "static ";<br>
> +  default:<br>
> +    break;<br>
> +  }<br>
> +  Ty.outputPre(OS);<br>
> +<br>
> +  if (Ty.Quals & Q_Const) {<br>
> +    outputSpaceIfNecessary(OS);<br>
> +    OS << "const";<br>
> +  }<br>
> +<br>
> +  if (Ty.Quals & Q_Volatile) {<br>
> +    outputSpaceIfNecessary(OS);<br>
> +    OS << "volatile";<br>
> +  }<br>
> +}<br>
> +<br>
> +// Write the "second half" of a given type.<br>
> +void Type::outputPost(OutputStream &OS, Type &Ty) { Ty.outputPost(OS); }<br>
> +<br>
> +void Type::outputPre(OutputStream &OS) {<br>
> +  switch (Prim) {<br>
> +  case PrimTy::Void:<br>
> +    OS << "void";<br>
> +    break;<br>
> +  case PrimTy::Bool:<br>
> +    OS << "bool";<br>
> +    break;<br>
> +  case PrimTy::Char:<br>
> +    OS << "char";<br>
> +    break;<br>
> +  case PrimTy::Schar:<br>
> +    OS << "signed char";<br>
> +    break;<br>
> +  case PrimTy::Uchar:<br>
> +    OS << "unsigned char";<br>
> +    break;<br>
> +  case PrimTy::Short:<br>
> +    OS << "short";<br>
> +    break;<br>
> +  case PrimTy::Ushort:<br>
> +    OS << "unsigned short";<br>
> +    break;<br>
> +  case PrimTy::Int:<br>
> +    OS << "int";<br>
> +    break;<br>
> +  case PrimTy::Uint:<br>
> +    OS << "unsigned int";<br>
> +    break;<br>
> +  case PrimTy::Long:<br>
> +    OS << "long";<br>
> +    break;<br>
> +  case PrimTy::Ulong:<br>
> +    OS << "unsigned long";<br>
> +    break;<br>
> +  case PrimTy::Int64:<br>
> +    OS << "__int64";<br>
> +    break;<br>
> +  case PrimTy::Uint64:<br>
> +    OS << "unsigned __int64";<br>
> +    break;<br>
> +  case PrimTy::Wchar:<br>
> +    OS << "wchar_t";<br>
> +    break;<br>
> +  case PrimTy::Float:<br>
> +    OS << "float";<br>
> +    break;<br>
> +  case PrimTy::Double:<br>
> +    OS << "double";<br>
> +    break;<br>
> +  case PrimTy::Ldouble:<br>
> +    OS << "long double";<br>
> +    break;<br>
> +  default:<br>
> +    assert(false && "Invalid primitive type!");<br>
> +  }<br>
> +}<br>
> +void Type::outputPost(OutputStream &OS) {}<br>
> +<br>
> +Type *PointerType::clone(ArenaAllocator &Arena) const {<br>
> +  return new (Arena) PointerType(*this);<br>
> +}<br>
> +<br>
> +void PointerType::outputPre(OutputStream &OS) {<br>
> +  Type::outputPre(OS, *Pointee);<br>
> +<br>
> +  outputSpaceIfNecessary(OS);<br>
> +<br>
> +  if (Quals & Q_Unaligned)<br>
> +    OS << "__unaligned ";<br>
> +<br>
> +  // "[]" and "()" (for function parameters) take precedence over "*",<br>
> +  // so "int *x(int)" means "x is a function returning int *". We need<br>
> +  // parentheses to supercede the default precedence. (e.g. we want to<br>
> +  // emit something like "int (*x)(int)".)<br>
> +  if (Pointee->Prim == PrimTy::Function || Pointee->Prim == PrimTy::Array)<br>
> +    OS << "(";<br>
> +<br>
> +  if (Prim == PrimTy::Ptr)<br>
> +    OS << "*";<br>
> +  else<br>
> +    OS << "&";<br>
> +<br>
> +  // if (Ty.Quals & Q_Pointer64)<br>
> +  //  OS << " __ptr64";<br>
> +  if (Quals & Q_Restrict)<br>
> +    OS << " __restrict";<br>
> +}<br>
> +<br>
> +void PointerType::outputPost(OutputStream &OS) {<br>
> +  if (Pointee->Prim == PrimTy::Function || Pointee->Prim == PrimTy::Array)<br>
> +    OS << ")";<br>
> +<br>
> +  Type::outputPost(OS, *Pointee);<br>
> +}<br>
> +<br>
> +Type *FunctionType::clone(ArenaAllocator &Arena) const {<br>
> +  return new (Arena) FunctionType(*this);<br>
> +}<br>
> +<br>
> +void FunctionType::outputPre(OutputStream &OS) {<br>
> +  if (!(FunctionClass & Global)) {<br>
> +    if (FunctionClass & Static)<br>
> +      OS << "static ";<br>
> +  }<br>
> +<br>
> +  if (ReturnType)<br>
> +    Type::outputPre(OS, *ReturnType);<br>
> +<br>
> +  outputCallingConvention(OS, CallConvention);<br>
> +}<br>
> +<br>
> +void FunctionType::outputPost(OutputStream &OS) {<br>
> +  OS << "(";<br>
> +  outputParameterList(OS, Params);<br>
> +  OS << ")";<br>
> +  if (Quals & Q_Const)<br>
> +    OS << " const";<br>
> +  if (Quals & Q_Volatile)<br>
> +    OS << " volatile";<br>
> +  return;<br>
> +}<br>
> +<br>
> +Type *UdtType::clone(ArenaAllocator &Arena) const {<br>
> +  return new (Arena) UdtType(*this);<br>
> +}<br>
> +<br>
> +void UdtType::outputPre(OutputStream &OS) {<br>
> +  switch (Prim) {<br>
> +  case PrimTy::Class:<br>
> +    OS << "class ";<br>
> +    break;<br>
> +  case PrimTy::Struct:<br>
> +    OS << "struct ";<br>
> +    break;<br>
> +  case PrimTy::Union:<br>
> +    OS << "union ";<br>
> +    break;<br>
> +  case PrimTy::Enum:<br>
> +    OS << "enum ";<br>
> +    break;<br>
> +  default:<br>
> +    assert(false && "Not a udt type!");<br>
> +  }<br>
> +<br>
> +  outputName(OS, UdtName);<br>
> +}<br>
> +<br>
> +Type *ArrayType::clone(ArenaAllocator &Arena) const {<br>
> +  return new (Arena) ArrayType(*this);<br>
> +}<br>
> +<br>
> +void ArrayType::outputPre(OutputStream &OS) {<br>
> +  Type::outputPre(OS, *ElementType);<br>
> +}<br>
> +<br>
> +void ArrayType::outputPost(OutputStream &OS) {<br>
> +  if (ArrayDimension > 0)<br>
> +    OS << "[" << ArrayDimension << "]";<br>
> +  if (NextDimension)<br>
> +    Type::outputPost(OS, *NextDimension);<br>
> +  else if (ElementType)<br>
> +    Type::outputPost(OS, *ElementType);<br>
> +}<br>
> +<br>
> +} // namespace<br>
> +<br>
> +namespace {<br>
> +<br>
> +// Demangler class takes the main role in demangling symbols.<br>
> +// It has a set of functions to parse mangled symbols into Type instances.<br>
> +// It also has a set of functions to cnovert Type instances to strings.<br>
> +class Demangler {<br>
> +public:<br>
> +  Demangler(OutputStream &OS, StringView s) : OS(OS), MangledName(s) {}<br>
> +<br>
> +  // You are supposed to call parse() first and then check if error is true.  If<br>
> +  // it is false, call output() to write the formatted name to the given stream.<br>
> +  void parse();<br>
> +  void output();<br>
> +<br>
> +  // True if an error occurred.<br>
> +  bool Error = false;<br>
> +<br>
> +private:<br>
> +  Type *demangleVariableEncoding();<br>
> +  Type *demangleFunctionEncoding();<br>
> +<br>
> +  Qualifiers demanglePointerExtQualifiers();<br>
> +<br>
> +  // Parser functions. This is a recursive-descent parser.<br>
> +  Type *demangleType(QualifierMangleMode QMM);<br>
> +  Type *demangleBasicType();<br>
> +  UdtType *demangleClassType();<br>
> +  PointerType *demanglePointerType();<br>
> +<br>
> +  ArrayType *demangleArrayType();<br>
> +<br>
> +  ParamList demangleParameterList();<br>
> +<br>
> +  int demangleNumber();<br>
> +  void demangleNamePiece(Name &Node, bool IsHead);<br>
> +<br>
> +  StringView demangleString(bool memorize);<br>
> +  void memorizeString(StringView s);<br>
> +  Name *demangleName();<br>
> +  void demangleOperator(Name *);<br>
> +  StringView demangleOperatorName();<br>
> +  int demangleFunctionClass();<br>
> +  CallingConv demangleCallingConvention();<br>
> +  StorageClass demangleVariableStorageClass();<br>
> +  ReferenceKind demangleReferenceKind();<br>
> +<br>
> +  Qualifiers demangleFunctionQualifiers();<br>
> +  Qualifiers demangleVariablQ_ifiers();<br>
> +  Qualifiers demangleReturnTypQ_ifiers();<br>
> +<br>
> +  Qualifiers demangleQualifiers(<br>
> +      QualifierMangleLocation Location = QualifierMangleLocation::Detect);<br>
> +<br>
> +  // Mangled symbol. demangle* functions shorten this string<br>
> +  // as they parse it.<br>
> +  StringView MangledName;<br>
> +<br>
> +  // A parsed mangled symbol.<br>
> +  Type *SymbolType;<br>
> +<br>
> +  // The main symbol name. (e.g. "ns::foo" in "int ns::foo()".)<br>
> +  Name *SymbolName = nullptr;<br>
> +<br>
> +  // Memory allocator.<br>
> +  ArenaAllocator Arena;<br>
> +<br>
> +  // The first 10 BackReferences in a mangled name can be back-referenced by<br>
> +  // special name @[0-9]. This is a storage for the first 10 BackReferences.<br>
> +  StringView BackReferences[10];<br>
> +  size_t BackRefCount = 0;<br>
> +<br>
> +  // The result is written to this stream.<br>
> +  OutputStream OS;<br>
> +};<br>
> +} // namespace<br>
> +<br>
> +// Parser entry point.<br>
> +void Demangler::parse() {<br>
> +  // MSVC-style mangled symbols must start with '?'.<br>
> +  if (!MangledName.consumeFront("?")) {<br>
> +    SymbolName = new (Arena) Name;<br>
> +    SymbolName->Str = MangledName;<br>
> +    SymbolType = new (Arena) Type;<br>
> +    SymbolType->Prim = PrimTy::Unknown;<br>
> +  }<br>
> +<br>
> +  // What follows is a main symbol name. This may include<br>
> +  // namespaces or class BackReferences.<br>
> +  SymbolName = demangleName();<br>
> +<br>
> +  // Read a variable.<br>
> +  if (startsWithDigit(MangledName)) {<br>
> +    SymbolType = demangleVariableEncoding();<br>
> +    return;<br>
> +  }<br>
> +<br>
> +  // Read a function.<br>
> +  SymbolType = demangleFunctionEncoding();<br>
> +}<br>
> +<br>
> +// <type-encoding> ::= <storage-class> <variable-type><br>
> +// <storage-class> ::= 0  # private static member<br>
> +//                 ::= 1  # protected static member<br>
> +//                 ::= 2  # public static member<br>
> +//                 ::= 3  # global<br>
> +//                 ::= 4  # static local<br>
> +<br>
> +Type *Demangler::demangleVariableEncoding() {<br>
> +  StorageClass SC = demangleVariableStorageClass();<br>
> +<br>
> +  Type *Ty = demangleType(QualifierMangleMode::Drop);<br>
> +<br>
> +  Ty->Storage = SC;<br>
> +<br>
> +  // <variable-type> ::= <type> <cvr-qualifiers><br>
> +  //                 ::= <type> <pointee-cvr-qualifiers> # pointers, references<br>
> +  switch (Ty->Prim) {<br>
> +  case PrimTy::Ptr:<br>
> +  case PrimTy::Ref: {<br>
> +    Qualifiers ExtraChildQuals = Q_None;<br>
> +    Ty->Quals = Qualifiers(Ty->Quals | demanglePointerExtQualifiers());<br>
> +<br>
> +    PointerType *PTy = static_cast<PointerType *>(Ty);<br>
> +    QualifierMangleLocation Location = PTy->isMemberPointer()<br>
> +                                           ? QualifierMangleLocation::Member<br>
> +                                           : QualifierMangleLocation::NonMember;<br>
> +<br>
> +    ExtraChildQuals = demangleQualifiers(Location);<br>
> +<br>
> +    if (PTy->isMemberPointer()) {<br>
> +      Name *BackRefName = demangleName();<br>
> +      (void)BackRefName;<br>
> +    }<br>
> +<br>
> +    PTy->Pointee->Quals = Qualifiers(PTy->Pointee->Quals | ExtraChildQuals);<br>
> +    break;<br>
> +  }<br>
> +  default:<br>
> +    Ty->Quals = demangleQualifiers();<br>
> +    break;<br>
> +  }<br>
> +<br>
> +  return Ty;<br>
> +}<br>
> +<br>
> +// Sometimes numbers are encoded in mangled symbols. For example,<br>
> +// "int (*x)[20]" is a valid C type (x is a pointer to an array of<br>
> +// length 20), so we need some way to embed numbers as part of symbols.<br>
> +// This function parses it.<br>
> +//<br>
> +// <number>               ::= [?] <non-negative integer><br>
> +//<br>
> +// <non-negative integer> ::= <decimal digit> # when 1 <= Number <= 10<br>
> +//                        ::= <hex digit>+ @  # when Numbrer == 0 or >= 10<br>
> +//<br>
> +// <hex-digit>            ::= [A-P]           # A = 0, B = 1, ...<br>
> +int Demangler::demangleNumber() {<br>
> +  bool neg = MangledName.consumeFront("?");<br>
> +<br>
> +  if (startsWithDigit(MangledName)) {<br>
> +    int32_t Ret = MangledName[0] - '0' + 1;<br>
> +    MangledName = MangledName.dropFront(1);<br>
> +    return neg ? -Ret : Ret;<br>
> +  }<br>
> +<br>
> +  int Ret = 0;<br>
> +  for (size_t i = 0; i < MangledName.size(); ++i) {<br>
> +    char C = MangledName[i];<br>
> +    if (C == '@') {<br>
> +      MangledName = MangledName.dropFront(i + 1);<br>
> +      return neg ? -Ret : Ret;<br>
> +    }<br>
> +    if ('A' <= C && C <= 'P') {<br>
> +      Ret = (Ret << 4) + (C - 'A');<br>
> +      continue;<br>
> +    }<br>
> +    break;<br>
> +  }<br>
> +<br>
> +  Error = true;<br>
> +  return 0;<br>
> +}<br>
> +<br>
> +// Read until the next '@'.<br>
> +StringView Demangler::demangleString(bool Memorize) {<br>
> +  for (size_t i = 0; i < MangledName.size(); ++i) {<br>
> +    if (MangledName[i] != '@')<br>
> +      continue;<br>
> +    StringView ret = MangledName.substr(0, i);<br>
> +    MangledName = MangledName.dropFront(i + 1);<br>
> +<br>
> +    if (Memorize)<br>
> +      memorizeString(ret);<br>
> +    return ret;<br>
> +  }<br>
> +<br>
> +  Error = true;<br>
> +  return "";<br>
> +}<br>
> +<br>
> +// First 10 strings can be referenced by special BackReferences ?0, ?1, ..., ?9.<br>
> +// Memorize it.<br>
> +void Demangler::memorizeString(StringView S) {<br>
> +  if (BackRefCount >= sizeof(BackReferences) / sizeof(*BackReferences))<br>
> +    return;<br>
> +  for (size_t i = 0; i < BackRefCount; ++i)<br>
> +    if (S == BackReferences[i])<br>
> +      return;<br>
> +  BackReferences[BackRefCount++] = S;<br>
> +}<br>
> +<br>
> +void Demangler::demangleNamePiece(Name &Node, bool IsHead) {<br>
> +  if (startsWithDigit(MangledName)) {<br>
> +    size_t I = MangledName[0] - '0';<br>
> +    if (I >= BackRefCount) {<br>
> +      Error = true;<br>
> +      return;<br>
> +    }<br>
> +    MangledName = MangledName.dropFront();<br>
> +    Node.Str = BackReferences[I];<br>
> +  } else if (MangledName.consumeFront("?$")) {<br>
> +    // Class template.<br>
> +    Node.Str = demangleString(false);<br>
> +    Node.TemplateParams = demangleParameterList();<br>
> +    if (!MangledName.consumeFront('@')) {<br>
> +      Error = true;<br>
> +      return;<br>
> +    }<br>
> +  } else if (!IsHead && MangledName.consumeFront("?A")) {<br>
> +    // Anonymous namespace starts with ?A.  So does overloaded operator[],<br>
> +    // but the distinguishing factor is that namespace themselves are not<br>
> +    // mangled, only the variables and functions inside of them are.  So<br>
> +    // an anonymous namespace will never occur as the first item in the<br>
> +    // name.<br>
> +    Node.Str = "`anonymous namespace'";<br>
> +    if (!MangledName.consumeFront('@')) {<br>
> +      Error = true;<br>
> +      return;<br>
> +    }<br>
> +  } else if (MangledName.consumeFront("?")) {<br>
> +    // Overloaded operator.<br>
> +    demangleOperator(&Node);<br>
> +  } else {<br>
> +    // Non-template functions or classes.<br>
> +    Node.Str = demangleString(true);<br>
> +  }<br>
> +}<br>
> +<br>
> +// Parses a name in the form of A@B@C@@ which represents C::B::A.<br>
> +Name *Demangler::demangleName() {<br>
> +  Name *Head = nullptr;<br>
> +<br>
> +  while (!MangledName.consumeFront("@")) {<br>
> +    Name *Elem = new (Arena) Name;<br>
> +<br>
> +    assert(!Error);<br>
> +    demangleNamePiece(*Elem, Head == nullptr);<br>
> +    if (Error)<br>
> +      return nullptr;<br>
> +<br>
> +    Elem->Next = Head;<br>
> +    Head = Elem;<br>
> +  }<br>
> +<br>
> +  return Head;<br>
> +}<br>
> +<br>
> +void Demangler::demangleOperator(Name *OpName) {<br>
> +  OpName->Operator = demangleOperatorName();<br>
> +  if (!Error && !MangledName.empty() && MangledName.front() != '@')<br>
> +    demangleNamePiece(*OpName, false);<br>
> +}<br>
> +<br>
> +StringView Demangler::demangleOperatorName() {<br>
> +  SwapAndRestore<StringView> RestoreOnError(MangledName, MangledName);<br>
> +  RestoreOnError.shouldRestore(false);<br>
> +<br>
> +  switch (MangledName.popFront()) {<br>
> +  case '0':<br>
> +    return "ctor";<br>
> +  case '1':<br>
> +    return "dtor";<br>
> +  case '2':<br>
> +    return " new";<br>
> +  case '3':<br>
> +    return " delete";<br>
> +  case '4':<br>
> +    return "=";<br>
> +  case '5':<br>
> +    return ">>";<br>
> +  case '6':<br>
> +    return "<<";<br>
> +  case '7':<br>
> +    return "!";<br>
> +  case '8':<br>
> +    return "==";<br>
> +  case '9':<br>
> +    return "!=";<br>
> +  case 'A':<br>
> +    return "[]";<br>
> +  case 'C':<br>
> +    return "->";<br>
> +  case 'D':<br>
> +    return "*";<br>
> +  case 'E':<br>
> +    return "++";<br>
> +  case 'F':<br>
> +    return "--";<br>
> +  case 'G':<br>
> +    return "-";<br>
> +  case 'H':<br>
> +    return "+";<br>
> +  case 'I':<br>
> +    return "&";<br>
> +  case 'J':<br>
> +    return "->*";<br>
> +  case 'K':<br>
> +    return "/";<br>
> +  case 'L':<br>
> +    return "%";<br>
> +  case 'M':<br>
> +    return "<";<br>
> +  case 'N':<br>
> +    return "<=";<br>
> +  case 'O':<br>
> +    return ">";<br>
> +  case 'P':<br>
> +    return ">=";<br>
> +  case 'Q':<br>
> +    return ",";<br>
> +  case 'R':<br>
> +    return "()";<br>
> +  case 'S':<br>
> +    return "~";<br>
> +  case 'T':<br>
> +    return "^";<br>
> +  case 'U':<br>
> +    return "|";<br>
> +  case 'V':<br>
> +    return "&&";<br>
> +  case 'W':<br>
> +    return "||";<br>
> +  case 'X':<br>
> +    return "*=";<br>
> +  case 'Y':<br>
> +    return "+=";<br>
> +  case 'Z':<br>
> +    return "-=";<br>
> +  case '_': {<br>
> +    if (MangledName.empty())<br>
> +      break;<br>
> +<br>
> +    switch (MangledName.popFront()) {<br>
> +    case '0':<br>
> +      return "/=";<br>
> +    case '1':<br>
> +      return "%=";<br>
> +    case '2':<br>
> +      return ">>=";<br>
> +    case '3':<br>
> +      return "<<=";<br>
> +    case '4':<br>
> +      return "&=";<br>
> +    case '5':<br>
> +      return "|=";<br>
> +    case '6':<br>
> +      return "^=";<br>
> +    case 'U':<br>
> +      return " new[]";<br>
> +    case 'V':<br>
> +      return " delete[]";<br>
> +    case '_':<br>
> +      if (MangledName.consumeFront("L"))<br>
> +        return " co_await";<br>
> +    }<br>
> +  }<br>
> +  }<br>
> +<br>
> +  Error = true;<br>
> +  RestoreOnError.shouldRestore(true);<br>
> +  return "";<br>
> +}<br>
> +<br>
> +int Demangler::demangleFunctionClass() {<br>
> +  SwapAndRestore<StringView> RestoreOnError(MangledName, MangledName);<br>
> +  RestoreOnError.shouldRestore(false);<br>
> +<br>
> +  switch (MangledName.popFront()) {<br>
> +  case 'A':<br>
> +    return Private;<br>
> +  case 'B':<br>
> +    return Private | FFar;<br>
> +  case 'C':<br>
> +    return Private | Static;<br>
> +  case 'D':<br>
> +    return Private | Static;<br>
> +  case 'E':<br>
> +    return Private | Virtual;<br>
> +  case 'F':<br>
> +    return Private | Virtual;<br>
> +  case 'I':<br>
> +    return Protected;<br>
> +  case 'J':<br>
> +    return Protected | FFar;<br>
> +  case 'K':<br>
> +    return Protected | Static;<br>
> +  case 'L':<br>
> +    return Protected | Static | FFar;<br>
> +  case 'M':<br>
> +    return Protected | Virtual;<br>
> +  case 'N':<br>
> +    return Protected | Virtual | FFar;<br>
> +  case 'Q':<br>
> +    return Public;<br>
> +  case 'R':<br>
> +    return Public | FFar;<br>
> +  case 'S':<br>
> +    return Public | Static;<br>
> +  case 'T':<br>
> +    return Public | Static | FFar;<br>
> +  case 'U':<br>
> +    return Public | Virtual;<br>
> +  case 'V':<br>
> +    return Public | Virtual | FFar;<br>
> +  case 'Y':<br>
> +    return Global;<br>
> +  case 'Z':<br>
> +    return Global | FFar;<br>
> +  }<br>
> +<br>
> +  Error = true;<br>
> +  RestoreOnError.shouldRestore(true);<br>
> +  return 0;<br>
> +}<br>
> +<br>
> +Qualifiers Demangler::demangleFunctionQualifiers() {<br>
> +  SwapAndRestore<StringView> RestoreOnError(MangledName, MangledName);<br>
> +  RestoreOnError.shouldRestore(false);<br>
> +<br>
> +  switch (MangledName.popFront()) {<br>
> +  case 'A':<br>
> +    return Q_None;<br>
> +  case 'B':<br>
> +    return Q_Const;<br>
> +  case 'C':<br>
> +    return Q_Volatile;<br>
> +  case 'D':<br>
> +    return Qualifiers(Q_Const | Q_Volatile);<br>
> +  }<br>
> +<br>
> +  Error = true;<br>
> +  RestoreOnError.shouldRestore(true);<br>
> +  return Q_None;<br>
> +}<br>
> +<br>
> +CallingConv Demangler::demangleCallingConvention() {<br>
> +  switch (MangledName.popFront()) {<br>
> +  case 'A':<br>
> +  case 'B':<br>
> +    return CallingConv::Cdecl;<br>
> +  case 'C':<br>
> +  case 'D':<br>
> +    return CallingConv::Pascal;<br>
> +  case 'E':<br>
> +  case 'F':<br>
> +    return CallingConv::Thiscall;<br>
> +  case 'G':<br>
> +  case 'H':<br>
> +    return CallingConv::Stdcall;<br>
> +  case 'I':<br>
> +  case 'J':<br>
> +    return CallingConv::Fastcall;<br>
> +  case 'M':<br>
> +  case 'N':<br>
> +    return CallingConv::Clrcall;<br>
> +  case 'O':<br>
> +  case 'P':<br>
> +    return CallingConv::Eabi;<br>
> +  case 'Q':<br>
> +    return CallingConv::Vectorcall;<br>
> +  }<br>
> +<br>
> +  return CallingConv::None;<br>
> +};<br>
> +<br>
> +StorageClass Demangler::demangleVariableStorageClass() {<br>
> +  assert(std::isdigit(MangledName.front()));<br>
> +<br>
> +  switch (MangledName.popFront()) {<br>
> +  case '0':<br>
> +    return StorageClass::PrivateStatic;<br>
> +  case '1':<br>
> +    return StorageClass::ProtectedStatic;<br>
> +  case '2':<br>
> +    return StorageClass::PublicStatic;<br>
> +  case '3':<br>
> +    return StorageClass::Global;<br>
> +  case '4':<br>
> +    return StorageClass::FunctionLocalStatic;<br>
> +  }<br>
> +  Error = true;<br>
> +  return StorageClass::None;<br>
> +}<br>
> +<br>
> +Qualifiers Demangler::demangleVariablQ_ifiers() {<br>
> +  SwapAndRestore<StringView> RestoreOnError(MangledName, MangledName);<br>
> +  RestoreOnError.shouldRestore(false);<br>
> +<br>
> +  switch (MangledName.popFront()) {<br>
> +  case 'A':<br>
> +    return Q_None;<br>
> +  case 'B':<br>
> +    return Q_Const;<br>
> +  case 'C':<br>
> +    return Q_Volatile;<br>
> +  case 'D':<br>
> +    return Qualifiers(Q_Const | Q_Volatile);<br>
> +  case 'E':<br>
> +    return Q_Far;<br>
> +  case 'F':<br>
> +    return Qualifiers(Q_Const | Q_Far);<br>
> +  case 'G':<br>
> +    return Qualifiers(Q_Volatile | Q_Far);<br>
> +  case 'H':<br>
> +    return Qualifiers(Q_Const | Q_Volatile | Q_Far);<br>
> +  }<br>
> +<br>
> +  Error = true;<br>
> +  RestoreOnError.shouldRestore(true);<br>
> +  return Q_None;<br>
> +}<br>
> +<br>
> +Qualifiers Demangler::demangleReturnTypQ_ifiers() {<br>
> +  if (!MangledName.consumeFront("?"))<br>
> +    return Q_None;<br>
> +<br>
> +  SwapAndRestore<StringView> RestoreOnError(MangledName, MangledName);<br>
> +  RestoreOnError.shouldRestore(false);<br>
> +<br>
> +  switch (MangledName.popFront()) {<br>
> +  case 'A':<br>
> +    return Q_None;<br>
> +  case 'B':<br>
> +    return Q_Const;<br>
> +  case 'C':<br>
> +    return Q_Volatile;<br>
> +  case 'D':<br>
> +    return Qualifiers(Q_Const | Q_Volatile);<br>
> +  }<br>
> +<br>
> +  Error = true;<br>
> +  RestoreOnError.shouldRestore(true);<br>
> +  return Q_None;<br>
> +}<br>
> +<br>
> +Qualifiers Demangler::demangleQualifiers(QualifierMangleLocation Location) {<br>
> +  if (Location == QualifierMangleLocation::Detect) {<br>
> +    switch (MangledName.front()) {<br>
> +    case 'Q':<br>
> +    case 'R':<br>
> +    case 'S':<br>
> +    case 'T':<br>
> +      Location = QualifierMangleLocation::Member;<br>
> +      break;<br>
> +    case 'A':<br>
> +    case 'B':<br>
> +    case 'C':<br>
> +    case 'D':<br>
> +      Location = QualifierMangleLocation::NonMember;<br>
> +      break;<br>
> +    default:<br>
> +      Error = true;<br>
> +      return Q_None;<br>
> +    }<br>
> +  }<br>
> +<br>
> +  if (Location == QualifierMangleLocation::Member) {<br>
> +    switch (MangledName.popFront()) {<br>
> +    // Member qualifiers<br>
> +    case 'Q':<br>
> +      return Q_None;<br>
> +    case 'R':<br>
> +      return Q_Const;<br>
> +    case 'S':<br>
> +      return Q_Volatile;<br>
> +    case 'T':<br>
> +      return Qualifiers(Q_Const | Q_Volatile);<br>
> +    }<br>
> +  } else {<br>
> +    switch (MangledName.popFront()) {<br>
> +    // Non-Member qualifiers<br>
> +    case 'A':<br>
> +      return Q_None;<br>
> +    case 'B':<br>
> +      return Q_Const;<br>
> +    case 'C':<br>
> +      return Q_Volatile;<br>
> +    case 'D':<br>
> +      return Qualifiers(Q_Const | Q_Volatile);<br>
> +    }<br>
> +  }<br>
> +  Error = true;<br>
> +  return Q_None;<br>
> +}<br>
> +<br>
> +// <variable-type> ::= <type> <cvr-qualifiers><br>
> +//                 ::= <type> <pointee-cvr-qualifiers> # pointers, references<br>
> +Type *Demangler::demangleType(QualifierMangleMode QMM) {<br>
> +  Qualifiers Quals = Q_None;<br>
> +  if (QMM == QualifierMangleMode::Mangle)<br>
> +    Quals = Qualifiers(Quals | demangleQualifiers());<br>
> +  else if (QMM == QualifierMangleMode::Result) {<br>
> +    if (MangledName.consumeFront('?'))<br>
> +      Quals = Qualifiers(Quals | demangleQualifiers());<br>
> +  }<br>
> +<br>
> +  Type *Ty = nullptr;<br>
> +  switch (MangledName.front()) {<br>
> +  case 'T': // union<br>
> +  case 'U': // struct<br>
> +  case 'V': // class<br>
> +  case 'W': // enum<br>
> +    Ty = demangleClassType();<br>
> +    break;<br>
> +  case 'A': // foo &<br>
> +  case 'P': // foo *<br>
> +  case 'Q': // foo *const<br>
> +  case 'R': // foo *volatile<br>
> +  case 'S': // foo *const volatile<br>
> +    Ty = demanglePointerType();<br>
> +    break;<br>
> +  case 'Y':<br>
> +    Ty = demangleArrayType();<br>
> +    break;<br>
> +  default:<br>
> +    Ty = demangleBasicType();<br>
> +    break;<br>
> +  }<br>
> +  Ty->Quals = Qualifiers(Ty->Quals | Quals);<br>
> +  return Ty;<br>
> +}<br>
> +<br>
> +static bool functionHasThisPtr(const FunctionType &Ty) {<br>
> +  assert(Ty.Prim == PrimTy::Function);<br>
> +  if (Ty.FunctionClass & Global)<br>
> +    return false;<br>
> +  if (Ty.FunctionClass & Static)<br>
> +    return false;<br>
> +  return true;<br>
> +}<br>
> +<br>
> +ReferenceKind Demangler::demangleReferenceKind() {<br>
> +  if (MangledName.consumeFront('G'))<br>
> +    return ReferenceKind::LValueRef;<br>
> +  else if (MangledName.consumeFront('H'))<br>
> +    return ReferenceKind::RValueRef;<br>
> +  return ReferenceKind::None;<br>
> +}<br>
> +<br>
> +Type *Demangler::demangleFunctionEncoding() {<br>
> +  FunctionType *FTy = new (Arena) FunctionType;<br>
> +<br>
> +  FTy->Prim = PrimTy::Function;<br>
> +  FTy->FunctionClass = (FuncClass)demangleFunctionClass();<br>
> +  if (functionHasThisPtr(*FTy)) {<br>
> +    FTy->Quals = demanglePointerExtQualifiers();<br>
> +    FTy->RefKind = demangleReferenceKind();<br>
> +    FTy->Quals = Qualifiers(FTy->Quals | demangleQualifiers());<br>
> +  }<br>
> +<br>
> +  // Fields that appear on both member and non-member functions.<br>
> +  FTy->CallConvention = demangleCallingConvention();<br>
> +<br>
> +  // <return-type> ::= <type><br>
> +  //               ::= @ # structors (they have no declared return type)<br>
> +  bool IsStructor = MangledName.consumeFront('@');<br>
> +  if (!IsStructor)<br>
> +    FTy->ReturnType = demangleType(QualifierMangleMode::Result);<br>
> +<br>
> +  FTy->Params = demangleParameterList();<br>
> +<br>
> +  return FTy;<br>
> +}<br>
> +<br>
> +// Reads a primitive type.<br>
> +Type *Demangler::demangleBasicType() {<br>
> +  Type *Ty = new (Arena) Type;<br>
> +<br>
> +  switch (MangledName.popFront()) {<br>
> +  case 'X':<br>
> +    Ty->Prim = PrimTy::Void;<br>
> +    break;<br>
> +  case 'D':<br>
> +    Ty->Prim = PrimTy::Char;<br>
> +    break;<br>
> +  case 'C':<br>
> +    Ty->Prim = PrimTy::Schar;<br>
> +    break;<br>
> +  case 'E':<br>
> +    Ty->Prim = PrimTy::Uchar;<br>
> +    break;<br>
> +  case 'F':<br>
> +    Ty->Prim = PrimTy::Short;<br>
> +    break;<br>
> +  case 'G':<br>
> +    Ty->Prim = PrimTy::Ushort;<br>
> +    break;<br>
> +  case 'H':<br>
> +    Ty->Prim = PrimTy::Int;<br>
> +    break;<br>
> +  case 'I':<br>
> +    Ty->Prim = PrimTy::Uint;<br>
> +    break;<br>
> +  case 'J':<br>
> +    Ty->Prim = PrimTy::Long;<br>
> +    break;<br>
> +  case 'K':<br>
> +    Ty->Prim = PrimTy::Ulong;<br>
> +    break;<br>
> +  case 'M':<br>
> +    Ty->Prim = PrimTy::Float;<br>
> +    break;<br>
> +  case 'N':<br>
> +    Ty->Prim = PrimTy::Double;<br>
> +    break;<br>
> +  case 'O':<br>
> +    Ty->Prim = PrimTy::Ldouble;<br>
> +    break;<br>
> +  case '_': {<br>
> +    switch (MangledName.popFront()) {<br>
> +    case 'N':<br>
> +      Ty->Prim = PrimTy::Bool;<br>
> +      break;<br>
> +    case 'J':<br>
> +      Ty->Prim = PrimTy::Int64;<br>
> +      break;<br>
> +    case 'K':<br>
> +      Ty->Prim = PrimTy::Uint64;<br>
> +      break;<br>
> +    case 'W':<br>
> +      Ty->Prim = PrimTy::Wchar;<br>
> +      break;<br>
> +    }<br>
> +    break;<br>
> +  }<br>
> +  }<br>
> +  return Ty;<br>
> +}<br>
> +<br>
> +UdtType *Demangler::demangleClassType() {<br>
> +  UdtType *UTy = new (Arena) UdtType;<br>
> +<br>
> +  switch (MangledName.popFront()) {<br>
> +  case 'T':<br>
> +    UTy->Prim = PrimTy::Union;<br>
> +    break;<br>
> +  case 'U':<br>
> +    UTy->Prim = PrimTy::Struct;<br>
> +    break;<br>
> +  case 'V':<br>
> +    UTy->Prim = PrimTy::Class;<br>
> +    break;<br>
> +  case 'W':<br>
> +    if (MangledName.popFront() != '4') {<br>
> +      Error = true;<br>
> +      return nullptr;<br>
> +    }<br>
> +    UTy->Prim = PrimTy::Enum;<br>
> +    break;<br>
> +  default:<br>
> +    assert(false);<br>
> +  }<br>
> +<br>
> +  UTy->UdtName = demangleName();<br>
> +  return UTy;<br>
> +}<br>
> +<br>
> +// <pointer-type> ::= E? <pointer-cvr-qualifiers> <ext-qualifiers> <type><br>
> +//                       # the E is required for 64-bit non-static pointers<br>
> +PointerType *Demangler::demanglePointerType() {<br>
> +  PointerType *Pointer = new (Arena) PointerType;<br>
> +<br>
> +  Pointer->Quals = Q_None;<br>
> +  switch (MangledName.popFront()) {<br>
> +  case 'A':<br>
> +    Pointer->Prim = PrimTy::Ref;<br>
> +    break;<br>
> +  case 'P':<br>
> +    Pointer->Prim = PrimTy::Ptr;<br>
> +    break;<br>
> +  case 'Q':<br>
> +    Pointer->Prim = PrimTy::Ptr;<br>
> +    Pointer->Quals = Q_Const;<br>
> +    break;<br>
> +  case 'R':<br>
> +    Pointer->Quals = Q_Volatile;<br>
> +    Pointer->Prim = PrimTy::Ptr;<br>
> +    break;<br>
> +  case 'S':<br>
> +    Pointer->Quals = Qualifiers(Q_Const | Q_Volatile);<br>
> +    Pointer->Prim = PrimTy::Ptr;<br>
> +    break;<br>
> +  default:<br>
> +    assert(false && "Ty is not a pointer type!");<br>
> +  }<br>
> +<br>
> +  if (MangledName.consumeFront("6")) {<br>
> +    FunctionType *FTy = new (Arena) FunctionType;<br>
> +    FTy->Prim = PrimTy::Function;<br>
> +    FTy->CallConvention = demangleCallingConvention();<br>
> +<br>
> +    FTy->ReturnType = demangleType(QualifierMangleMode::Drop);<br>
> +    FTy->Params = demangleParameterList();<br>
> +<br>
> +    if (!MangledName.consumeFront("@Z"))<br>
> +      MangledName.consumeFront("Z");<br>
> +<br>
> +    Pointer->Pointee = FTy;<br>
> +    return Pointer;<br>
> +  }<br>
> +<br>
> +  Qualifiers ExtQuals = demanglePointerExtQualifiers();<br>
> +  Pointer->Quals = Qualifiers(Pointer->Quals | ExtQuals);<br>
> +<br>
> +  Pointer->Pointee = demangleType(QualifierMangleMode::Mangle);<br>
> +  return Pointer;<br>
> +}<br>
> +<br>
> +Qualifiers Demangler::demanglePointerExtQualifiers() {<br>
> +  Qualifiers Quals = Q_None;<br>
> +  if (MangledName.consumeFront('E'))<br>
> +    Quals = Qualifiers(Quals | Q_Pointer64);<br>
> +  if (MangledName.consumeFront('I'))<br>
> +    Quals = Qualifiers(Quals | Q_Restrict);<br>
> +  if (MangledName.consumeFront('F'))<br>
> +    Quals = Qualifiers(Quals | Q_Unaligned);<br>
> +<br>
> +  return Quals;<br>
> +}<br>
> +<br>
> +ArrayType *Demangler::demangleArrayType() {<br>
> +  assert(MangledName.front() == 'Y');<br>
> +  MangledName.popFront();<br>
> +<br>
> +  int Dimension = demangleNumber();<br>
> +  if (Dimension <= 0) {<br>
> +    Error = true;<br>
> +    return nullptr;<br>
> +  }<br>
> +<br>
> +  ArrayType *ATy = new (Arena) ArrayType;<br>
> +  ArrayType *Dim = ATy;<br>
> +  for (int I = 0; I < Dimension; ++I) {<br>
> +    Dim->Prim = PrimTy::Array;<br>
> +    Dim->ArrayDimension = demangleNumber();<br>
> +    Dim->NextDimension = new (Arena) ArrayType;<br>
> +    Dim = Dim->NextDimension;<br>
> +  }<br>
> +<br>
> +  if (MangledName.consumeFront("$$C")) {<br>
> +    if (MangledName.consumeFront("B"))<br>
> +      ATy->Quals = Q_Const;<br>
> +    else if (MangledName.consumeFront("C") || MangledName.consumeFront("D"))<br>
> +      ATy->Quals = Qualifiers(Q_Const | Q_Volatile);<br>
> +    else if (!MangledName.consumeFront("A"))<br>
> +      Error = true;<br>
> +  }<br>
> +<br>
> +  ATy->ElementType = demangleType(QualifierMangleMode::Drop);<br>
> +  Dim->ElementType = ATy->ElementType;<br>
> +  return ATy;<br>
> +}<br>
> +<br>
> +// Reads a function or a template parameters.<br>
> +ParamList Demangler::demangleParameterList() {<br>
> +  // Within the same parameter list, you can backreference the first 10 types.<br>
> +  Type *BackRef[10];<br>
> +  int Idx = 0;<br>
> +<br>
> +  ParamList *Head;<br>
> +  ParamList **Current = &Head;<br>
> +  while (!Error && !MangledName.startsWith('@') &&<br>
> +         !MangledName.startsWith('Z')) {<br>
> +    if (startsWithDigit(MangledName)) {<br>
> +      int N = MangledName[0] - '0';<br>
> +      if (N >= Idx) {<br>
> +        Error = true;<br>
> +        return {};<br>
> +      }<br>
> +      MangledName = MangledName.dropFront();<br>
> +<br>
> +      *Current = new (Arena) ParamList;<br>
> +      (*Current)->Current = BackRef[N]->clone(Arena);<br>
> +      Current = &(*Current)->Next;<br>
> +      continue;<br>
> +    }<br>
> +<br>
> +    size_t ArrayDimension = MangledName.size();<br>
> +<br>
> +    *Current = new (Arena) ParamList;<br>
> +    (*Current)->Current = demangleType(QualifierMangleMode::Drop);<br>
> +<br>
> +    // Single-letter types are ignored for backreferences because<br>
> +    // memorizing them doesn't save anything.<br>
> +    if (Idx <= 9 && ArrayDimension - MangledName.size() > 1)<br>
> +      BackRef[Idx++] = (*Current)->Current;<br>
> +    Current = &(*Current)->Next;<br>
> +  }<br>
> +<br>
> +  return *Head;<br>
> +}<br>
> +<br>
> +void Demangler::output() {<br>
> +  // Converts an AST to a string.<br>
> +  //<br>
> +  // Converting an AST representing a C++ type to a string is tricky due<br>
> +  // to the bad grammar of the C++ declaration inherited from C. You have<br>
> +  // to construct a string from inside to outside. For example, if a type<br>
> +  // X is a pointer to a function returning int, the order you create a<br>
> +  // string becomes something like this:<br>
> +  //<br>
> +  //   (1) X is a pointer: *X<br>
> +  //   (2) (1) is a function returning int: int (*X)()<br>
> +  //<br>
> +  // So you cannot construct a result just by appending strings to a result.<br>
> +  //<br>
> +  // To deal with this, we split the function into two. outputPre() writes<br>
> +  // the "first half" of type declaration, and outputPost() writes the<br>
> +  // "second half". For example, outputPre() writes a return type for a<br>
> +  // function and outputPost() writes an parameter list.<br>
> +  Type::outputPre(OS, *SymbolType);<br>
> +  outputName(OS, SymbolName);<br>
> +  Type::outputPost(OS, *SymbolType);<br>
> +<br>
> +  // Null terminate the buffer.<br>
> +  OS << '\0';<br>
> +}<br>
> +<br>
> +char *llvm::microsoftDemangle(const char *MangledName, char *Buf, size_t *N,<br>
> +                              int *Status) {<br>
> +  OutputStream OS = OutputStream::create(Buf, N, 1024);<br>
> +<br>
> +  Demangler D(OS, StringView(MangledName));<br>
> +  D.parse();<br>
> +<br>
> +  if (D.Error)<br>
> +    *Status = llvm::demangle_invalid_mangled_name;<br>
> +  else<br>
> +    *Status = llvm::demangle_success;<br>
> +<br>
> +  D.output();<br>
> +  return OS.getBuffer();<br>
> +}<br>
><br>
> Added: llvm/trunk/test/Demangle/ms-basic.test<br>
> URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Demangle/ms-basic.test?rev=337584&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Demangle/ms-basic.test?rev=337584&view=auto</a><br>
> ==============================================================================<br>
> --- llvm/trunk/test/Demangle/ms-basic.test (added)<br>
> +++ llvm/trunk/test/Demangle/ms-basic.test Fri Jul 20 10:27:48 2018<br>
> @@ -0,0 +1,230 @@<br>
> +; RUN: llvm-undname < %s | FileCheck %s<br>
> +<br>
> +; CHECK-NOT: Invalid mangled name<br>
> +<br>
> +?x@@3HA<br>
> +; CHECK: int x<br>
> +<br>
> +?x@@3PEAHEA<br>
> +; CHECK: int *x<br>
> +<br>
> +?x@@3PEAPEAHEA<br>
> +; CHECK: int **x<br>
> +<br>
> +?x@@3PEAY02HEA<br>
> +; CHECK: int (*x)[3]<br>
> +<br>
> +?x@@3PEAY124HEA<br>
> +; CHECK: int (*x)[3][5]<br>
> +<br>
> +?x@@3PEAY02$$CBHEA<br>
> +; CHECK: int const (*x)[3]<br>
> +<br>
> +?x@@3PEAEEA<br>
> +; CHECK: unsigned char *x<br>
> +<br>
> +?x@@3PEAY1NKM@5HEA<br>
> +; CHECK: int (*x)[3500][6]<br>
> +<br>
> +?x@@YAXMH@Z<br>
> +; CHECK: void __cdecl x(float, int)<br>
> +<br>
> +?x@@3P6AHMNH@ZEA<br>
> +; CHECK: int __cdecl (*x)(float, double, int)<br>
> +<br>
> +?x@@3P6AHP6AHM@ZN@ZEA<br>
> +; CHECK: int __cdecl (*x)(int __cdecl (*)(float), double)<br>
> +<br>
> +?x@@3P6AHP6AHM@Z0@ZEA<br>
> +; CHECK: int __cdecl (*x)(int __cdecl (*)(float), int __cdecl (*)(float))<br>
> +<br>
> +?x@ns@@3HA<br>
> +; CHECK: int ns::x<br>
> +<br>
> +; Microsoft's undname doesn't handle Q correctly or the multiple occurrences<br>
> +; of the const modifier.  So the results here differ, but ours are correct.<br>
> +?x@@3PEAHEA<br>
> +; CHECK: int *x<br>
> +<br>
> +?x@@3PEBHEB<br>
> +; CHECK: int const *x<br>
> +<br>
> +?x@@3QEAHEA<br>
> +; CHECK: int *const x<br>
> +<br>
> +?x@@3QEBHEB<br>
> +; CHECK: int const *const x<br>
> +<br>
> +<br>
> +?x@@3AEBHEB<br>
> +; CHECK: int const &x<br>
> +<br>
> +?x@@3PEAUty@@EA<br>
> +; CHECK: struct ty *x<br>
> +<br>
> +?x@@3PEATty@@EA<br>
> +; CHECK: union ty *x<br>
> +<br>
> +?x@@3PEAUty@@EA<br>
> +; CHECK: struct ty *x<br>
> +<br>
> +?x@@3PEAW4ty@@EA<br>
> +; CHECK: enum ty *x<br>
> +<br>
> +?x@@3PEAVty@@EA<br>
> +; CHECK: class ty *x<br>
> +<br>
> +?x@@3PEAV?$tmpl@H@@EA<br>
> +; CHECK: class tmpl<int> *x<br>
> +<br>
> +?x@@3PEAU?$tmpl@H@@EA<br>
> +; CHECK: struct tmpl<int> *x<br>
> +<br>
> +?x@@3PEAT?$tmpl@H@@EA<br>
> +; CHECK: union tmpl<int> *x<br>
> +<br>
> +?instance@@3Vklass@@A<br>
> +; CHECK: class klass instance<br>
> +<br>
> +?instance$initializer$@@3P6AXXZEA<br>
> +; CHECK: void __cdecl (*instance$initializer$)(void)<br>
> +<br>
> +??0klass@@QEAA@XZ<br>
> +; CHECK: __cdecl klass::klass(void)<br>
> +<br>
> +??1klass@@QEAA@XZ<br>
> +; CHECK: __cdecl klass::~klass(void)<br>
> +<br>
> +?x@@YAHPEAVklass@@AEAV1@@Z<br>
> +; CHECK: int __cdecl x(class klass *, class klass &)<br>
> +<br>
> +?x@ns@@3PEAV?$klass@HH@1@EA<br>
> +; CHECK: class ns::klass<int, int> *ns::x<br>
> +<br>
> +?fn@?$klass@H@ns@@QEBAIXZ<br>
> +; CHECK: unsigned int __cdecl ns::klass<int>::fn(void) const<br>
> +<br>
> +<br>
> +??4klass@@QEAAAEBV0@AEBV0@@Z<br>
> +; CHECK: class klass const &__cdecl klass::operator=(class klass const &)<br>
> +<br>
> +??7klass@@QEAA_NXZ<br>
> +; CHECK: bool __cdecl klass::operator!(void)<br>
> +<br>
> +??8klass@@QEAA_NAEBV0@@Z<br>
> +; CHECK: bool __cdecl klass::operator==(class klass const &)<br>
> +<br>
> +??9klass@@QEAA_NAEBV0@@Z<br>
> +; CHECK: bool __cdecl klass::operator!=(class klass const &)<br>
> +<br>
> +??Aklass@@QEAAH_K@Z<br>
> +; CHECK: int __cdecl klass::operator[](unsigned __int64)<br>
> +<br>
> +??Cklass@@QEAAHXZ<br>
> +; CHECK: int __cdecl klass::operator->(void)<br>
> +<br>
> +??Dklass@@QEAAHXZ<br>
> +; CHECK: int __cdecl klass::operator*(void)<br>
> +<br>
> +??Eklass@@QEAAHXZ<br>
> +; CHECK: int __cdecl klass::operator++(void)<br>
> +<br>
> +??Eklass@@QEAAHH@Z<br>
> +; CHECK: int __cdecl klass::operator++(int)<br>
> +<br>
> +??Fklass@@QEAAHXZ<br>
> +; CHECK: int __cdecl klass::operator--(void)<br>
> +<br>
> +??Fklass@@QEAAHH@Z<br>
> +; CHECK: int __cdecl klass::operator--(int)<br>
> +<br>
> +??Hklass@@QEAAHH@Z<br>
> +; CHECK: int __cdecl klass::operator+(int)<br>
> +<br>
> +??Gklass@@QEAAHH@Z<br>
> +; CHECK: int __cdecl klass::operator-(int)<br>
> +<br>
> +??Iklass@@QEAAHH@Z<br>
> +; CHECK: int __cdecl klass::operator&(int)<br>
> +<br>
> +??Jklass@@QEAAHH@Z<br>
> +; CHECK: int __cdecl klass::operator->*(int)<br>
> +<br>
> +??Kklass@@QEAAHH@Z<br>
> +; CHECK: int __cdecl klass::operator/(int)<br>
> +<br>
> +??Mklass@@QEAAHH@Z<br>
> +; CHECK: int __cdecl klass::operator<(int)<br>
> +<br>
> +??Nklass@@QEAAHH@Z<br>
> +; CHECK: int __cdecl klass::operator<=(int)<br>
> +<br>
> +??Oklass@@QEAAHH@Z<br>
> +; CHECK: int __cdecl klass::operator>(int)<br>
> +<br>
> +??Pklass@@QEAAHH@Z<br>
> +; CHECK: int __cdecl klass::operator>=(int)<br>
> +<br>
> +??Qklass@@QEAAHH@Z<br>
> +; CHECK: int __cdecl klass::operator,(int)<br>
> +<br>
> +??Rklass@@QEAAHH@Z<br>
> +; CHECK: int __cdecl klass::operator()(int)<br>
> +<br>
> +??Sklass@@QEAAHXZ<br>
> +; CHECK: int __cdecl klass::operator~(void)<br>
> +<br>
> +??Tklass@@QEAAHH@Z<br>
> +; CHECK: int __cdecl klass::operator^(int)<br>
> +<br>
> +??Uklass@@QEAAHH@Z<br>
> +; CHECK: int __cdecl klass::operator|(int)<br>
> +<br>
> +??Vklass@@QEAAHH@Z<br>
> +; CHECK: int __cdecl klass::operator&&(int)<br>
> +<br>
> +??Wklass@@QEAAHH@Z<br>
> +; CHECK: int __cdecl klass::operator||(int)<br>
> +<br>
> +??Xklass@@QEAAHH@Z<br>
> +; CHECK: int __cdecl klass::operator*=(int)<br>
> +<br>
> +??Yklass@@QEAAHH@Z<br>
> +; CHECK: int __cdecl klass::operator+=(int)<br>
> +<br>
> +??Zklass@@QEAAHH@Z<br>
> +; CHECK: int __cdecl klass::operator-=(int)<br>
> +<br>
> +??_0klass@@QEAAHH@Z<br>
> +; CHECK: int __cdecl klass::operator/=(int)<br>
> +<br>
> +??_1klass@@QEAAHH@Z<br>
> +; CHECK: int __cdecl klass::operator%=(int)<br>
> +<br>
> +??_2klass@@QEAAHH@Z<br>
> +; CHECK: int __cdecl klass::operator>>=(int)<br>
> +<br>
> +??_3klass@@QEAAHH@Z<br>
> +; CHECK: int __cdecl klass::operator<<=(int)<br>
> +<br>
> +??_6klass@@QEAAHH@Z<br>
> +; CHECK: int __cdecl klass::operator^=(int)<br>
> +<br>
> +??6@YAAEBVklass@@AEBV0@H@Z<br>
> +; CHECK: class klass const &__cdecl operator<<(class klass const &, int)<br>
> +<br>
> +??5@YAAEBVklass@@AEBV0@_K@Z<br>
> +; CHECK: class klass const &__cdecl operator>>(class klass const &, unsigned __int64)<br>
> +<br>
> +??2@YAPEAX_KAEAVklass@@@Z<br>
> +; CHECK: void *__cdecl operator new(unsigned __int64, class klass &)<br>
> +<br>
> +??_U@YAPEAX_KAEAVklass@@@Z<br>
> +; CHECK: void *__cdecl operator new[](unsigned __int64, class klass &)<br>
> +<br>
> +??3@YAXPEAXAEAVklass@@@Z<br>
> +; CHECK: void __cdecl operator delete(void *, class klass &)<br>
> +<br>
> +??_V@YAXPEAXAEAVklass@@@Z<br>
> +; CHECK: void __cdecl operator delete[](void *, class klass &)<br>
> +<br>
><br>
> Added: llvm/trunk/test/Demangle/ms-mangle.test<br>
> URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Demangle/ms-mangle.test?rev=337584&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Demangle/ms-mangle.test?rev=337584&view=auto</a><br>
> ==============================================================================<br>
> --- llvm/trunk/test/Demangle/ms-mangle.test (added)<br>
> +++ llvm/trunk/test/Demangle/ms-mangle.test Fri Jul 20 10:27:48 2018<br>
> @@ -0,0 +1,363 @@<br>
> +; These tests are based on clang/test/CodeGenCXX/mangle-ms.cpp<br>
> +; RUN: llvm-undname < %s | FileCheck %s<br>
> +<br>
> +; CHECK-NOT: Invalid mangled name<br>
> +<br>
> +?k@@3PTfoo@@DT1<br>
> +<br>
> +<br>
> +?a@@3HA<br>
> +; CHECK: int a<br>
> +<br>
> +?b@N@@3HA<br>
> +; CHECK: int N::b<br>
> +<br>
> +?anonymous@?A@N@@3HA<br>
> +; CHECK: int N::`anonymous namespace'::anonymous<br>
> +<br>
> +?_c@@YAHXZ<br>
> +; CHECK: int __cdecl _c(void)<br>
> +<br>
> +?d@foo@@0FB<br>
> +; CHECK: static short const foo::d<br>
> +<br>
> +?e@foo@@1JC<br>
> +; CHECK: static long volatile foo::e<br>
> +<br>
> +?f@foo@@2DD<br>
> +; CHECK: static char const volatile foo::f<br>
> +<br>
> +??0foo@@QAE@XZ<br>
> +; CHECK: __thiscall foo::foo(void)<br>
> +<br>
> +??0foo@@QEAA@XZ<br>
> +; CHECK: __cdecl foo::foo(void)<br>
> +<br>
> +??1foo@@QAE@XZ<br>
> +; CHECK: __thiscall foo::~foo(void)<br>
> +<br>
> +??1foo@@QEAA@XZ<br>
> +; CHECK: __cdecl foo::~foo(void)<br>
> +<br>
> +??0foo@@QAE@H@Z<br>
> +; CHECK: __thiscall foo::foo(int)<br>
> +<br>
> +??0foo@@QEAA@H@Z<br>
> +; CHECK: __cdecl foo::foo(int)<br>
> +<br>
> +??0foo@@QAE@PAD@Z<br>
> +; CHECK: __thiscall foo::foo(char *)<br>
> +<br>
> +??0foo@@QEAA@PEAD@Z<br>
> +; CHECK: __cdecl foo::foo(char *)<br>
> +<br>
> +?bar@@YA?AVfoo@@XZ<br>
> +; CHECK: class foo __cdecl bar(void)<br>
> +<br>
> +?bar@@YA?AVfoo@@XZ<br>
> +; CHECK: class foo __cdecl bar(void)<br>
> +<br>
> +??Hfoo@@QAEHH@Z<br>
> +; CHECK: int __thiscall foo::operator+(int)<br>
> +<br>
> +??Hfoo@@QEAAHH@Z<br>
> +; CHECK: int __cdecl foo::operator+(int)<br>
> +<br>
> +?static_method@foo@@SAPAV1@XZ<br>
> +; CHECK: static class foo *__cdecl foo::static_method(void)<br>
> +<br>
> +?static_method@foo@@SAPEAV1@XZ<br>
> +; CHECK: static class foo *__cdecl foo::static_method(void)<br>
> +<br>
> +?g@bar@@2HA<br>
> +; CHECK: static int bar::g<br>
> +<br>
> +; undname returns `int *h1`, but it is a bug in their demangler.  Their mangler<br>
> +; correctly mangles `int *h1` as ?h1@3PAHA and `int * const h1` as ?h1@3QAHA<br>
> +?h1@@3QAHA<br>
> +; CHECK: int *const h1<br>
> +<br>
> +?h2@@3QBHB<br>
> +; CHECK: int const *const h2<br>
> +<br>
> +?h3@@3QIAHIA<br>
> +; CHECK: int * __restrict const h3<br>
> +<br>
> +?h3@@3QEIAHEIA<br>
> +; CHECK: int * __restrict const h3<br>
> +<br>
> +?i@@3PAY0BE@HA<br>
> +; CHECK: int (*i)[20]<br>
> +<br>
> +?FunArr@@3PAY0BE@P6AHHH@ZA<br>
> +; CHECK: int __cdecl (*(*FunArr)[20])(int, int)<br>
> +<br>
> +?j@@3P6GHCE@ZA<br>
> +; CHECK: int __stdcall (*j)(signed char, unsigned char)<br>
> +<br>
> +<br>
> +; FIXME: We don't handle member pointers yet.<br>
> +; ?k@@3PTfoo@@DT1<br>
> +; FIXME: char const volatile foo::*k<br>
> +<br>
> +; ?k@@3PETfoo@@DET1<br>
> +; FIXME: char const volatile foo::*k<br>
> +<br>
> +; ?l@@3P8foo@@AEHH@ZQ1<br>
> +; FIXME: int __thiscall (foo::*l)(int)<br>
> +<br>
> +?g_cInt@@3HB<br>
> +; CHECK: int const g_cInt<br>
> +<br>
> +?g_vInt@@3HC<br>
> +; CHECK: int volatile g_vInt<br>
> +<br>
> +?g_cvInt@@3HD<br>
> +; CHECK: int const volatile g_cvInt<br>
> +<br>
> +?beta@@YI_N_J_W@Z<br>
> +; CHECK: bool __fastcall beta(__int64, wchar_t)<br>
> +<br>
> +?beta@@YA_N_J_W@Z<br>
> +; CHECK: bool __cdecl beta(__int64, wchar_t)<br>
> +<br>
> +?alpha@@YGXMN@Z<br>
> +; CHECK: void __stdcall alpha(float, double)<br>
> +<br>
> +?alpha@@YAXMN@Z<br>
> +; CHECK: void __cdecl alpha(float, double)<br>
> +<br>
> +?gamma@@YAXVfoo@@Ubar@@Tbaz@@W4quux@@@Z<br>
> +; CHECK: void __cdecl gamma(class foo, struct bar, union baz, enum quux)<br>
> +<br>
> +?gamma@@YAXVfoo@@Ubar@@Tbaz@@W4quux@@@Z<br>
> +; CHECK: void __cdecl gamma(class foo, struct bar, union baz, enum quux)<br>
> +<br>
> +?delta@@YAXQAHABJ@Z<br>
> +; CHECK: void __cdecl delta(int *const, long const &)<br>
> +<br>
> +?delta@@YAXQEAHAEBJ@Z<br>
> +; CHECK: void __cdecl delta(int *const, long const &)<br>
> +<br>
> +?epsilon@@YAXQAY19BE@H@Z<br>
> +; CHECK: void __cdecl epsilon(int (*const)[10][20])<br>
> +<br>
> +?epsilon@@YAXQEAY19BE@H@Z<br>
> +; CHECK: void __cdecl epsilon(int (*const)[10][20])<br>
> +<br>
> +?zeta@@YAXP6AHHH@Z@Z<br>
> +; CHECK: void __cdecl zeta(int __cdecl (*)(int, int))<br>
> +<br>
> +?zeta@@YAXP6AHHH@Z@Z<br>
> +; CHECK: void __cdecl zeta(int __cdecl (*)(int, int))<br>
> +<br>
> +??2@YAPAXI@Z<br>
> +; CHECK: void *__cdecl operator new(unsigned int)<br>
> +<br>
> +??3@YAXPAX@Z<br>
> +; CHECK: void __cdecl operator delete(void *)<br>
> +<br>
> +??_U@YAPAXI@Z<br>
> +; CHECK: void *__cdecl operator new[](unsigned int)<br>
> +<br>
> +??_V@YAXPAX@Z<br>
> +; CHECK: void __cdecl operator delete[](void *)<br>
> +<br>
> +?color1@@3PANA<br>
> +; CHECK: double *color1<br>
> +<br>
> +?color2@@3QBNB<br>
> +; CHECK: double const *const color2<br>
> +<br>
> +; FIXME-EXTRACONST: These tests fails because we print an extra const inside the parens.<br>
> +; ?color3@@3QAY02$$CBNA<br>
> +; FIXME-EXTRACONST: double const (*color3)[3]<br>
> +<br>
> +; ?color4@@3QAY02$$CBNA<br>
> +; FIXME-EXTRACONST: double const (*color4)[3]<br>
> +<br>
> +; FIXME-MEMBERPTR: We don't support member pointers yet.<br>
> +; ?memptr1@@3RESB@@HES1<br>
> +; FIXME-MEMBERPTR: volatile int B::*memptr2<br>
> +<br>
> +; ?memptr2@@3PESB@@HES1<br>
> +; FIXME: volatile int B::*memptr2<br>
> +<br>
> +; ?memptr3@@3REQB@@HEQ1<br>
> +; FIXME-MEMBERPTR: int B::* volatile memptr3<br>
> +<br>
> +; ?funmemptr1@@3RESB@@R6AHXZES1<br>
> +; FIXME-MEMBERPTR: int __cdecl (* volatile B::* volatile funmemptr1)(void)<br>
> +<br>
> +; ?funmemptr2@@3PESB@@R6AHXZES1<br>
> +; FIXME-MEMBERPTR: int __cdecl (* volatile B::*funmemptr2)(void)<br>
> +<br>
> +; ?funmemptr3@@3REQB@@P6AHXZEQ1<br>
> +; FIXME-MEMBERPTR: int __cdecl (* B::*volatile funmemptr3)(void)<br>
> +<br>
> +; ?memptrtofun1@@3R8B@@EAAXXZEQ1<br>
> +; FIXME-MEMBERPTR: void __cdecl (B::*volatile memptrtofun1)(void)<br>
> +<br>
> +; ?memptrtofun2@@3P8B@@EAAXXZEQ1<br>
> +; FIXME-MEMBERPTR: void __cdecl (B::*memptrtofun2)(void)<br>
> +<br>
> +; ?memptrtofun3@@3P8B@@EAAXXZEQ1<br>
> +; FIXME-MEMBERPTR: void __cdecl (B::*memptrtofun3)(void)<br>
> +<br>
> +; ?memptrtofun4@@3R8B@@EAAHXZEQ1<br>
> +; FIXME-MEMBERPTR: int __cdecl (B::* volatile memptrtofun4)(void)<br>
> +<br>
> +; ?memptrtofun5@@3P8B@@EAA?CHXZEQ1<br>
> +; FIXME-MEMBERPTR: int volatile __cdecl (B::*memptrtofun5)(void)<br>
> +<br>
> +; ?memptrtofun6@@3P8B@@EAA?BHXZEQ1<br>
> +; FIXME-MEMBERPTR: int const __cdecl (B::*memptrtofun6)(void)<br>
> +<br>
> +; ?memptrtofun7@@3R8B@@EAAP6AHXZXZEQ1<br>
> +; FIXME-MEMBERPTR: int __cdecl (*(__cdecl B::*volatile memptrtofun7)(void))(void)<br>
> +<br>
> +; ?memptrtofun8@@3P8B@@EAAR6AHXZXZEQ1<br>
> +; FIXME-MEMBERPTR: int __cdecl (*(__cdecl B::*memptrtofun8)(void))(void)<br>
> +<br>
> +; ?memptrtofun9@@3P8B@@EAAQ6AHXZXZEQ1<br>
> +; FIXME-MEMBERPTR: int __cdecl(*(__cdecl B::*memptrtofun9)(void))(void)<br>
> +<br>
> +<br>
> +?fooE@@YA?AW4E@@XZ<br>
> +; CHECK: enum E __cdecl fooE(void)<br>
> +<br>
> +?fooE@@YA?AW4E@@XZ<br>
> +; CHECK: enum E __cdecl fooE(void)<br>
> +<br>
> +?fooX@@YA?AVX@@XZ<br>
> +; CHECK: class X __cdecl fooX(void)<br>
> +<br>
> +?fooX@@YA?AVX@@XZ<br>
> +; CHECK: class X __cdecl fooX(void)<br>
> +<br>
> +?s0@PR13182@@3PADA<br>
> +; CHECK: char *PR13182::s0<br>
> +<br>
> +?s1@PR13182@@3PADA<br>
> +; CHECK: char *PR13182::s1<br>
> +<br>
> +?s2@PR13182@@3QBDB<br>
> +; CHECK: char const *const PR13182::s2<br>
> +<br>
> +?s3@PR13182@@3QBDB<br>
> +; CHECK: char const *const PR13182::s3<br>
> +<br>
> +?s4@PR13182@@3RCDC<br>
> +; CHECK: char volatile *volatile PR13182::s4<br>
> +<br>
> +?s5@PR13182@@3SDDD<br>
> +; CHECK: char const volatile *const volatile PR13182::s5<br>
> +<br>
> +; undname adds an extra const in here, but it seems like their bug.<br>
> +?s6@PR13182@@3PBQBDB<br>
> +; CHECK: char const *const *PR13182::s6<br>
> +<br>
> +; FIXME-EXTERNC: We don't properly support static locals in extern c functions yet.<br>
> +; ?local@?1??extern_c_func@@9@4HA<br>
> +; FIXME-EXTERNC: int `extern_c_func'::`2'::local<br>
> +<br>
> +; ?local@?1??extern_c_func@@9@4HA<br>
> +; FIXME-EXTERNC: int `extern_c_func'::`2'::local<br>
> +<br>
> +??2OverloadedNewDelete@@SAPAXI@Z<br>
> +; CHECK: static void *__cdecl OverloadedNewDelete::operator new(unsigned int)<br>
> +<br>
> +<br>
> +??_UOverloadedNewDelete@@SAPAXI@Z<br>
> +; CHECK: static void *__cdecl OverloadedNewDelete::operator new[](unsigned int)<br>
> +<br>
> +??3OverloadedNewDelete@@SAXPAX@Z<br>
> +; CHECK: static void __cdecl OverloadedNewDelete::operator delete(void *)<br>
> +<br>
> +<br>
> +??_VOverloadedNewDelete@@SAXPAX@Z<br>
> +; CHECK: static void __cdecl OverloadedNewDelete::operator delete[](void *)<br>
> +<br>
> +??HOverloadedNewDelete@@QAEHH@Z<br>
> +; CHECK: int __thiscall OverloadedNewDelete::operator+(int)<br>
> +<br>
> +??2OverloadedNewDelete@@SAPEAX_K@Z<br>
> +; CHECK: static void *__cdecl OverloadedNewDelete::operator new(unsigned __int64)<br>
> +<br>
> +??_UOverloadedNewDelete@@SAPEAX_K@Z<br>
> +; CHECK: static void *__cdecl OverloadedNewDelete::operator new[](unsigned __int64)<br>
> +<br>
> +??3OverloadedNewDelete@@SAXPEAX@Z<br>
> +; CHECK: static void __cdecl OverloadedNewDelete::operator delete(void *)<br>
> +<br>
> +<br>
> +??_VOverloadedNewDelete@@SAXPEAX@Z<br>
> +; CHECK: static void __cdecl OverloadedNewDelete::operator delete[](void *)<br>
> +<br>
> +??HOverloadedNewDelete@@QEAAHH@Z<br>
> +; CHECK: int __cdecl OverloadedNewDelete::operator+(int)<br>
> +<br>
> +<br>
> +??2TypedefNewDelete@@SAPAXI@Z<br>
> +; CHECK: static void *__cdecl TypedefNewDelete::operator new(unsigned int)<br>
> +<br>
> +<br>
> +??_UTypedefNewDelete@@SAPAXI@Z<br>
> +; CHECK: static void *__cdecl TypedefNewDelete::operator new[](unsigned int)<br>
> +<br>
> +??3TypedefNewDelete@@SAXPAX@Z<br>
> +; CHECK: static void __cdecl TypedefNewDelete::operator delete(void *)<br>
> +<br>
> +??_VTypedefNewDelete@@SAXPAX@Z<br>
> +; CHECK: static void __cdecl TypedefNewDelete::operator delete[](void *)<br>
> +<br>
> +?vector_func@@YQXXZ<br>
> +; CHECK: void __vectorcall vector_func(void)<br>
> +<br>
> +; FIXME-EXTERNC: We don't support extern C funcs currently.<br>
> +; ??$fn_tmpl@$1?extern_c_func@@YAXXZ@@YAXXZ<br>
> +; FIXME-EXTERNC: void __cdecl fn_tmpl<&void __cdecl extern_c_func(void)>(void)<br>
> +<br>
> +; ?overloaded_fn@@$$J0YAXXZ<br>
> +; FIXME-EXTERNC: extern \"C\" void __cdecl overloaded_fn(void)<br>
> +<br>
> +<br>
> +?f@UnnamedType@@YAXUT2@S@1@@Z<br>
> +; CHECK: void __cdecl UnnamedType::f(struct UnnamedType::S::T2)<br>
> +<br>
> +?f@UnnamedType@@YAXPAUT4@S@1@@Z<br>
> +; CHECK: void __cdecl UnnamedType::f(struct UnnamedType::S::T4 *)<br>
> +<br>
> +?f@UnnamedType@@YAXUT4@S@1@@Z<br>
> +; CHECK: void __cdecl UnnamedType::f(struct UnnamedType::S::T4)<br>
> +<br>
> +?f@UnnamedType@@YAXUT5@S@1@@Z<br>
> +; CHECK: void __cdecl UnnamedType::f(struct UnnamedType::S::T5)<br>
> +<br>
> +?f@UnnamedType@@YAXUT2@S@1@@Z<br>
> +; CHECK: void __cdecl UnnamedType::f(struct UnnamedType::S::T2)<br>
> +<br>
> +?f@UnnamedType@@YAXUT4@S@1@@Z<br>
> +; CHECK: void __cdecl UnnamedType::f(struct UnnamedType::S::T4)<br>
> +<br>
> +?f@UnnamedType@@YAXUT5@S@1@@Z<br>
> +; CHECK: void __cdecl UnnamedType::f(struct UnnamedType::S::T5)<br>
> +<br>
> +?f@Atomic@@YAXU?$_Atomic@H@__clang@@@Z<br>
> +; CHECK: void __cdecl Atomic::f(struct __clang::_Atomic<int>)<br>
> +<br>
> +?f@Complex@@YAXU?$_Complex@H@__clang@@@Z<br>
> +; CHECK: void __cdecl Complex::f(struct __clang::_Complex<int>)<br>
> +<br>
> +?f@Float16@@YAXU_Float16@__clang@@@Z<br>
> +; CHECK: void __cdecl Float16::f(struct __clang::_Float16)<br>
> +<br>
> +<br>
> +??0?$L@H@NS@@QEAA@XZ<br>
> +; CHECK: __cdecl NS::L<int>::L<int>(void)<br>
> +<br>
> +??0Bar@Foo@@QEAA@XZ<br>
> +; CHECK: __cdecl Foo::Bar::Bar(void)<br>
> +<br>
> +??0?$L@V?$H@PAH@PR26029@@@PR26029@@QAE@XZ<br>
> +; CHECK: __thiscall PR26029::L<class PR26029::H<int *>>::L<class PR26029::H<int *>>(void)<br>
><br>
> Added: llvm/trunk/test/Demangle/ms-windows.test<br>
> URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Demangle/ms-windows.test?rev=337584&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Demangle/ms-windows.test?rev=337584&view=auto</a><br>
> ==============================================================================<br>
> --- llvm/trunk/test/Demangle/ms-windows.test (added)<br>
> +++ llvm/trunk/test/Demangle/ms-windows.test Fri Jul 20 10:27:48 2018<br>
> @@ -0,0 +1,17 @@<br>
> +; See clang/test/CodeGenCXX/mangle-windows.cpp<br>
> +; These tests are based on clang/test/CodeGenCXX/mangle-ms.cpp<br>
> +; RUN: llvm-undname < %s | FileCheck %s<br>
> +<br>
> +; CHECK-NOT: Invalid mangled name<br>
> +<br>
> +?bar@Foo@@SGXXZ<br>
> +; CHECK: static void __stdcall Foo::bar(void)<br>
> +<br>
> +?bar@Foo@@QAGXXZ<br>
> +; CHECK: void __stdcall Foo::bar(void)<br>
> +<br>
> +?f2@@YIXXZ<br>
> +; CHECK: void __fastcall f2(void)<br>
> +<br>
> +?f1@@YGXXZ<br>
> +; CHECK: void __stdcall f1(void)<br>
><br>
> Modified: llvm/trunk/tools/LLVMBuild.txt<br>
> URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/LLVMBuild.txt?rev=337584&r1=337583&r2=337584&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/LLVMBuild.txt?rev=337584&r1=337583&r2=337584&view=diff</a><br>
> ==============================================================================<br>
> --- llvm/trunk/tools/LLVMBuild.txt (original)<br>
> +++ llvm/trunk/tools/LLVMBuild.txt Fri Jul 20 10:27:48 2018<br>
> @@ -50,6 +50,7 @@ subdirectories =<br>
>    llvm-rtdyld<br>
>    llvm-size<br>
>    llvm-split<br>
> + llvm-undname<br>
>    opt<br>
>    verify-uselistorder<br>
>   <br>
><br>
> Added: llvm/trunk/tools/llvm-undname/CMakeLists.txt<br>
> URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-undname/CMakeLists.txt?rev=337584&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-undname/CMakeLists.txt?rev=337584&view=auto</a><br>
> ==============================================================================<br>
> --- llvm/trunk/tools/llvm-undname/CMakeLists.txt (added)<br>
> +++ llvm/trunk/tools/llvm-undname/CMakeLists.txt Fri Jul 20 10:27:48 2018<br>
> @@ -0,0 +1,8 @@<br>
> +set(LLVM_LINK_COMPONENTS<br>
> +  Demangle<br>
> +  Support<br>
> +  )<br>
> +<br>
> +add_llvm_tool(llvm-undname<br>
> +  llvm-undname.cpp<br>
> +  )<br>
><br>
> Added: llvm/trunk/tools/llvm-undname/LLVMBuild.txt<br>
> URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-undname/LLVMBuild.txt?rev=337584&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-undname/LLVMBuild.txt?rev=337584&view=auto</a><br>
> ==============================================================================<br>
> --- llvm/trunk/tools/llvm-undname/LLVMBuild.txt (added)<br>
> +++ llvm/trunk/tools/llvm-undname/LLVMBuild.txt Fri Jul 20 10:27:48 2018<br>
> @@ -0,0 +1,23 @@<br>
> +;===- ./tools/llvm-undname/LLVMBuild.txt -----------------------*- Conf -*--===;<br>
> +;<br>
> +;                     The LLVM Compiler Infrastructure<br>
> +;<br>
> +; This file is distributed under the University of Illinois Open Source<br>
> +; License. See LICENSE.TXT for details.<br>
> +;<br>
> +;===------------------------------------------------------------------------===;<br>
> +;<br>
> +; This is an LLVMBuild description file for the components in this subdirectory.<br>
> +;<br>
> +</blockquote></div>