Expression evaluation for overloaded C functions (redux)
authorLuke Drummond <luke.drummond@codeplay.com>
Mon, 19 Dec 2016 17:22:44 +0000 (17:22 +0000)
committerLuke Drummond <luke.drummond@codeplay.com>
Mon, 19 Dec 2016 17:22:44 +0000 (17:22 +0000)
This is a redux of [Ewan's patch](https://reviews.llvm.org/D17957) , refactored
to properly substitute primitive  types using a hook in the itanium demangler,
and updated after the previous patch went stale

The new `SubsPrimitiveParmItanium` function takes a symbol name and replacement
primitive type parameter as before but parses it using the FastDemangler, which
has been modified to be able to notify clients of parse events (primitive types
at this point).

Additionally, we now use a `set` of `ConstStrings` instead of a `vector` so
that we don't try and resolve the same invalid candidate multiple times.

Differential Revision: https://reviews.llvm.org/D27223
Subscribers: lldb-commits

llvm-svn: 290117

lldb/include/lldb/Core/FastDemangle.h
lldb/source/Core/FastDemangle.cpp
lldb/source/Expression/IRExecutionUnit.cpp
lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h

index 1a8b8ad..f779aaa 100644 (file)
 #ifndef liblldb_FastDemangle_h_
 #define liblldb_FastDemangle_h_
 
+#include <cstddef>
+
+#include <functional>
+
 namespace lldb_private {
 
 char *FastDemangle(const char *mangled_name);
 
-char *FastDemangle(const char *mangled_name, long mangled_name_length);
+char *
+FastDemangle(const char *mangled_name, size_t mangled_name_length,
+             std::function<void(const char *s)> primitive_type_hook = nullptr);
 }
 
 #endif
index 00a158a..0bed4a1 100644 (file)
@@ -11,6 +11,9 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <functional>
+
+#include "lldb/Core/FastDemangle.h"
 #include "lldb/lldb-private.h"
 
 //#define DEBUG_FAILURES 1
@@ -119,7 +122,9 @@ public:
   /// @param storage_size Number of bytes of space available scratch memory
   /// referenced by storage_ptr
 
-  SymbolDemangler(void *storage_ptr, int storage_size) {
+  SymbolDemangler(void *storage_ptr, size_t storage_size,
+                  std::function<void(const char *)> builtins_hook = nullptr)
+      : m_builtins_hook(builtins_hook) {
     // Use up to 1/8th of the provided space for rewrite ranges
     m_rewrite_ranges_size = (storage_size >> 3) / sizeof(BufferRange);
     m_rewrite_ranges = (BufferRange *)storage_ptr;
@@ -509,6 +514,9 @@ private:
   //                ::= u <source-name>    # vendor extended type
 
   const char *TryParseBuiltinType() {
+    if (m_builtins_hook)
+      m_builtins_hook(m_read_ptr);
+
     switch (*m_read_ptr++) {
     case 'v':
       return "void";
@@ -1150,6 +1158,23 @@ private:
     return RewriteTemplateArg(count + 1);
   }
 
+  // <vector-type>
+  // Dv <dimension number> _ <vector type>
+  bool TryParseVectorType() {
+    const int dimension = TryParseNumber();
+    if (dimension == -1)
+      return false;
+
+    if (*m_read_ptr++ != '_')
+      return false;
+
+    char vec_dimens[32] = {'\0'};
+    ::snprintf(vec_dimens, sizeof vec_dimens - 1, " __vector(%d)", dimension);
+    ParseType();
+    Write(vec_dimens);
+    return true;
+  }
+
   // <type> ::= <builtin-type>
   //        ::= <function-type>
   //        ::= <class-enum-type>
@@ -1191,9 +1216,12 @@ private:
         if (!ParseType())
           return false;
         break;
+      case 'v':
+        if (!TryParseVectorType())
+          return false;
+        break;
       case 'T':
       case 't':
-      case 'v':
       default:
 #ifdef DEBUG_FAILURES
         printf("*** Unsupported type: %.3s\n", failed_type);
@@ -2346,6 +2374,7 @@ private:
   char *m_write_ptr;
   int m_next_template_arg_index;
   int m_next_substitute_index;
+  std::function<void(const char *s)> m_builtins_hook;
 };
 
 } // Anonymous namespace
@@ -2358,9 +2387,10 @@ char *FastDemangle(const char *mangled_name) {
   return demangler.GetDemangledCopy(mangled_name);
 }
 
-char *FastDemangle(const char *mangled_name, long mangled_name_length) {
+char *FastDemangle(const char *mangled_name, size_t mangled_name_length,
+                   std::function<void(const char *s)> builtins_hook) {
   char buffer[16384];
-  SymbolDemangler demangler(buffer, sizeof(buffer));
+  SymbolDemangler demangler(buffer, sizeof(buffer), builtins_hook);
   return demangler.GetDemangledCopy(mangled_name, mangled_name_length);
 }
 } // lldb_private namespace
index 9ad2b33..f18d96b 100644 (file)
@@ -776,22 +776,9 @@ void IRExecutionUnit::CollectCandidateCPlusPlusNames(
       }
     }
 
-    // Maybe we're looking for a const symbol but the debug info told us it was
-    // const...
-    if (!strncmp(name.GetCString(), "_ZN", 3) &&
-        strncmp(name.GetCString(), "_ZNK", 4)) {
-      std::string fixed_scratch("_ZNK");
-      fixed_scratch.append(name.GetCString() + 3);
-      CPP_specs.push_back(ConstString(fixed_scratch.c_str()));
-    }
-
-    // Maybe we're looking for a static symbol but we thought it was global...
-    if (!strncmp(name.GetCString(), "_Z", 2) &&
-        strncmp(name.GetCString(), "_ZL", 3)) {
-      std::string fixed_scratch("_ZL");
-      fixed_scratch.append(name.GetCString() + 2);
-      CPP_specs.push_back(ConstString(fixed_scratch.c_str()));
-    }
+    std::set<ConstString> alternates;
+    CPlusPlusLanguage::FindAlternateFunctionManglings(name, alternates);
+    CPP_specs.insert(CPP_specs.end(), alternates.begin(), alternates.end());
   }
 }
 
index 3fa78f0..c1470c5 100644 (file)
@@ -1978,11 +1978,12 @@ void ClangExpressionDeclMap::AddOneFunction(NameSearchContext &context,
   if (function) {
     Type *function_type = function->GetType();
 
-    const lldb::LanguageType comp_unit_language =
-        function->GetCompileUnit()->GetLanguage();
-    const bool extern_c = Language::LanguageIsC(comp_unit_language) ||
-                          (Language::LanguageIsObjC(comp_unit_language) &&
-                           !Language::LanguageIsCPlusPlus(comp_unit_language));
+    const auto lang = function->GetCompileUnit()->GetLanguage();
+    const auto name = function->GetMangled().GetMangledName().AsCString();
+    const bool extern_c = (Language::LanguageIsC(lang) &&
+                           !CPlusPlusLanguage::IsCPPMangledName(name)) ||
+                          (Language::LanguageIsObjC(lang) &&
+                           !Language::LanguageIsCPlusPlus(lang));
 
     if (!extern_c) {
       TypeSystem *type_system = function->GetDeclContext().GetTypeSystem();
index fc70948..b5527ed 100644 (file)
 #include "CPlusPlusLanguage.h"
 
 // C Includes
-// C++ Includes
 #include <cctype>
 #include <cstring>
+
+// C++ Includes
 #include <functional>
+#include <memory>
 #include <mutex>
+#include <set>
 
 // Other libraries and framework includes
 #include "llvm/ADT/StringRef.h"
 
 // Project includes
 #include "lldb/Core/ConstString.h"
+#include "lldb/Core/FastDemangle.h"
+#include "lldb/Core/Log.h"
 #include "lldb/Core/PluginManager.h"
 #include "lldb/Core/RegularExpression.h"
 #include "lldb/Core/UniqueCStringMap.h"
@@ -440,6 +445,101 @@ CPlusPlusLanguage::FindEquivalentNames(ConstString type_name,
   return count;
 }
 
+/// Given a mangled function `mangled`, replace all the primitive function type
+/// arguments of `search` with type `replace`.
+static ConstString SubsPrimitiveParmItanium(llvm::StringRef mangled,
+                                            llvm::StringRef search,
+                                            llvm::StringRef replace) {
+  Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE);
+
+  const size_t max_len =
+      mangled.size() + mangled.count(search) * replace.size() + 1;
+
+  // Make a temporary buffer to fix up the mangled parameter types and copy the
+  // original there
+  std::string output_buf;
+  output_buf.reserve(max_len);
+  output_buf.insert(0, mangled.str());
+  ptrdiff_t replaced_offset = 0;
+
+  auto swap_parms_hook = [&](const char *parsee) {
+    if (!parsee || !*parsee)
+      return;
+
+    // Check whether we've found a substitutee
+    llvm::StringRef s(parsee);
+    if (s.startswith(search)) {
+      // account for the case where a replacement is of a different length to
+      // the original
+      replaced_offset += replace.size() - search.size();
+
+      ptrdiff_t replace_idx = (mangled.size() - s.size()) + replaced_offset;
+      output_buf.erase(replace_idx, search.size());
+      output_buf.insert(replace_idx, replace.str());
+    }
+  };
+
+  // FastDemangle will call our hook for each instance of a primitive type,
+  // allowing us to perform substitution
+  const char *const demangled =
+      FastDemangle(mangled.str().c_str(), mangled.size(), swap_parms_hook);
+
+  if (log)
+    log->Printf("substituted mangling for %s:{%s} %s:{%s}\n",
+                mangled.str().c_str(), demangled, output_buf.c_str(),
+                FastDemangle(output_buf.c_str()));
+
+  return output_buf == mangled ? ConstString() : ConstString(output_buf);
+}
+
+uint32_t CPlusPlusLanguage::FindAlternateFunctionManglings(
+    const ConstString mangled_name, std::set<ConstString> &alternates) {
+  const auto start_size = alternates.size();
+  /// Get a basic set of alternative manglings for the given symbol `name`, by
+  /// making a few basic possible substitutions on basic types, storage duration
+  /// and `const`ness for the given symbol. The output parameter `alternates`
+  /// is filled with a best-guess, non-exhaustive set of different manglings
+  /// for the given name.
+
+  // Maybe we're looking for a const symbol but the debug info told us it was
+  // non-const...
+  if (!strncmp(mangled_name.GetCString(), "_ZN", 3) &&
+      strncmp(mangled_name.GetCString(), "_ZNK", 4)) {
+    std::string fixed_scratch("_ZNK");
+    fixed_scratch.append(mangled_name.GetCString() + 3);
+    alternates.insert(ConstString(fixed_scratch));
+  }
+
+  // Maybe we're looking for a static symbol but we thought it was global...
+  if (!strncmp(mangled_name.GetCString(), "_Z", 2) &&
+      strncmp(mangled_name.GetCString(), "_ZL", 3)) {
+    std::string fixed_scratch("_ZL");
+    fixed_scratch.append(mangled_name.GetCString() + 2);
+    alternates.insert(ConstString(fixed_scratch));
+  }
+
+  // `char` is implementation defined as either `signed` or `unsigned`.  As a
+  // result a char parameter has 3 possible manglings: 'c'-char, 'a'-signed
+  // char, 'h'-unsigned char.  If we're looking for symbols with a signed char
+  // parameter, try finding matches which have the general case 'c'.
+  if (ConstString char_fixup =
+          SubsPrimitiveParmItanium(mangled_name.GetStringRef(), "a", "c"))
+    alternates.insert(char_fixup);
+
+  // long long parameter mangling 'x', may actually just be a long 'l' argument
+  if (ConstString long_fixup =
+          SubsPrimitiveParmItanium(mangled_name.GetStringRef(), "x", "l"))
+    alternates.insert(long_fixup);
+
+  // unsigned long long parameter mangling 'y', may actually just be unsigned
+  // long 'm' argument
+  if (ConstString ulong_fixup =
+          SubsPrimitiveParmItanium(mangled_name.GetStringRef(), "y", "m"))
+    alternates.insert(ulong_fixup);
+
+  return alternates.size() - start_size;
+}
+
 static void LoadLibCxxFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
   if (!cpp_category_sp)
     return;
@@ -931,7 +1031,7 @@ std::unique_ptr<Language::TypeScavenger> CPlusPlusLanguage::GetTypeScavenger() {
       return candidate;
     }
   };
-  
+
   return std::unique_ptr<TypeScavenger>(new CPlusPlusTypeScavenger());
 }
 
index af60957..be5cbae 100644 (file)
@@ -12,6 +12,7 @@
 
 // C Includes
 // C++ Includes
+#include <set>
 #include <vector>
 
 // Other libraries and framework includes
@@ -93,7 +94,6 @@ public:
   }
 
   std::unique_ptr<TypeScavenger> GetTypeScavenger() override;
-  
   lldb::TypeCategoryImplSP GetFormatters() override;
 
   HardcodedFormatters::HardcodedSummaryFinder GetHardcodedSummaries() override;
@@ -142,6 +142,12 @@ public:
   static uint32_t FindEquivalentNames(ConstString type_name,
                                       std::vector<ConstString> &equivalents);
 
+  // Given a mangled function name, calculates some alternative manglings since
+  // the compiler mangling may not line up with the symbol we are expecting
+  static uint32_t
+  FindAlternateFunctionManglings(const ConstString mangled,
+                                 std::set<ConstString> &candidates);
+
   //------------------------------------------------------------------
   // PluginInterface protocol
   //------------------------------------------------------------------