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 @@ -4,23 +4,29 @@ class FF : Flag<["--"], name>, HelpText; multiclass BB { - def NAME: Flag<["--"], name>, HelpText; - def no_ # NAME: Flag<["--"], "no-" # name>, HelpText; + def NAME : Flag<["--"], name>, HelpText; + def no_ #NAME : Flag<["--"], "no-" #name>, HelpText; } multiclass Eq { - def NAME #_EQ : Joined<["--"], name #"=">, - HelpText; - def : Separate<["--"], name>, Alias(NAME #_EQ)>; + def NAME #_EQ : Joined<["--"], name #"=">, HelpText; +def: + Separate<["--"], name>, Alias(NAME #_EQ)>; } def help : FF<"help", "Display this help">; -defm strip_underscore : BB<"strip-underscore", "Strip the leading underscore", "Don't strip the leading underscore">; +defm strip_underscore : BB<"strip-underscore", "Strip the leading underscore", + "Don't strip the leading underscore">; 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">; -def : F<"s", "Alias for --format">; +defm format : Eq<"format", "Specify mangling format: gnu (default), itanium, " + "dlang, rust, microsoft or auto">, + MetaVarName<"">; +def : JoinedOrSeparate<["-"], "s">, + Alias, + HelpText<"Alias for --format">, + MetaVarName<"">; def : F<"_", "Alias for --strip-underscore">, Alias; def : F<"h", "Alias for --help">, Alias; 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 @@ -6,6 +6,7 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ADT/Optional.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Triple.h" #include "llvm/Demangle/Demangle.h" @@ -48,6 +49,18 @@ #undef OPTION }; +/// The different Demangle formats +enum DemangleFormatTy { + itanium = 0, + dlang, + rust, + gnu, // gnu means itaninum, dlang and rust + microsoft, + autodetect +}; + +static DemangleFormatTy DemangleFormat; + class CxxfiltOptTable : public opt::OptTable { public: CxxfiltOptTable() : OptTable(InfoTable) { setGroupedShortOptions(true); } @@ -64,17 +77,85 @@ exit(1); } +static bool isItaniumEncoding(const char *S) { + // Itanium encoding requires 1 or 3 leading underscores, followed by 'Z'. + return std::strncmp(S, "_Z", 2) == 0 || std::strncmp(S, "___Z", 4) == 0; +} + +static bool isRustEncoding(const char *S) { return S[0] == '_' && S[1] == 'R'; } + +static bool isDLangEncoding(const std::string &MangledName) { + return MangledName.size() >= 2 && MangledName[0] == '_' && + MangledName[1] == 'D'; +} + +// Demangle MangledName with specific DemangleFormat +// Return a llvm::Optional that's false if it couldn't +// demangle the string. +static llvm::Optional +demangleFormat(const std::string &MangledName, DemangleFormatTy Format) { + const char *S = MangledName.c_str(); + llvm::Optional Result; + char *Demangled = nullptr; + int Status = demangle_unknown_error; + + switch (Format) { + case itanium: + if (isItaniumEncoding(S)) { + Demangled = itaniumDemangle(S, nullptr, nullptr, nullptr); + Status = demangle_success; + } + break; + case dlang: + if (isDLangEncoding(S)) { + Demangled = dlangDemangle(S); + Status = demangle_success; + } + break; + case rust: + if (isRustEncoding(S)) { + Demangled = rustDemangle(S); + Status = demangle_success; + } + break; + case microsoft: + Demangled = microsoftDemangle(S, nullptr, nullptr, nullptr, &Status); + break; + case autodetect: + case gnu: + if (auto D = demangleFormat(MangledName, itanium)) + return D; + if (auto D = demangleFormat(MangledName, rust)) + return D; + if (auto D = demangleFormat(MangledName, dlang)) + return D; + // If the flag is non_microsoft we don't want to run the code below + if (Format == autodetect) + if (auto D = demangleFormat(MangledName, microsoft)) + return D; + return Result; + } + + if (Status == demangle_success && Demangled) { + Result = Demangled; + std::free(Demangled); + } + + return Result; +} + static std::string demangle(const std::string &Mangled) { const char *DecoratedStr = Mangled.c_str(); if (StripUnderscore) if (DecoratedStr[0] == '_') ++DecoratedStr; - std::string Result; - if (nonMicrosoftDemangle(DecoratedStr, Result)) - return Result; + if (auto Result = demangleFormat(DecoratedStr, DemangleFormat)) { + return Result.value(); + } std::string Prefix; + std::string Result = DecoratedStr; char *Undecorated = nullptr; if (Types) @@ -162,6 +243,23 @@ return 0; } + StringRef V = Args.getLastArgValue(OPT_format_EQ, "gnu"); + if (V == "gnu") + DemangleFormat = gnu; + else if (V == "microsoft") + DemangleFormat = microsoft; + else if (V == "auto") + DemangleFormat = autodetect; + else if (V == "dlang") + DemangleFormat = dlang; + else if (V == "rust") + DemangleFormat = rust; + else if (V == "itanium") + DemangleFormat = 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 =