jarin created this revision.
Herald added subscribers: lldb-commits, arphaman, aprantl.
Herald added a project: LLDB.

WORK IN PROGRESS

This change is mostly for discussion, it is messy and likely wrong as I am new 
to the lldb codebase.

The goal of this exercise is to support expressions containing template 
instantiations, e.g. foo<int>::x.

The idea here is to collect all template types in the dwarf index. Later, when 
asked to find a name X, we import all the instantiations of X and pass the 
template declaration with the instantiations to Clang. In addition to my poor 
abstraction skills, the major problem with this is that we need to import all 
the instantiations even when only one is used. I tried to only import forward 
declarations, but if the instantiations were already previously completed, the 
AST importer still imports everything. This leads to massive evaluation times 
(tens of seconds) on templates with many instantiations (std::vector and the 
likes).

I am planning to pursue a different avenue, where I would introduce another 
hook into clang (perhaps in Sema::CheckTemplateIdType), but I am not sure how 
viable that path is.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D69309

Files:
  
lldb/packages/Python/lldbsuite/test/lang/cpp/class-template-instantiation/Makefile
  
lldb/packages/Python/lldbsuite/test/lang/cpp/class-template-instantiation/TestClassTemplateInstantiation.py
  
lldb/packages/Python/lldbsuite/test/lang/cpp/class-template-instantiation/main.cpp
  lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp
  lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp
  lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.h
  lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h
  lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp
  lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h
  lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp
  lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h
  lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
  lldb/source/Symbol/TypeMap.cpp

Index: lldb/source/Symbol/TypeMap.cpp
===================================================================
--- lldb/source/Symbol/TypeMap.cpp
+++ lldb/source/Symbol/TypeMap.cpp
@@ -141,6 +141,27 @@
                                exact_match);
 }
 
+namespace {
+
+bool TypeBasenamesMatch(const std::string &type_basename,
+                        llvm::StringRef match_type_basename,
+                        bool is_instantiation) {
+  if (match_type_basename == type_basename)
+    return true;
+  // If the basenames do not match, let us see if {match_type_basename} could
+  // be an instantiation of {type_basename}.
+  if (is_instantiation)
+    return false;
+  size_t basename_size = type_basename.size();
+  if (match_type_basename.size() <= basename_size)
+    return false;
+  if (match_type_basename[basename_size] != '<')
+    return false;
+  return match_type_basename.take_front(basename_size) == type_basename;
+}
+
+} // namespace
+
 void TypeMap::RemoveMismatchedTypes(const std::string &type_scope,
                                     const std::string &type_basename,
                                     TypeClass type_class, bool exact_match) {
@@ -152,6 +173,8 @@
 
   iterator pos, end = m_types.end();
 
+  bool is_instantiation = type_basename.find('<') != std::string::npos;
+
   for (pos = m_types.begin(); pos != end; ++pos) {
     Type *the_type = pos->second.get();
     bool keep_match = false;
@@ -171,7 +194,8 @@
       if (Type::GetTypeScopeAndBasename(match_type_name, match_type_scope,
                                         match_type_basename,
                                         match_type_class)) {
-        if (match_type_basename == type_basename) {
+        if (TypeBasenamesMatch(type_basename, match_type_basename,
+                               is_instantiation)) {
           const size_t type_scope_size = type_scope.size();
           const size_t match_type_scope_size = match_type_scope.size();
           if (exact_match || (type_scope_size == match_type_scope_size)) {
@@ -203,7 +227,9 @@
       } else {
         // The type we are currently looking at doesn't exists in a namespace
         // or class, so it only matches if there is no type scope...
-        keep_match = type_scope.empty() && type_basename == match_type_name;
+        keep_match = type_scope.empty() &&
+                     TypeBasenamesMatch(type_basename, match_type_name,
+                                        is_instantiation);
       }
     }
 
Index: lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -2419,6 +2419,24 @@
     }
   }
 
+  die_offsets.clear();
+  m_index->GetGenericTypes(name, die_offsets);
+  for (size_t i = 0; i < die_offsets.size(); ++i) {
+    const DIERef &die_ref = die_offsets[i];
+    DWARFDIE die = GetDIE(die_ref);
+    if (die) {
+      if (!DIEInDeclContext(parent_decl_ctx, die))
+        continue; // The containing decl contexts don't match
+      if (Type *matching_type = ResolveType(die, true, true)) {
+        types.InsertUnique(matching_type->shared_from_this());
+        if (types.GetSize() >= max_matches)
+          break;
+      }
+    } else {
+      m_index->ReportInvalidDIERef(die_ref, name.GetStringRef());
+    }
+  }
+
   // Next search through the reachable Clang modules. This only applies for
   // DWARF objects compiled with -gmodules that haven't been processed by
   // dsymutil.
Index: lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h
+++ lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h
@@ -34,6 +34,9 @@
                             DIEArray &offsets) override;
   void GetTypes(ConstString name, DIEArray &offsets) override;
   void GetTypes(const DWARFDeclContext &context, DIEArray &offsets) override;
+  void GetGenericTypes(ConstString name, DIEArray &offsets) override;
+  void GetGenericTypes(const DWARFDeclContext &context,
+                       DIEArray &offsets) override;
   void GetNamespaces(ConstString name, DIEArray &offsets) override;
   void GetFunctions(ConstString name, SymbolFileDWARF &dwarf,
                     const CompilerDeclContext &parent_decl_ctx,
@@ -53,6 +56,7 @@
     NameToDIE objc_class_selectors;
     NameToDIE globals;
     NameToDIE types;
+    NameToDIE generic_types;
     NameToDIE namespaces;
   };
   void Index();
Index: lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp
+++ lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp
@@ -84,6 +84,7 @@
                      [&]() { finalize_fn(&IndexSet::objc_class_selectors); },
                      [&]() { finalize_fn(&IndexSet::globals); },
                      [&]() { finalize_fn(&IndexSet::types); },
+                     [&]() { finalize_fn(&IndexSet::generic_types); },
                      [&]() { finalize_fn(&IndexSet::namespaces); });
 }
 
@@ -311,10 +312,20 @@
     case DW_TAG_typedef:
     case DW_TAG_union_type:
     case DW_TAG_unspecified_type:
-      if (name && !is_declaration)
-        set.types.Insert(ConstString(name), ref);
-      if (mangled_cstr && !is_declaration)
+      if (name && !is_declaration) {
+        ConstString name_cs(name);
+        set.types.Insert(name_cs, ref);
+        if (Language::LanguageIsCPlusPlus(cu_language) && !name_cs.IsEmpty() &&
+            name[name_cs.GetLength() - 1] == '>') {
+          const char *angle_bracket_pos = strchr(name, '<');
+          assert(angle_bracket_pos && "missing matching angle bracket");
+          size_t generic_length = angle_bracket_pos - name;
+          set.generic_types.Insert(ConstString(name, generic_length), ref);
+        }
+      }
+      if (mangled_cstr && !is_declaration) {
         set.types.Insert(ConstString(mangled_cstr), ref);
+      }
       break;
 
     case DW_TAG_namespace:
@@ -388,6 +399,17 @@
   m_set.types.Find(ConstString(context[0].name), offsets);
 }
 
+void ManualDWARFIndex::GetGenericTypes(ConstString name, DIEArray &offsets) {
+  Index();
+  m_set.generic_types.Find(name, offsets);
+}
+
+void ManualDWARFIndex::GetGenericTypes(const DWARFDeclContext &context,
+                                       DIEArray &offsets) {
+  Index();
+  m_set.generic_types.Find(ConstString(context[0].name), offsets);
+}
+
 void ManualDWARFIndex::GetNamespaces(ConstString name, DIEArray &offsets) {
   Index();
   m_set.namespaces.Find(name, offsets);
Index: lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h
+++ lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h
@@ -33,6 +33,9 @@
                             DIEArray &offsets) override;
   void GetTypes(ConstString name, DIEArray &offsets) override;
   void GetTypes(const DWARFDeclContext &context, DIEArray &offsets) override;
+  void GetGenericTypes(ConstString name, DIEArray &offsets) override;
+  void GetGenericTypes(const DWARFDeclContext &context,
+                       DIEArray &offsets) override;
   void GetNamespaces(ConstString name, DIEArray &offsets) override;
   void GetFunctions(ConstString name, SymbolFileDWARF &dwarf,
                     const CompilerDeclContext &parent_decl_ctx,
Index: lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp
+++ lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp
@@ -204,6 +204,16 @@
   }
 }
 
+void DebugNamesDWARFIndex::GetGenericTypes(ConstString name,
+                                           DIEArray &offsets) {
+  m_fallback.GetGenericTypes(name, offsets);
+}
+
+void DebugNamesDWARFIndex::GetGenericTypes(const DWARFDeclContext &context,
+                                           DIEArray &offsets) {
+  m_fallback.GetGenericTypes(context, offsets);
+}
+
 void DebugNamesDWARFIndex::GetNamespaces(ConstString name, DIEArray &offsets) {
   m_fallback.GetNamespaces(name, offsets);
 
Index: lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h
+++ lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h
@@ -38,6 +38,9 @@
                                     DIEArray &offsets) = 0;
   virtual void GetTypes(ConstString name, DIEArray &offsets) = 0;
   virtual void GetTypes(const DWARFDeclContext &context, DIEArray &offsets) = 0;
+  virtual void GetGenericTypes(ConstString name, DIEArray &offsets) = 0;
+  virtual void GetGenericTypes(const DWARFDeclContext &context,
+                               DIEArray &offsets) = 0;
   virtual void GetNamespaces(ConstString name, DIEArray &offsets) = 0;
   virtual void GetFunctions(ConstString name, SymbolFileDWARF &dwarf,
                             const CompilerDeclContext &parent_decl_ctx,
Index: lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.h
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.h
+++ lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.h
@@ -41,6 +41,9 @@
                             DIEArray &offsets) override;
   void GetTypes(ConstString name, DIEArray &offsets) override;
   void GetTypes(const DWARFDeclContext &context, DIEArray &offsets) override;
+  void GetGenericTypes(ConstString name, DIEArray &offsets) override;
+  void GetGenericTypes(const DWARFDeclContext &context,
+                       DIEArray &offsets) override;
   void GetNamespaces(ConstString name, DIEArray &offsets) override;
   void GetFunctions(ConstString name, SymbolFileDWARF &dwarf,
                     const CompilerDeclContext &parent_decl_ctx,
Index: lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp
+++ lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp
@@ -148,6 +148,15 @@
   m_apple_types_up->FindByName(type_name.GetStringRef(), offsets);
 }
 
+void AppleDWARFIndex::GetGenericTypes(ConstString name, DIEArray &offsets) {
+  return;
+}
+
+void AppleDWARFIndex::GetGenericTypes(const DWARFDeclContext &context,
+                                      DIEArray &offsets) {
+  return;
+}
+
 void AppleDWARFIndex::GetNamespaces(ConstString name, DIEArray &offsets) {
   if (m_apple_namespaces_up)
     m_apple_namespaces_up->FindByName(name.GetStringRef(), offsets);
Index: lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp
===================================================================
--- lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp
+++ lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp
@@ -808,6 +808,27 @@
          name_string_ref.startswith("_$");
 }
 
+namespace {
+
+ClassTemplateDecl *
+TryGetSpecializedTemplateFromInstantiation(const CompilerType compiler_type) {
+  clang::QualType qual_type = ClangUtil::GetQualType(compiler_type);
+  if (qual_type->getTypeClass() == clang::Type::Record) {
+    const clang::CXXRecordDecl *cxx_record_decl =
+        qual_type->getAsCXXRecordDecl();
+    if (cxx_record_decl) {
+      const clang::ClassTemplateSpecializationDecl *d =
+          llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(
+              cxx_record_decl);
+      if (d)
+        return d->getSpecializedTemplate();
+    }
+  }
+  return nullptr;
+}
+
+} // namespace
+
 void ClangASTSource::FindExternalVisibleDecls(
     NameSearchContext &context, lldb::ModuleSP module_sp,
     CompilerDeclContext &namespace_decl, unsigned int current_id) {
@@ -905,6 +926,24 @@
           continue;
         }
 
+        bool prefer_generic =
+            name.IsEmpty() || name.GetStringRef()[name.GetLength() - 1] != '>';
+        if (prefer_generic) {
+          if (ClassTemplateDecl *t = TryGetSpecializedTemplateFromInstantiation(
+                  copied_clang_type)) {
+            // Try to import all instances of the template class and add the
+            // relevant class template to the context.
+            for (size_t gti = ti + 1; gti < num_types; ++gti) {
+              lldb::TypeSP inst_type_sp = types.GetTypeAtIndex(gti);
+
+              GuardedCopyType(inst_type_sp->GetForwardCompilerType());
+            }
+            context.AddNamedDecl(t);
+            context.m_found.type = true;
+            break;
+          }
+        }
+
         context.AddTypeDecl(copied_clang_type);
 
         context.m_found.type = true;
Index: lldb/packages/Python/lldbsuite/test/lang/cpp/class-template-instantiation/main.cpp
===================================================================
--- /dev/null
+++ lldb/packages/Python/lldbsuite/test/lang/cpp/class-template-instantiation/main.cpp
@@ -0,0 +1,27 @@
+template <typename T>
+struct foo {
+  static int x;
+};
+
+template <typename T>
+int foo<T>::x = 42 + sizeof(T);
+
+struct A {
+  template <typename T>
+  struct bar {
+    T f;
+  };
+
+  bar<int> bi;
+  bar<short> bs;
+
+  int size() {
+    return sizeof(bar<int>) + sizeof(bar<short>); // break method
+  }
+};
+
+int main() {
+  A a;
+  a.size();
+  return foo<char>::x + foo<int>::x;  // break main
+}
Index: lldb/packages/Python/lldbsuite/test/lang/cpp/class-template-instantiation/TestClassTemplateInstantiation.py
===================================================================
--- /dev/null
+++ lldb/packages/Python/lldbsuite/test/lang/cpp/class-template-instantiation/TestClassTemplateInstantiation.py
@@ -0,0 +1,60 @@
+"""
+Test that the expression evaluator can instantiate templates. We should be able
+to instantiate at least those templates that are instantiated in the symbol
+file.
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestClassTemplateInstantiation(TestBase):
+    mydir = TestBase.compute_mydir(__file__)
+
+    @skipIf(debug_info=no_match(["dwarf"]),oslist=no_match(["macosx"]))
+    def test_instantiate_template_from_function(self):
+        self.main_source_file = lldb.SBFileSpec("main.cpp")
+        self.build()
+        (_, _, thread, _) = lldbutil.run_to_source_breakpoint(self, "// break main", self.main_source_file)
+        frame = thread.GetSelectedFrame()
+
+        expr_result = frame.EvaluateExpression("foo<char>::x")
+        self.assertTrue(expr_result.IsValid())
+        self.assertEqual(expr_result.GetValue(), "43")
+
+        expr_result = frame.EvaluateExpression("foo<int>::x")
+        self.assertTrue(expr_result.IsValid())
+        self.assertEqual(expr_result.GetValue(), "46")
+
+        expr_result = frame.EvaluateExpression("sizeof(A::bar<short>)")
+        self.assertTrue(expr_result.IsValid())
+        self.assertEqual(expr_result.GetValue(), "2")
+
+        expr_result = frame.EvaluateExpression("sizeof(A::bar<int>)")
+        self.assertTrue(expr_result.IsValid())
+        self.assertEqual(expr_result.GetValue(), "4")
+
+    @skipIf(debug_info=no_match(["dwarf"]),oslist=no_match(["macosx"]))
+    def test_instantiate_template_from_method(self):
+        self.main_source_file = lldb.SBFileSpec("main.cpp")
+        self.build()
+        (_, _, thread, _) = lldbutil.run_to_source_breakpoint(self, "// break method", self.main_source_file)
+        frame = thread.GetSelectedFrame()
+
+        expr_result = frame.EvaluateExpression("sizeof(bar<short>)")
+        self.assertTrue(expr_result.IsValid())
+        self.assertEqual(expr_result.GetValue(), "2")
+
+        expr_result = frame.EvaluateExpression("sizeof(bar<int>)")
+        self.assertTrue(expr_result.IsValid())
+        self.assertEqual(expr_result.GetValue(), "4")
+
+        expr_result = frame.EvaluateExpression("::foo<char>::x")
+        self.assertTrue(expr_result.IsValid())
+        self.assertEqual(expr_result.GetValue(), "43")
+
+        expr_result = frame.EvaluateExpression("foo<int>::x")
+        self.assertTrue(expr_result.IsValid())
+        self.assertEqual(expr_result.GetValue(), "46")
Index: lldb/packages/Python/lldbsuite/test/lang/cpp/class-template-instantiation/Makefile
===================================================================
--- /dev/null
+++ lldb/packages/Python/lldbsuite/test/lang/cpp/class-template-instantiation/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to