diff --git a/llvm/include/llvm/Demangle/Demangle.h b/llvm/include/llvm/Demangle/Demangle.h --- a/llvm/include/llvm/Demangle/Demangle.h +++ b/llvm/include/llvm/Demangle/Demangle.h @@ -9,6 +9,8 @@ #ifndef LLVM_DEMANGLE_DEMANGLE_H #define LLVM_DEMANGLE_DEMANGLE_H +#include "llvm/ADT/Optional.h" + #include <cstddef> #include <string> @@ -28,6 +30,16 @@ demangle_success = 0, }; +/// The different Demangle formats +enum DemangleFormatTy { + demangle_format_itanium = 0, + demangle_format_dlang, + demangle_format_rust, + demangle_format_non_microsoft, // this is all of the above formats + demangle_format_microsoft, + demangle_format_autodetect, +}; + char *itaniumDemangle(const char *mangled_name, char *buf, size_t *n, int *status); @@ -70,6 +82,15 @@ /// demangling occurred. std::string demangle(const std::string &MangledName); +/// Demangle with specified DemangleFormat. This replaces the demangle() +/// function. \param MangledName - reference to string to demangle. \param +/// Format - Specifier for what formats to try to demangle. Pass +/// demangle_format_autodetect to try all the demangler \return - a +/// llvm::Optional that is populated if the demangling succeeded. +llvm::Optional<std::string> demangle(const std::string &MangledName, + DemangleFormatTy Format); + +/// Alias for demangle(name, demangle_format_non_microsoft) bool nonMicrosoftDemangle(const char *MangledName, std::string &Result); /// "Partial" demangler. This supports demangling a string into an AST diff --git a/llvm/lib/Demangle/Demangle.cpp b/llvm/lib/Demangle/Demangle.cpp --- a/llvm/lib/Demangle/Demangle.cpp +++ b/llvm/lib/Demangle/Demangle.cpp @@ -26,39 +26,71 @@ MangledName[1] == 'D'; } -std::string llvm::demangle(const std::string &MangledName) { - std::string Result; +llvm::Optional<std::string> llvm::demangle(const std::string &MangledName, + DemangleFormatTy Format) { const char *S = MangledName.c_str(); + llvm::Optional<std::string> Result; + char *Demangled = nullptr; + int Status = demangle_unknown_error; - if (nonMicrosoftDemangle(S, Result)) - return Result; - - if (S[0] == '_' && nonMicrosoftDemangle(S + 1, Result)) + switch (Format) { + case demangle_format_itanium: + if (isItaniumEncoding(S)) { + Demangled = itaniumDemangle(S, nullptr, nullptr, nullptr); + Status = demangle_success; + } + break; + case demangle_format_dlang: + if (isDLangEncoding(S)) { + Demangled = dlangDemangle(S); + Status = demangle_success; + } + break; + case demangle_format_rust: + if (isRustEncoding(S)) { + Demangled = rustDemangle(S); + Status = demangle_success; + } + break; + case demangle_format_microsoft: + Demangled = microsoftDemangle(S, nullptr, nullptr, nullptr, &Status); + break; + case demangle_format_autodetect: + case demangle_format_non_microsoft: + if (auto D = demangle(MangledName, demangle_format_itanium)) + return D; + if (auto D = demangle(MangledName, demangle_format_rust)) + return D; + if (auto D = demangle(MangledName, demangle_format_dlang)) + return D; + // If the flag is non_microsoft we don't want to run the code below + if (Format == demangle_format_autodetect) + if (auto D = demangle(MangledName, demangle_format_microsoft)) + return D; return Result; + } - if (char *Demangled = - microsoftDemangle(S, nullptr, nullptr, nullptr, nullptr)) { + if (Status == demangle_success && Demangled) { Result = Demangled; std::free(Demangled); - return Result; } + return Result; +} + +std::string llvm::demangle(const std::string &MangledName) { + auto D = demangle(MangledName, demangle_format_autodetect); + if (D) + return D.value(); return MangledName; } bool llvm::nonMicrosoftDemangle(const char *MangledName, std::string &Result) { - char *Demangled = nullptr; - if (isItaniumEncoding(MangledName)) - Demangled = itaniumDemangle(MangledName, nullptr, nullptr, nullptr); - else if (isRustEncoding(MangledName)) - Demangled = rustDemangle(MangledName); - else if (isDLangEncoding(MangledName)) - Demangled = dlangDemangle(MangledName); - - if (!Demangled) - return false; - - Result = Demangled; - std::free(Demangled); - return true; + auto D = demangle(MangledName, demangle_format_non_microsoft); + if (D) { + Result = D.value(); + return true; + } + Result = MangledName; + return false; } diff --git a/llvm/test/tools/llvm-cxxfilt/microsoft-format.test b/llvm/test/tools/llvm-cxxfilt/microsoft-format.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-cxxfilt/microsoft-format.test @@ -0,0 +1,13 @@ +## Test microsoft demangle format +## This should only demangle microsofts format if format=microsoft or format=auto +## And fail if we set format=gnu + +RUN: llvm-cxxfilt --format=microsoft ?a@@YAHD@Z ?c@b@@AAGXM@Z | FileCheck %s +RUN: llvm-cxxfilt --format=auto ?a@@YAHD@Z ?c@b@@AAGXM@Z | FileCheck %s +RUN: llvm-cxxfilt --format=gnu ?a@@YAHD@Z ?c@b@@AAGXM@Z | FileCheck %s --check-prefix=GNU + +CHECK: int __cdecl a(char) +CHECK-NEXT: private: void __stdcall b::c(float) + +GNU: ?a@@YAHD@Z +GNU-NEXT: ?c@b@@AAGXM@Z diff --git a/llvm/tools/llvm-cxxfilt/Opts.td b/llvm/tools/llvm-cxxfilt/Opts.td --- a/llvm/tools/llvm-cxxfilt/Opts.td +++ b/llvm/tools/llvm-cxxfilt/Opts.td @@ -19,7 +19,7 @@ def types : FF<"types", "Attempt to demangle types as well as function names">; def version : FF<"version", "Display the version">; -defm : Eq<"format", "Specify mangling format. Currently ignored because only 'gnu' is supported">; +defm format : Eq<"format", "Specify mangling format: gnu (default), itanium, dlang, rust, microsoft or auto">, MetaVarName<"<format>">; def : F<"s", "Alias for --format">; def : F<"_", "Alias for --strip-underscore">, Alias<strip_underscore>; diff --git a/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp b/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp --- a/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp +++ b/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp @@ -48,6 +48,8 @@ #undef OPTION }; +static DemangleFormatTy DemangleFormat; + class CxxfiltOptTable : public opt::OptTable { public: CxxfiltOptTable() : OptTable(InfoTable) { setGroupedShortOptions(true); } @@ -70,11 +72,12 @@ if (DecoratedStr[0] == '_') ++DecoratedStr; - std::string Result; - if (nonMicrosoftDemangle(DecoratedStr, Result)) - return Result; + if (auto Result = demangle(DecoratedStr, DemangleFormat)) { + return Result.value(); + } std::string Prefix; + std::string Result = DecoratedStr; char *Undecorated = nullptr; if (Types) @@ -162,6 +165,23 @@ return 0; } + StringRef V = Args.getLastArgValue(OPT_format_EQ, "gnu"); + if (V == "gnu") + DemangleFormat = llvm::demangle_format_non_microsoft; + else if (V == "microsoft") + DemangleFormat = llvm::demangle_format_microsoft; + else if (V == "auto") + DemangleFormat = llvm::demangle_format_autodetect; + else if (V == "dlang") + DemangleFormat = llvm::demangle_format_dlang; + else if (V == "rust") + DemangleFormat = llvm::demangle_format_rust; + else if (V == "itanium") + DemangleFormat = llvm::demangle_format_itanium; + else + error("--format value should be one of: gnu (default), itanium, dlang, " + "rust, microsoft or auto"); + // The default value depends on the default triple. Mach-O has symbols // prefixed with "_", so strip by default. if (opt::Arg *A =