Implement fast type lookup in a corpus
authorDodji Seketeli <dodji@redhat.com>
Thu, 15 Oct 2015 08:14:22 +0000 (10:14 +0200)
committerDodji Seketeli <dodji@redhat.com>
Thu, 15 Oct 2015 11:50:48 +0000 (13:50 +0200)
Profiling has shown that on libraries with a lot of class types
declarations (more than 10K types), the phase of resolving those
declarations to their definition was a hot spot.  The lookup of the
type definition inside the entire corpus was the bottleneck.

This patch removes (or loosen) that bottleneck by doing away with the
graph-walking-based type lookup algorithm that was used.  Rather, maps
of name -> types are maintained by each scope, in each translation
unit. Those maps are updated each time a type is added to a scope.
And looking up a type amounts to a lookup in a map.  Way faster.

* include/abg-fwd.h (components_to_type_name): Declare new
function.
* include/abg-ir.h (string_type_base_wptr_map_type): New typedef.
(translation_unit::{get,set}_types): Declare new member functions.
* src/abg-ir.cc (translation_unit::priv::types_): New data member.
(translation_unit::{get,set}_types): Define these member
functions.
(maybe_update_types_lookup_map): Define new static function.
(components_to_type_name): Define new function.
(scope_decl::{add_member_decl, insert_member_decl}): Call the new
maybe_update_types_lookup_map.
(scope_decl::find_iterator_for_member): Fix logic.
(class_decl::set_is_declaration_only): When a class declaration
becomes a definition, update the name -> type map maintained in
the scope of the class.
(lookup_type_in_translation_unit): Use the hash map of qualified
name -> types that is now maintained in the translation unit.
This is way faster than the previous walking algorithm.
* src/abg-dwarf-reader.cc (build_translation_unit_and_add_to_ir):
When fixing up global variable declarations that need to be
re-added to the translation unit, use the new fast type lookup
function.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
include/abg-fwd.h
include/abg-ir.h
src/abg-dwarf-reader.cc
src/abg-ir.cc

index 0ebe430..67b720b 100644 (file)
@@ -633,6 +633,9 @@ void
 fqn_to_components(const std::string&,
                  std::list<string>&);
 
+string
+components_to_type_name(const std::list<string>&);
+
 const shared_ptr<decl_base>
 lookup_type_in_corpus(const string&, const corpus&);
 
index 82b232c..bebbd25 100644 (file)
@@ -279,6 +279,10 @@ struct ir_traversable_base : public traversable_base
   traverse(ir_node_visitor& v);
 }; // end class ir_traversable_base
 
+/// A convenience typedef for a map which key is a string and which
+/// value is a @ref type_base_wptr.
+typedef unordered_map<string, type_base_wptr> string_type_base_wptr_map_type;
+
 /// This is the abstraction of the set of relevant artefacts (types,
 /// variable declarations, functions, templates, etc) bundled together
 /// into a translation unit.
@@ -367,6 +371,12 @@ public:
   const function_types_type
   get_function_types() const;
 
+  const string_type_base_wptr_map_type&
+  get_types() const;
+
+  string_type_base_wptr_map_type&
+  get_types();
+
   location_manager&
   get_loc_mgr();
 
index 8e7cf77..2328849 100644 (file)
@@ -6137,9 +6137,13 @@ build_translation_unit_and_add_to_ir(read_context&       ctxt,
            string mem_name = fqn_comps.back();
            fqn_comps.pop_back();
            decl_base_sptr ty_decl;
+           string ty_name;
            if (!fqn_comps.empty())
-             ty_decl = lookup_type_in_translation_unit(fqn_comps,
-                                                       *ctxt.cur_tu());
+             {
+               ty_name = components_to_type_name(fqn_comps);
+               ty_decl = lookup_type_in_translation_unit(ty_name,
+                                                         *ctxt.cur_tu());
+             }
            if (class_decl_sptr cl = dynamic_pointer_cast<class_decl>(ty_decl))
              {
                // So we are seeing a member variable for which there
index f1b81d1..4a693ff 100644 (file)
@@ -241,16 +241,17 @@ typedef unordered_map<function_type_sptr,
 /// Private type to hold private members of @ref translation_unit
 struct translation_unit::priv
 {
-  environment*                         env_;
-  const corpus*                        corp;
-  bool                                 is_constructed_;
-  char                                 address_size_;
-  language                             language_;
-  std::string                          path_;
-  location_manager                     loc_mgr_;
-  mutable global_scope_sptr            global_scope_;
-  mutable function_types_type          function_types_;
-  mutable vector<type_base_sptr>       synthesized_types_;
+  environment*                                 env_;
+  const corpus*                                corp;
+  bool                                         is_constructed_;
+  char                                         address_size_;
+  language                                     language_;
+  std::string                                  path_;
+  location_manager                             loc_mgr_;
+  mutable global_scope_sptr                    global_scope_;
+  mutable function_types_type                  function_types_;
+  mutable vector<type_base_sptr>               synthesized_types_;
+  mutable string_type_base_wptr_map_type       types_;
 
   priv(environment* env)
     : env_(env),
@@ -314,6 +315,22 @@ const function_types_type
 translation_unit::get_function_types() const
 { return priv_->function_types_; }
 
+/// Getter of the types of the current @ref translation_unit.
+///
+/// @return a map of the types of the translation unit.  The key of
+/// the map is the qualified name of the type, and value is the type.
+const string_type_base_wptr_map_type&
+translation_unit::get_types() const
+{return priv_->types_;}
+
+/// Getter of the types of the current @ref translation_unit.
+///
+/// @return a map of the types of the translation unit.  The key of
+/// the map is the qualified name of the type, and value is the type.
+string_type_base_wptr_map_type&
+translation_unit::get_types()
+{return priv_->types_;}
+
 /// Getter of the environment of the current @ref translation_unit.
 ///
 /// @return the translation unit of the current translation unit.
@@ -3526,6 +3543,51 @@ static void
 update_qualified_name(decl_base_sptr d)
 {return update_qualified_name(d.get());}
 
+/// Update the map that is going to be used later for lookup of types
+/// in a given scope declaration.
+///
+/// That is, add a new name -> type relationship in the map, for a
+/// given type declaration.
+///
+/// @param member the declaration of the type to update the scope for.
+/// This type declaration must be added to the scope, after or before
+/// invoking this function.  If it appears that this @p member is not
+/// a type, this function does nothing.  Also, if this member is a
+/// declaration-only class, the function does nothing.
+static void
+maybe_update_types_lookup_map(scope_decl *scope,
+                             decl_base_sptr member)
+{
+  string n = member->get_qualified_name();
+  type_base_sptr t = is_type(member);
+  bool update_qname_map = t;
+  if (update_qname_map)
+    {
+      if (class_decl_sptr c = is_class_type(member))
+       {
+         if (c->get_is_declaration_only())
+           {
+             if (class_decl_sptr def = c->get_definition_of_declaration())
+               t = def;
+             else
+               update_qname_map = false;
+           }
+       }
+    }
+  if (update_qname_map)
+    {
+      translation_unit* tu = get_translation_unit(scope);
+      if (tu)
+       {
+         string qname = member->get_qualified_name();
+         string_type_base_wptr_map_type& types = tu->get_types();
+         string_type_base_wptr_map_type::iterator it = types.find(qname);
+         if (it == types.end())
+           types[qname] = t;
+       }
+    }
+}
+
 /// Add a member decl to this scope.  Note that user code should not
 /// use this, but rather use add_decl_to_scope.
 ///
@@ -3559,6 +3621,8 @@ scope_decl::add_member_decl(const decl_base_sptr member)
        member->set_corpus(c);
     }
 
+  maybe_update_types_lookup_map(this, member);
+
   return member;
 }
 
@@ -3598,6 +3662,8 @@ scope_decl::insert_member_decl(const decl_base_sptr member,
        member->set_corpus(c);
     }
 
+  maybe_update_types_lookup_map(this, member);
+
   return member;
 }
 
@@ -3755,38 +3821,21 @@ bool
 scope_decl::find_iterator_for_member(const decl_base* decl,
                                     declarations::iterator& i)
 {
-  if (class_decl* klass = dynamic_cast<class_decl*>(this))
-    assert(!klass->get_is_declaration_only());
-
   if (!decl)
     return false;
 
   if (get_member_decls().empty())
     {
       i = get_member_decls().end();
-      return true;
+      return false;
     }
 
-  const class_decl* is_class = dynamic_cast<const class_decl*>(decl);
-  if (is_class)
-    assert(!is_class->get_is_declaration_only());
-
-  string qual_name1 = decl->get_qualified_name();
   for (declarations::iterator it = get_member_decls().begin();
        it != get_member_decls().end();
        ++it)
     {
-      string qual_name2 = (*it)->get_qualified_name();
-      if (qual_name1 == qual_name2)
+      if ((*it).get() == decl)
        {
-         if (is_class)
-           {
-             class_decl_sptr cur_class =
-               dynamic_pointer_cast<class_decl>(*it);
-             assert(cur_class);
-             if (cur_class->get_is_declaration_only())
-               continue;
-           }
          i = it;
          return true;
        }
@@ -5327,6 +5376,27 @@ fqn_to_components(const string& fqn,
     } while (true);
 }
 
+/// Turn a set of qualified name components (that name a type) into a
+/// qualified name string.
+///
+/// @param comps the name components
+///
+/// @return the resulting string, which would be the qualified name of
+/// a type.
+string
+components_to_type_name(const list<string>& comps)
+{
+  string result;
+  for (list<string>::const_iterator c = comps.begin();
+       c != comps.end();
+       ++c)
+    if (c == comps.begin())
+      result = *c;
+    else
+      result += "::" + *c;
+  return result;
+}
+
 /// This predicate returns true if a given container iterator points
 /// to the last element of the container, false otherwise.
 ///
@@ -5360,9 +5430,15 @@ const decl_base_sptr
 lookup_type_in_translation_unit(const string& fqn,
                                const translation_unit& tu)
 {
-  list<string> comps;
-  fqn_to_components(fqn, comps);
-  return lookup_type_in_translation_unit(comps, tu);
+  decl_base_sptr result;
+  const string_type_base_wptr_map_type& types = tu.get_types();
+  string_type_base_wptr_map_type::const_iterator it, nil = types.end();
+  it = types.find(fqn);
+  if (it != nil)
+    if (!it->second.expired())
+      result = get_type_declaration(type_base_sptr(it->second));
+
+  return result;
 }
 
 /// Lookup a class type from a translation unit.
@@ -5377,11 +5453,7 @@ lookup_type_in_translation_unit(const string& fqn,
 const class_decl_sptr
 lookup_class_type_in_translation_unit(const string& fqn,
                                      const translation_unit& tu)
-{
-  list<string> comps;
-  fqn_to_components(fqn, comps);
-  return lookup_class_type_in_translation_unit(comps, tu);
-}
+{return is_class_type(lookup_type_in_translation_unit(fqn, tu));}
 
 /// Lookup a function type from a translation unit.
 ///
@@ -10389,7 +10461,18 @@ class_decl::get_is_declaration_only() const
 /// @param f true if the class is a decalaration-only class.
 void
 class_decl::set_is_declaration_only(bool f)
-{priv_->is_declaration_only_ = f;}
+{
+  priv_->is_declaration_only_ = f;
+  if (!f)
+    if (scope_decl* s = get_scope())
+      {
+       declarations::iterator i;
+       if (s->find_iterator_for_member(this, i))
+         maybe_update_types_lookup_map(s, *i);
+       else
+         abort();
+      }
+}
 
 /// Set the "is-struct" flag of the class.
 ///