[llvm] [Demangle] extend DLangDemangle to be able to parse nearly all symbols (PR #92278)

via llvm-commits llvm-commits at lists.llvm.org
Wed May 15 08:08:38 PDT 2024


https://github.com/Mai-Lapyst created https://github.com/llvm/llvm-project/pull/92278

This patch extends very basic the Dlang mangler contained inside llvm / libLLVMDemangle so it accepts the majority of valid symbols according to the offical DLang abi (https://dlang.org/spec/abi.html#name_mangling).

>From f3b4e5ab8efe2db403a075133f749419556754a3 Mon Sep 17 00:00:00 2001
From: Mai-Lapyst <67418776+Mai-Lapyst at users.noreply.github.com>
Date: Wed, 15 May 2024 12:39:56 +0200
Subject: [PATCH] [Demangle] extend DLangDemangle to be able to parse nearly
 all symbols

---
 llvm/lib/Demangle/DLangDemangle.cpp           | 1203 ++++++++++++++++-
 llvm/unittests/Demangle/DLangDemangleTest.cpp |   63 +-
 2 files changed, 1231 insertions(+), 35 deletions(-)

diff --git a/llvm/lib/Demangle/DLangDemangle.cpp b/llvm/lib/Demangle/DLangDemangle.cpp
index 8856302be6dd1..954a539991ccb 100644
--- a/llvm/lib/Demangle/DLangDemangle.cpp
+++ b/llvm/lib/Demangle/DLangDemangle.cpp
@@ -52,10 +52,13 @@ struct Demangler {
   ///
   /// \param Demangled output buffer to write the demangled name.
   /// \param Mangled mangled symbol to be demangled.
+  /// \param PrintType writes the type information of the symbol to the
+  /// demangled name.
   ///
   /// \see https://dlang.org/spec/abi.html#name_mangling .
   /// \see https://dlang.org/spec/abi.html#MangledName .
-  void parseMangle(OutputBuffer *Demangled, std::string_view &Mangled);
+  void parseMangle(OutputBuffer *Demangled, std::string_view &Mangled,
+                   bool PrintType = true);
 
   /// Extract the number from a given string.
   ///
@@ -103,11 +106,12 @@ struct Demangler {
   /// Extract and demangle backreferenced type from a given mangled symbol
   /// and append it to the output string.
   ///
+  /// \param Demangled output buffer to write the demangled name.
   /// \param Mangled mangled symbol to be demangled.
   ///
   /// \see https://dlang.org/spec/abi.html#back_ref .
   /// \see https://dlang.org/spec/abi.html#TypeBackRef .
-  void parseTypeBackref(std::string_view &Mangled);
+  void parseTypeBackref(OutputBuffer *Demangled, std::string_view &Mangled);
 
   /// Check whether it is the beginning of a symbol name.
   ///
@@ -151,12 +155,146 @@ struct Demangler {
   /// Extract and demangle a type from a given mangled symbol append it to
   /// the output string.
   ///
+  /// \param Demangled Output buffer to write the demangled name.
   /// \param Mangled mangled symbol to be demangled.
   ///
   /// \return true on success, false on error.
   ///
   /// \see https://dlang.org/spec/abi.html#Type .
-  bool parseType(std::string_view &Mangled);
+  bool parseType(OutputBuffer *Demangled, std::string_view &Mangled);
+
+  /// Extract and demangle a function type from a given mangled symbol append it
+  /// to the output string.
+  ///
+  /// \param Demangled Output buffer to write the demangled name.
+  /// \param Mangled mangled symbol to be demangled.
+  /// \param IsDelegate Flag to determine if an function or a delegate is
+  /// currently being demangled.
+  ///
+  /// \return true on success, false on error.
+  ///
+  /// \see https://dlang.org/spec/abi.html#TypeFunction .
+  bool parseTypeFunction(OutputBuffer *Demangled, std::string_view &Mangled,
+                         bool IsDelegate);
+
+  /// Extract and demangle a calling convention from a given mangled symbol
+  /// append it to the output string.
+  ///
+  /// \param Demangled Output buffer to write the demangled name.
+  /// \param Mangled mangled symbol to be demangled.
+  ///
+  /// \return true on success, false on error.
+  ///
+  /// \see https://dlang.org/spec/abi.html#CallConvention
+  bool parseCallConvention(OutputBuffer *Demangled, std::string_view &Mangled);
+
+  /// Extract and demangle function parameters from a given mangled symbol
+  /// append it to the output string.
+  ///
+  /// \param Demangled Output buffer to write the demangled name.
+  /// \param Mangled mangled symbol to be demangled.
+  ///
+  /// \return true on success, false on error.
+  ///
+  /// \see https://dlang.org/spec/abi.html#Parameters
+  bool parseFuncParameters(OutputBuffer *Demangled, std::string_view &Mangled);
+
+  /// Extract function attributes from a given mangled symbol.
+  ///
+  /// \param Mangled mangled symbol to be demangled.
+  ///
+  /// \return bitmap where each bit represents one function attribute.
+  ///
+  /// \see https://dlang.org/spec/abi.html#FuncAttrs
+  unsigned short parseFuncAttributes(std::string_view &Mangled);
+
+  /// Demangle function attributes append it to the output string.
+  ///
+  /// \param Demangled Output buffer to write the demangled name.
+  /// \param Attributes The extracted function attributes.
+  ///
+  /// \see https://dlang.org/spec/abi.html#FuncAttrs
+  void demangleFuncAttributes(OutputBuffer *Demangled,
+                              unsigned short Attributes);
+
+  /// Extract modifiers from a given mangled symbol.
+  ///
+  /// \param Mangled mangled symbol to be demangled.
+  ///
+  /// \return bitmap where each bit represents one function attribute.
+  ///
+  /// \see https://dlang.org/spec/abi.html#TypeModifiers
+  unsigned short parseModifiers(std::string_view &Mangled);
+
+  /// Demangle modifiers append it to the output string.
+  ///
+  /// \param Demangled Output buffer to write the demangled name.
+  /// \param Modifiers The extracted modifiers.
+  ///
+  /// \see https://dlang.org/spec/abi.html#TypeModifiers
+  void demangleModifiers(OutputBuffer *Demangled, unsigned short Modifiers);
+
+  /// Extract and demangle a function type without a return type from a given
+  /// mangled symbol append it to the output string.
+  ///
+  /// \param Attrs Output buffer to write the demangled attributes.
+  /// \param Demangled Output buffer to write the demangled name.
+  /// \param Mangled mangled symbol to be demangled.
+  ///
+  /// \return true on success, false on error.
+  ///
+  /// \see https://dlang.org/spec/abi.html#TypeFunctionNoReturn
+  bool parseFunctionTypeNoReturn(OutputBuffer *Attrs, OutputBuffer *Demangled,
+                                 std::string_view &Mangled);
+
+  /// Extract and demangle a template instance from a given mangled symbol
+  /// append it to the output string.
+  ///
+  /// \param Demangled Output buffer to write the demangled name.
+  /// \param Mangled mangled symbol to be demangled.
+  ///
+  /// \see https://dlang.org/spec/abi.html#TemplateInstanceName
+  void parseTemplateInstanceName(OutputBuffer *Demangled,
+                                 std::string_view &Mangled);
+
+  /// Extract and demangle a template argument value from a given mangled symbol
+  /// append it to the output string.
+  ///
+  /// \param Demangled Output buffer to write the demangled name.
+  /// \param Mangled mangled symbol to be demangled.
+  /// \param Type mangled template argument type.
+  /// \param DemangledType Output buffer containing the already
+  /// demangled template argument type if present.
+  ///
+  /// \see https://dlang.org/spec/abi.html#Value
+  void parseValue(OutputBuffer *Demangled, std::string_view &Mangled,
+                  char Type = '\0', OutputBuffer *DemangledType = nullptr);
+
+  /// Writes an hexadecimal number to the output string.
+  ///
+  /// \param Demangled Output buffer to write the hexadecimal number.
+  /// \param Val the numeric value to write.
+  /// \param Width the width of the hexadecimal number.
+  void printHexNumber(OutputBuffer *Demangled, unsigned long Val,
+                      unsigned Width = 0);
+
+  /// Extract and demangle a numeric template argument value from a given
+  /// mangled symbol append it to the output string.
+  ///
+  /// \param Demangled Output buffer to write the demangled name.
+  /// \param Mangled mangled symbol to be demangled.
+  /// \param Type mangled template argument type.
+  void parseIntegerValue(OutputBuffer *Demangled, std::string_view &Mangled,
+                         char Type = '\0');
+
+  /// Extract and demangle a numeric template argument value from a given
+  /// mangled symbol append it to the output string.
+  ///
+  /// \param Demangled Output buffer to write the demangled name.
+  /// \param Mangled mangled symbol to be demangled.
+  ///
+  /// \see https://dlang.org/spec/abi.html#HexFloat
+  void parseRealValue(OutputBuffer *Demangled, std::string_view &Mangled);
 
   /// An immutable view of the string we are demangling.
   const std::string_view Str;
@@ -166,6 +304,10 @@ struct Demangler {
 
 } // namespace
 
+inline bool isHexDigit(char Val) {
+  return std::isdigit(Val) || (Val >= 'A' && Val <= 'F');
+}
+
 void Demangler::decodeNumber(std::string_view &Mangled, unsigned long &Ret) {
   // Clear Mangled if trying to extract something that isn't a digit.
   if (Mangled.empty()) {
@@ -297,7 +439,8 @@ void Demangler::parseSymbolBackref(OutputBuffer *Demangled,
     Mangled = {};
 }
 
-void Demangler::parseTypeBackref(std::string_view &Mangled) {
+void Demangler::parseTypeBackref(OutputBuffer *Demangled,
+                                 std::string_view &Mangled) {
   // A type back reference always points to a letter.
   //    TypeBackRef:
   //        Q NumberBackRef
@@ -327,7 +470,7 @@ void Demangler::parseTypeBackref(std::string_view &Mangled) {
   }
 
   // TODO: Add support for function type back references.
-  if (!parseType(Backref))
+  if (!parseType(Demangled, Backref))
     Mangled = {};
 
   LastBackref = SaveRefPos;
@@ -343,7 +486,8 @@ bool Demangler::isSymbolName(std::string_view Mangled) {
   if (std::isdigit(Mangled.front()))
     return true;
 
-  // TODO: Handle template instances.
+  if (Mangled.front() == '_')
+    return true;
 
   if (Mangled.front() != 'Q')
     return false;
@@ -356,8 +500,8 @@ bool Demangler::isSymbolName(std::string_view Mangled) {
   return std::isdigit(Qref[-Ret]);
 }
 
-void Demangler::parseMangle(OutputBuffer *Demangled,
-                            std::string_view &Mangled) {
+void Demangler::parseMangle(OutputBuffer *Demangled, std::string_view &Mangled,
+                            bool PrintType) {
   // A D mangled symbol is comprised of both scope and type information.
   //    MangleName:
   //        _D QualifiedName Type
@@ -369,7 +513,36 @@ void Demangler::parseMangle(OutputBuffer *Demangled,
   // a function or the type of a variable.
   Mangled.remove_prefix(2);
 
-  parseQualified(Demangled, Mangled);
+  size_t NotFirst = false;
+  OutputBuffer Attrs;
+  do {
+    // Skip over anonymous symbols.
+    if (!Mangled.empty() && Mangled.front() == '0') {
+      do
+        Mangled.remove_prefix(1);
+      while (!Mangled.empty() && Mangled.front() == '0');
+
+      continue;
+    }
+
+    // Ignore all attributes for parent symbols
+    Attrs.setCurrentPosition(0);
+
+    if (NotFirst)
+      *Demangled << '.';
+    NotFirst = true;
+
+    parseIdentifier(Demangled, Mangled);
+    parseFunctionTypeNoReturn(&Attrs, Demangled, Mangled);
+
+  } while (!Mangled.empty() && isSymbolName(Mangled));
+
+  size_t TypePos = 0;
+  if (!Attrs.empty()) {
+    Attrs << ' ';
+    Demangled->insert(0, Attrs.getBuffer(), Attrs.getCurrentPosition());
+    TypePos = Attrs.getCurrentPosition();
+  }
 
   if (Mangled.empty()) {
     Mangled = {};
@@ -379,8 +552,16 @@ void Demangler::parseMangle(OutputBuffer *Demangled,
   // Artificial symbols end with 'Z' and have no type.
   if (Mangled.front() == 'Z') {
     Mangled.remove_prefix(1);
-  } else if (!parseType(Mangled))
-    Mangled = {};
+  } else {
+    OutputBuffer tmp;
+    if (!parseType(&tmp, Mangled))
+      Mangled = {};
+    else if (PrintType) {
+      tmp << ' ';
+      Demangled->insert(TypePos, tmp.getBuffer(), tmp.getCurrentPosition());
+    }
+    std::free(tmp.getBuffer());
+  }
 }
 
 void Demangler::parseQualified(OutputBuffer *Demangled,
@@ -416,6 +597,8 @@ void Demangler::parseQualified(OutputBuffer *Demangled,
     NotFirst = true;
 
     parseIdentifier(Demangled, Mangled);
+    parseFunctionTypeNoReturn(Demangled, Demangled, Mangled);
+
   } while (!Mangled.empty() && isSymbolName(Mangled));
 }
 
@@ -429,7 +612,8 @@ void Demangler::parseIdentifier(OutputBuffer *Demangled,
   if (Mangled.front() == 'Q')
     return parseSymbolBackref(Demangled, Mangled);
 
-  // TODO: Parse lengthless template instances.
+  if (Mangled.front() == '_')
+    return parseTemplateInstanceName(Demangled, Mangled);
 
   unsigned long Len;
   decodeNumber(Mangled, Len);
@@ -465,39 +649,1008 @@ void Demangler::parseIdentifier(OutputBuffer *Demangled,
   parseLName(Demangled, Mangled, Len);
 }
 
-bool Demangler::parseType(std::string_view &Mangled) {
+bool Demangler::parseType(OutputBuffer *Demangled, std::string_view &Mangled) {
   if (Mangled.empty()) {
     Mangled = {};
     return false;
   }
 
   switch (Mangled.front()) {
-  // TODO: Parse type qualifiers.
-  // TODO: Parse function types.
-  // TODO: Parse compound types.
-  // TODO: Parse delegate types.
-  // TODO: Parse tuple types.
+  // Type qualifiers.
+  case 'O': {
+    *Demangled << "shared(";
+    Mangled.remove_prefix(1);
+    if (!parseType(Demangled, Mangled)) {
+      Mangled = {};
+      return false;
+    }
+    *Demangled << ')';
+    return true;
+  }
 
-  // Basic types.
-  case 'i':
+  case 'x': {
+    *Demangled << "const(";
+    Mangled.remove_prefix(1);
+    if (!parseType(Demangled, Mangled)) {
+      Mangled = {};
+      return false;
+    }
+    *Demangled << ')';
+    return true;
+  }
+
+  case 'y': {
+    *Demangled << "immutable(";
+    Mangled.remove_prefix(1);
+    if (!parseType(Demangled, Mangled)) {
+      Mangled = {};
+      return false;
+    }
+    *Demangled << ')';
+    return true;
+  }
+
+  // Function types.
+  case 'F':
+  case 'U':
+  case 'W':
+  case 'V':
+  case 'R':
+    return parseTypeFunction(Demangled, Mangled, false);
+
+  // Array types.
+  case 'A': {
+    Mangled.remove_prefix(1);
+    if (!parseType(Demangled, Mangled)) {
+      Mangled = {};
+      return false;
+    }
+    *Demangled << "[]";
+    return true;
+  }
+
+  case 'G': {
+    Mangled.remove_prefix(1);
+    unsigned long len;
+    decodeNumber(Mangled, len);
+    if (!parseType(Demangled, Mangled)) {
+      Mangled = {};
+      return false;
+    }
+    *Demangled << '[' << len << ']';
+    return true;
+  }
+
+  case 'H': {
+    Mangled.remove_prefix(1);
+    OutputBuffer tmp;
+    if (!parseType(&tmp, Mangled)) {
+      std::free(tmp.getBuffer());
+      Mangled = {};
+      return false;
+    }
+    if (!parseType(Demangled, Mangled)) {
+      std::free(tmp.getBuffer());
+      Mangled = {};
+      return false;
+    }
+    *Demangled << '[';
+    *Demangled << tmp.getBuffer();
+    *Demangled << ']';
+    std::free(tmp.getBuffer());
+    return true;
+  }
+
+  case 'P': {
+    Mangled.remove_prefix(1);
+    if (!parseType(Demangled, Mangled)) {
+      Mangled = {};
+      return false;
+    }
+    *Demangled << "*";
+    return true;
+  }
+
+  // Delegate types.
+  case 'D': {
     Mangled.remove_prefix(1);
-    // TODO: Add type name dumping
+    auto Modifiers = parseModifiers(Mangled);
+    if (!parseTypeFunction(Demangled, Mangled, true)) {
+      Mangled = {};
+      return false;
+    }
+    if (Modifiers > 0) {
+      *Demangled << ' ';
+      demangleModifiers(Demangled, Modifiers);
+    }
     return true;
+  }
 
-    // TODO: Add support for the rest of the basic types.
+  // Compound types.
+  case 'I':
+  case 'C':
+  case 'S':
+  case 'E':
+  case 'T': {
+    Mangled.remove_prefix(1);
+    parseQualified(Demangled, Mangled);
+    return true;
+  }
+
+  // TODO: Parse tuple types.
+
+  // Cent types.
+  case 'z': {
+    Mangled.remove_prefix(1);
+    switch (Mangled.front()) {
+    case 'i':
+      *Demangled << "cent";
+      Mangled.remove_prefix(1);
+      return true;
+    case 'k':
+      *Demangled << "ucent";
+      Mangled.remove_prefix(1);
+      return true;
+    default:
+      Mangled = {};
+      return false;
+    }
+  }
+
+  // Misc.
+  case 'N': {
+    Mangled.remove_prefix(1);
+    switch (Mangled.front()) {
+    case 'n':
+      *Demangled << "noreturn";
+      Mangled.remove_prefix(1);
+      return true;
+    case 'h':
+      *Demangled << "__vector(";
+      Mangled.remove_prefix(1);
+      if (!parseType(Demangled, Mangled)) {
+        Mangled = {};
+        return false;
+      }
+      *Demangled << ')';
+      return true;
+    case 'g':
+      *Demangled << "inout(";
+      Mangled.remove_prefix(1);
+      if (!parseType(Demangled, Mangled)) {
+        Mangled = {};
+        return false;
+      }
+      *Demangled << ')';
+      return true;
+    }
+    Mangled = {};
+    return false;
+  }
 
   // Back referenced type.
   case 'Q': {
-    parseTypeBackref(Mangled);
+    parseTypeBackref(Demangled, Mangled);
     return true;
   }
 
-  default: // unhandled.
+  default:
+    char c = Mangled.front();
+    // Basic types.
+    if (c >= 'a' && c <= 'w') {
+      Mangled.remove_prefix(1);
+
+      static const char *Primitives[] = {
+          "char",    // a
+          "bool",    // b
+          "creal",   // c
+          "double",  // d
+          "real",    // e
+          "float",   // f
+          "byte",    // g
+          "ubyte",   // h
+          "int",     // i
+          "ireal",   // j
+          "uint",    // k
+          "long",    // l
+          "ulong",   // m
+          0,         // n
+          "ifloat",  // o
+          "idouble", // p
+          "cfloat",  // q
+          "cdouble", // r
+          "short",   // s
+          "ushort",  // t
+          "wchar",   // u
+          "void",    // v
+          "dchar",   // w
+      };
+
+      *Demangled << Primitives[c - 'a'];
+      return true;
+    }
+
+    // unhandled.
     Mangled = {};
     return false;
   }
 }
 
+bool Demangler::parseTypeFunction(OutputBuffer *Demangled,
+                                  std::string_view &Mangled, bool IsDelegate) {
+  if (!parseCallConvention(Demangled, Mangled)) {
+    Mangled = {};
+    return false;
+  }
+
+  auto funcAttrs = parseFuncAttributes(Mangled);
+  if (Mangled.empty()) {
+    Mangled = {};
+    return false;
+  }
+
+  auto begin = Demangled->getCurrentPosition();
+
+  if (IsDelegate)
+    *Demangled << "delegate";
+  else
+    *Demangled << "function";
+
+  *Demangled << '(';
+  if (!parseFuncParameters(Demangled, Mangled)) {
+    Mangled = {};
+    return false;
+  }
+  *Demangled << ')';
+
+  OutputBuffer tmp;
+  if (!parseType(&tmp, Mangled)) {
+    std::free(tmp.getBuffer());
+    Mangled = {};
+    return false;
+  }
+  tmp << ' ';
+  Demangled->insert(begin, tmp.getBuffer(), tmp.getCurrentPosition());
+  std::free(tmp.getBuffer());
+
+  if (funcAttrs > 0) {
+    *Demangled << ' ';
+    demangleFuncAttributes(Demangled, funcAttrs);
+  }
+
+  return true;
+}
+
+bool Demangler::parseCallConvention(OutputBuffer *Demangled,
+                                    std::string_view &Mangled) {
+  switch (Mangled.front()) {
+  case 'F':
+    Mangled.remove_prefix(1);
+    return true;
+  case 'U':
+    *Demangled << "extern (C) ";
+    Mangled.remove_prefix(1);
+    return true;
+  case 'W':
+    *Demangled << "extern (Windows) ";
+    Mangled.remove_prefix(1);
+    return true;
+  case 'R':
+    *Demangled << "extern (C++) ";
+    Mangled.remove_prefix(1);
+    return true;
+  default:
+    Mangled = {};
+    return false;
+  }
+}
+
+bool Demangler::parseFuncParameters(OutputBuffer *Demangled,
+                                    std::string_view &Mangled) {
+  for (size_t i = 0; true; i++) {
+    if (Mangled.empty()) {
+      Mangled = {};
+      return false;
+    }
+
+    switch (Mangled.front()) {
+    case 'X':
+      *Demangled << "...";
+      Mangled.remove_prefix(1);
+      return true;
+    case 'Y':
+      *Demangled << ", ...";
+      Mangled.remove_prefix(1);
+      return true;
+    case 'Z':
+      Mangled.remove_prefix(1);
+      return true;
+    }
+
+    if (i)
+      *Demangled << ", ";
+
+    while (1) {
+      if (Mangled.empty()) {
+        Mangled = {};
+        return false;
+      }
+
+      switch (Mangled.front()) {
+      case 'M':
+        *Demangled << "scope ";
+        Mangled.remove_prefix(1);
+        continue;
+      case 'N':
+        Mangled.remove_prefix(1);
+        if (Mangled.front() == 'k') {
+          *Demangled << "return ";
+          Mangled.remove_prefix(1);
+          continue;
+        } else
+          goto afterAttrLoop;
+      default:
+        goto afterAttrLoop;
+      }
+    }
+  afterAttrLoop:
+
+    switch (Mangled.front()) {
+    case 'I': {
+      *Demangled << "in ";
+      Mangled.remove_prefix(1);
+      if (Mangled.front() == 'K')
+        goto refStorageClass;
+      if (!parseType(Demangled, Mangled)) {
+        Mangled = {};
+        return false;
+      }
+      continue;
+    }
+
+    case 'K':
+    refStorageClass: {
+      *Demangled << "ref ";
+      Mangled.remove_prefix(1);
+      if (!parseType(Demangled, Mangled)) {
+        Mangled = {};
+        return false;
+      }
+      continue;
+    }
+
+    case 'J': {
+      *Demangled << "out ";
+      Mangled.remove_prefix(1);
+      if (!parseType(Demangled, Mangled)) {
+        Mangled = {};
+        return false;
+      }
+      continue;
+    }
+
+    case 'L': {
+      *Demangled << "lazy ";
+      Mangled.remove_prefix(1);
+      if (!parseType(Demangled, Mangled)) {
+        Mangled = {};
+        return false;
+      }
+      continue;
+    }
+
+    default:
+      if (!parseType(Demangled, Mangled)) {
+        Mangled = {};
+        return false;
+      }
+    }
+  }
+}
+
+enum FunctionAttribute {
+  Pure = (1 << 0),
+  NoThrow = (1 << 1),
+  Ref = (1 << 2),
+  Property = (1 << 3),
+  Trusted = (1 << 4),
+  Safe = (1 << 5),
+  NoGC = (1 << 6),
+  Return = (1 << 7),
+  Scope = (1 << 8),
+  Live = (1 << 9),
+  ReturnScope = (1 << 10),
+  ScopeReturn = (1 << 11),
+};
+#define FUNCTIONATTR_MAX 12
+
+unsigned short Demangler::parseFuncAttributes(std::string_view &Mangled) {
+  if (Mangled.empty()) {
+    return 0;
+  }
+
+  unsigned short Result = 0;
+  while (Mangled.front() == 'N') {
+    if (Mangled.length() < 2) {
+      return Result;
+    }
+
+    switch (Mangled.at(1)) {
+    case 'a':
+      Result |= FunctionAttribute::Pure;
+      Mangled.remove_prefix(2);
+      continue;
+    case 'b':
+      Result |= FunctionAttribute::NoThrow;
+      Mangled.remove_prefix(2);
+      continue;
+    case 'c':
+      Result |= FunctionAttribute::Ref;
+      Mangled.remove_prefix(2);
+      continue;
+    case 'd':
+      Result |= FunctionAttribute::Property;
+      Mangled.remove_prefix(2);
+      continue;
+    case 'e':
+      Result |= FunctionAttribute::Trusted;
+      Mangled.remove_prefix(2);
+      continue;
+    case 'f':
+      Result |= FunctionAttribute::Safe;
+      Mangled.remove_prefix(2);
+      continue;
+    case 'i':
+      Result |= FunctionAttribute::NoGC;
+      Mangled.remove_prefix(2);
+      continue;
+    case 'j': {
+      Mangled.remove_prefix(1);
+      if (starts_with(Mangled, "Nl")) {
+        Result |= FunctionAttribute::ReturnScope;
+        Mangled.remove_prefix(2);
+      } else
+        Result |= FunctionAttribute::Return;
+      continue;
+    }
+    case 'l': {
+      Mangled.remove_prefix(1);
+      if (starts_with(Mangled, "Nj")) {
+        Result |= FunctionAttribute::ScopeReturn;
+        Mangled.remove_prefix(2);
+      } else
+        Result |= FunctionAttribute::Scope;
+      continue;
+    }
+    case 'm':
+      Result |= FunctionAttribute::Live;
+      Mangled.remove_prefix(2);
+      continue;
+    default:
+      return Result;
+    }
+  }
+  return Result;
+}
+
+void Demangler::demangleFuncAttributes(OutputBuffer *Demangled,
+                                       unsigned short Attributes) {
+  static const char *AttributeNames[] = {
+      "pure",  "nothrow", "ref",   "@property", "@trusted",     "@safe",
+      "@nogc", "return",  "scope", "@live",     "return scope", "scope return",
+  };
+
+  bool NeedSpace = false;
+  for (unsigned short i = 0; i < FUNCTIONATTR_MAX; i++) {
+    if (Attributes & (1 << i)) {
+      if (NeedSpace)
+        *Demangled << ' ';
+      *Demangled << AttributeNames[i];
+      NeedSpace = true;
+    }
+  }
+}
+
+enum Modifier {
+  Const = (1 << 0),
+  Immutable = (1 << 1),
+  Shared = (1 << 2),
+  InOut = (1 << 3),
+};
+#define MODIFIER_MAX 4
+
+unsigned short Demangler::parseModifiers(std::string_view &Mangled) {
+  unsigned short Result = 0;
+  switch (Mangled.front()) {
+  case 'y':
+    Mangled.remove_prefix(1);
+    return Modifier::Immutable;
+  case 'O':
+    Mangled.remove_prefix(1);
+    Result |= Modifier::Shared;
+    switch (Mangled.front()) {
+    case 'x':
+      goto constMod;
+    case 'N':
+      goto wildMod;
+    default:
+      return Modifier::Shared;
+    }
+  case 'N':
+  wildMod:
+    if (Mangled.size() > 1 && Mangled.at(1) != 'g')
+      return Result;
+    Mangled.remove_prefix(2);
+    Result |= Modifier::InOut;
+    if (!Mangled.empty() && Mangled.front() == 'x')
+      goto constMod;
+    return Result;
+  case 'x':
+  constMod:
+    Mangled.remove_prefix(1);
+    Result |= Modifier::Const;
+    return Result;
+  default:
+    return 0;
+  }
+}
+
+void Demangler::demangleModifiers(OutputBuffer *Demangled,
+                                  unsigned short Modifiers) {
+  static const char *ModifierNames[] = {
+      "const",
+      "immutable",
+      "shared",
+      "inout",
+  };
+
+  bool NeedSpace = false;
+  for (unsigned short i = 0; i < MODIFIER_MAX; i++) {
+    if (Modifiers & (1 << i)) {
+      if (NeedSpace)
+        *Demangled << ' ';
+      *Demangled << ModifierNames[i];
+      NeedSpace = true;
+    }
+  }
+}
+
+bool Demangler::parseFunctionTypeNoReturn(OutputBuffer *Attrs,
+                                          OutputBuffer *Demangled,
+                                          std::string_view &Mangled) {
+  if (Mangled.empty())
+    return true;
+
+  if (Mangled.front() == 'M') {
+    Mangled.remove_prefix(1);
+    auto Modifiers = parseModifiers(Mangled);
+    if (Modifiers > 0) {
+      demangleModifiers(Attrs, Modifiers);
+      *Attrs << ' ';
+    }
+  }
+
+  switch (Mangled.front()) {
+  case 'F':
+  case 'U':
+  case 'W':
+  case 'R':
+    if (!parseCallConvention(Attrs, Mangled)) {
+      Mangled = {};
+      return false;
+    }
+
+    auto funcAttrs = parseFuncAttributes(Mangled);
+    if (funcAttrs > 0)
+      demangleFuncAttributes(Attrs, funcAttrs);
+
+    *Demangled << '(';
+    if (!parseFuncParameters(Demangled, Mangled)) {
+      Mangled = {};
+      return false;
+    }
+    *Demangled << ')';
+
+    return true;
+  }
+  return true;
+}
+
+void Demangler::parseTemplateInstanceName(OutputBuffer *Demangled,
+                                          std::string_view &Mangled) {
+  // TODO: handle template instances with length prefix
+
+  if (Mangled.length() < 3 || Mangled.substr(0, 3) != "__T") {
+    Mangled = {};
+    return;
+  }
+  Mangled.remove_prefix(3);
+
+  unsigned long Len;
+  decodeNumber(Mangled, Len);
+
+  if (Mangled.empty()) {
+    Mangled = {};
+    return;
+  }
+  if (!Len || Mangled.length() < Len) {
+    Mangled = {};
+    return;
+  }
+
+  parseLName(Demangled, Mangled, Len);
+
+  *Demangled << "!(";
+
+  for (size_t n = 0; true; n++) {
+    if (Mangled.empty()) {
+      Mangled = {};
+      return;
+    }
+
+    if (Mangled.front() == 'H')
+      Mangled.remove_prefix(1);
+
+    switch (Mangled.front()) {
+    case 'Z':
+      Mangled.remove_prefix(1);
+      goto after;
+
+    case 'T': {
+      Mangled.remove_prefix(1);
+      if (n)
+        *Demangled << ", ";
+      if (!parseType(Demangled, Mangled)) {
+        Mangled = {};
+        return;
+      }
+      continue;
+    }
+
+    case 'V': {
+      Mangled.remove_prefix(1);
+      if (n)
+        *Demangled << ", ";
+
+      char TypeChar = Mangled.front();
+      OutputBuffer DemangledType;
+      if (!parseType(&DemangledType, Mangled)) {
+        std::free(DemangledType.getBuffer());
+        Mangled = {};
+        return;
+      }
+
+      parseValue(Demangled, Mangled, TypeChar, &DemangledType);
+      std::free(DemangledType.getBuffer());
+      continue;
+    }
+
+    case 'S': {
+      Mangled.remove_prefix(1);
+      if (n)
+        *Demangled << ", ";
+
+      if (starts_with(Mangled, "_D"))
+        parseMangle(Demangled, Mangled, false);
+      else
+        parseQualified(Demangled, Mangled);
+
+      continue;
+    }
+
+    case 'X': {
+      Mangled.remove_prefix(1);
+      if (n)
+        *Demangled << ", ";
+      unsigned long Len;
+      decodeNumber(Mangled, Len);
+      parseLName(Demangled, Mangled, Len);
+      continue;
+    }
+
+    default:
+      Mangled = {};
+      return;
+    }
+  }
+after:
+
+  *Demangled << ')';
+}
+
+void Demangler::parseValue(OutputBuffer *Demangled, std::string_view &Mangled,
+                           char Type, OutputBuffer *DemangledType) {
+  if (Mangled.empty()) {
+    Mangled = {};
+    return;
+  }
+
+  switch (Mangled.front()) {
+  case 'n':
+    Mangled.remove_prefix(1);
+    *Demangled << "null";
+    return;
+
+  case 'i':
+    Mangled.remove_prefix(1);
+    if (!isdigit(Mangled.front())) {
+      Mangled = {};
+      return;
+    }
+    parseIntegerValue(Demangled, Mangled, Type);
+    return;
+
+  case 'N':
+    Mangled.remove_prefix(1);
+    *Demangled << '-';
+    parseIntegerValue(Demangled, Mangled);
+    return;
+
+  case 'e':
+    Mangled.remove_prefix(1);
+    parseRealValue(Demangled, Mangled);
+    return;
+
+  case 'c': {
+    Mangled.remove_prefix(1);
+    parseRealValue(Demangled, Mangled);
+    *Demangled << '+';
+    if (Mangled.empty() || Mangled.front() != 'c') {
+      Mangled = {};
+      return;
+    }
+    Mangled.remove_prefix(1);
+    parseRealValue(Demangled, Mangled);
+    *Demangled << 'i';
+    return;
+  }
+
+  case 'a':
+  case 'w':
+  case 'd': {
+    char Kind = Mangled.front();
+    Mangled.remove_prefix(1);
+
+    unsigned long Len;
+    decodeNumber(Mangled, Len);
+
+    if (Mangled.front() != '_') {
+      Mangled = {};
+      return;
+    }
+    Mangled.remove_prefix(1);
+
+    *Demangled << '"';
+    for (unsigned long i = 0; i < Len; i++) {
+      if (Mangled.length() < 2) {
+        Mangled = {};
+        return;
+      }
+
+      char HexVal[3] = {Mangled.at(0), Mangled.at(1), 0};
+      char Char = (char)(std::stoi(HexVal, 0, 16));
+      if (' ' <= Char && Char <= '~')
+        *Demangled << Char;
+      else
+        *Demangled << "\\x" << HexVal;
+
+      Mangled.remove_prefix(2);
+    }
+    *Demangled << '"';
+
+    if (Kind != 'a')
+      *Demangled << Kind;
+
+    return;
+  }
+
+  case 'A': {
+    Mangled.remove_prefix(1);
+
+    unsigned long Len;
+    decodeNumber(Mangled, Len);
+
+    *Demangled << '[';
+    for (size_t i = 0; i < Len; i++) {
+      if (i)
+        *Demangled << ", ";
+
+      if (Type == 'H') {
+        parseValue(Demangled, Mangled);
+        *Demangled << ':';
+      }
+
+      parseValue(Demangled, Mangled);
+    }
+    *Demangled << ']';
+    return;
+  }
+
+  case 'S': {
+    Mangled.remove_prefix(1);
+
+    unsigned long Len;
+    decodeNumber(Mangled, Len);
+
+    if (DemangledType)
+      Demangled->insert(Demangled->getCurrentPosition(),
+                        DemangledType->getBuffer(),
+                        DemangledType->getCurrentPosition());
+
+    *Demangled << '(';
+    for (size_t i = 0; i < Len; i++) {
+      if (i)
+        *Demangled << ", ";
+      parseValue(Demangled, Mangled);
+    }
+    *Demangled << ')';
+    return;
+  }
+
+    // TODO: f MangledName
+
+  default:
+    if (isdigit(Mangled.front())) {
+      parseIntegerValue(Demangled, Mangled, Type);
+      return;
+    }
+
+    Mangled = {};
+    return;
+  }
+}
+
+void Demangler::printHexNumber(OutputBuffer *Demangled, unsigned long Val,
+                               unsigned Width) {
+  if (Val == 0)
+    *Demangled << '0';
+
+  static const char Digits[] = "0123456789ABCDEF";
+
+  for (unsigned i = 0; Width ? (i < Width) : Val; ++i) {
+    unsigned char Mod = static_cast<unsigned char>(Val) & 15;
+    *Demangled << Digits[Mod];
+    Val >>= 4;
+  }
+}
+
+void Demangler::parseIntegerValue(OutputBuffer *Demangled,
+                                  std::string_view &Mangled, char Type) {
+  unsigned long Val;
+  decodeNumber(Mangled, Val);
+
+  switch (Type) {
+  case 'a':
+  case 'u':
+  case 'w': {
+    switch (Val) {
+    case '\'':
+      *Demangled << "'\\''";
+      return;
+    case '\\':
+      *Demangled << "'\\\\'";
+      return;
+    case '\a':
+      *Demangled << "'\\a'";
+      return;
+    case '\b':
+      *Demangled << "'\\b'";
+      return;
+    case '\f':
+      *Demangled << "'\\f'";
+      return;
+    case '\n':
+      *Demangled << "'\\n'";
+      return;
+    case '\r':
+      *Demangled << "'\\r'";
+      return;
+    case '\t':
+      *Demangled << "'\\t'";
+      return;
+    case '\v':
+      *Demangled << "'\\v'";
+      return;
+    default:
+      switch (Type) {
+      case 'a':
+        if (Val >= 0x20 && Val < 0x7F)
+          *Demangled << '\'' << (char)Val << '\'';
+        else {
+          *Demangled << "'\\x";
+          printHexNumber(Demangled, Val, 2);
+          *Demangled << '\'';
+        }
+        return;
+
+      case 'u':
+        *Demangled << "'\\u";
+        printHexNumber(Demangled, Val, 4);
+        *Demangled << '\'';
+        return;
+
+      case 'w':
+        *Demangled << "'\\U";
+        printHexNumber(Demangled, Val, 8);
+        *Demangled << '\'';
+        return;
+      }
+    }
+
+    Mangled = {};
+    return;
+  }
+
+  case 'b':
+    *Demangled << (Val ? "true" : "false");
+    return;
+
+  case 'h':
+  case 't':
+  case 'k':
+    *Demangled << Val << 'u';
+    return;
+
+  case 'l':
+    *Demangled << Val << 'L';
+    return;
+
+  case 'm':
+    *Demangled << Val << "uL";
+    return;
+
+  default:
+    *Demangled << Val;
+    return;
+  }
+}
+
+void Demangler::parseRealValue(OutputBuffer *Demangled,
+                               std::string_view &Mangled) {
+  if (starts_with(Mangled, "INF")) {
+    *Demangled << "real.infinity";
+    Mangled.remove_prefix(3);
+    return;
+  } else if (Mangled.front() == 'N') {
+    Mangled.remove_prefix(1);
+    if (starts_with(Mangled, "INF")) {
+      *Demangled << "-real.infinity";
+      Mangled.remove_prefix(3);
+      return;
+    }
+    if (starts_with(Mangled, "AN")) {
+      *Demangled << "real.nan";
+      Mangled.remove_prefix(2);
+      return;
+    }
+    *Demangled << '-';
+  }
+
+  *Demangled << "0x";
+
+  while (isHexDigit(Mangled.front())) {
+    *Demangled << Mangled.front();
+    Mangled.remove_prefix(1);
+  }
+
+  if (Mangled.front() != 'P') {
+    Mangled = {};
+    return;
+  }
+  Mangled.remove_prefix(1);
+
+  if (Mangled.front() == 'N') {
+    Mangled.remove_prefix(1);
+    *Demangled << '-';
+  } else
+    *Demangled << '+';
+
+  while (isdigit(Mangled.front())) {
+    *Demangled << Mangled.front();
+    Mangled.remove_prefix(1);
+  }
+}
+
 void Demangler::parseLName(OutputBuffer *Demangled, std::string_view &Mangled,
                            unsigned long Len) {
   switch (Len) {
@@ -558,7 +1711,7 @@ Demangler::Demangler(std::string_view Mangled)
 
 const char *Demangler::parseMangle(OutputBuffer *Demangled) {
   std::string_view M(this->Str);
-  parseMangle(Demangled, M);
+  parseMangle(Demangled, M, true);
   return M.data();
 }
 
diff --git a/llvm/unittests/Demangle/DLangDemangleTest.cpp b/llvm/unittests/Demangle/DLangDemangleTest.cpp
index 1322f530e5963..d8aa5d37c93f7 100644
--- a/llvm/unittests/Demangle/DLangDemangleTest.cpp
+++ b/llvm/unittests/Demangle/DLangDemangleTest.cpp
@@ -48,28 +48,71 @@ INSTANTIATE_TEST_SUITE_P(
                        "ModuleInfo for demangle.test"),
         std::make_pair("_D8demangle4__S14testZ", "demangle.test"),
         std::make_pair("_D8demangle4__Sd4testZ", "demangle.__Sd.test"),
-        std::make_pair("_D8demangle3fooi", "demangle.foo"),
+        std::make_pair("_D8demangle3fooi", "int demangle.foo"),
         std::make_pair("_D8demangle3foo",
                        nullptr), // symbol without a type sequence.
         std::make_pair("_D8demangle3fooinvalidtypeseq",
                        nullptr), // invalid type sequence.
         std::make_pair(
             "_D8demangle3ABCQe1ai",
-            "demangle.ABC.ABC.a"), // symbol back reference: `Qe` is a back
-                                   // reference for position 5, counting from e
-                                   // char, so decoding it points to `3`. Since
-                                   // `3` is a number, 3 chars get read and it
-                                   // succeeded.
+            "int demangle.ABC.ABC.a"), // symbol back reference: `Qe` is a back
+                                       // reference for position 5, counting
+                                       // from e char, so decoding it points to
+                                       // `3`. Since `3` is a number, 3 chars
+                                       // get read and it succeeded.
         std::make_pair("_D8demangle3ABCQa1ai",
                        nullptr), // invalid symbol back reference (recursive).
         std::make_pair("_D8demangleQDXXXXXXXXXXXXx",
                        nullptr), // overflow back reference position.
         std::make_pair(
             "_D8demangle4ABCi1aQd",
-            "demangle.ABCi.a"), // type back reference: `Qd` is a back reference
-                                // for position 4, counting from `d` char, so
-                                // decoding it points to `i`.
+            "int demangle.ABCi.a"), // type back reference: `Qd` is a back
+                                    // reference for position 4, counting from
+                                    // `d` char, so decoding it points to `i`.
         std::make_pair("_D8demangle3fooQXXXx",
                        nullptr), // invalid type back reference position.
         std::make_pair("_D8demangle5recurQa",
-                       nullptr))); // invalid type back reference (recursive).
+                       nullptr), // invalid type back reference
+                                 // (recursive).
+        std::make_pair(
+            "_D8demangle3fooMFiZv",
+            "void demangle.foo(int)"), // function with one parameter.
+        std::make_pair(
+            "_D8demangle3fooFFNaZvZv",
+            "void demangle.foo(void function() pure)"), // function with a
+                                                        // function as
+                                                        // parameter.
+        std::make_pair(
+            "_D8demangle3fooMxWNaZv",
+            "const extern (Windows) pure void demangle.foo()"), // function with
+                                                                // attributes
+                                                                // and a calling
+                                                                // convention.
+        std::make_pair("_D8demangle3fooDxWNaZv",
+                       "extern (Windows) void delegate() pure const "
+                       "demangle.foo"), // delegate with attributes and a
+                                        // calling convention.
+        std::make_pair(
+            "_D8demangle__T3fooTiZQhFiZv",
+            "void demangle.foo!(int).foo(int)"), // template instance with a
+                                                 // type and inner function.
+        std::make_pair("_D8demangle__T3fooVAiA2i1i2ZZ",
+                       "demangle.foo!([1, 2])"), // template instance with an
+                                                 // array literal.
+        std::make_pair(
+            "_D8demangle__T3fooVHiiA2i1i2i3i4ZZ",
+            "demangle.foo!([1:2, 3:4])"), // template instance with an
+                                          // assocative array literal.
+        std::make_pair(
+            "_D8demangle__T3fooVSQs3FooS1i1ZZ",
+            "demangle.foo!(demangle.Foo(1))"), // template instance with a
+                                               // struct literal.
+        std::make_pair("_D8demangle__T3fooVai97ZZ",
+                       "demangle.foo!('a')"), // template instance with a
+                                              // character literal.
+        std::make_pair("_D8demangle__T3fooVAyaa3_626172ZZ",
+                       "demangle.foo!(\"bar\")"), // template instance with a
+                                                  // string literal.
+        std::make_pair("_D8demangle__T3fooS_DQt1fSQy3FooZZ",
+                       "demangle.foo!(demangle.f)"))); // template instance with
+                                                       // a qualified name.
\ No newline at end of file



More information about the llvm-commits mailing list