Support naming typedef and use them to speed up type canonicalization
authorDodji Seketeli <dodji@redhat.com>
Tue, 11 Oct 2016 13:06:02 +0000 (15:06 +0200)
committerDodji Seketeli <dodji@redhat.com>
Tue, 29 Nov 2016 15:44:06 +0000 (16:44 +0100)
There is a common C idiom in which an anonymous struct is named using
a typedef:

    typedef struct {int member;} anonymous_struct_type;

The typedef name "anonymous_struct_type" becomes the name of the
otherwise anonymous struct.  That is what a naming typedef is.

So, the nice thing about naming typedefs is that an anonymous class
type suddenly becomes non anymous.  So that type becomes eligible to
the ODR-based optimization during type canonicalization.  That speeds
up type canonicalization, at least for 'abidw'.

This patch represents naming typedefs for class_decl types in the
internal representation.  The patch also changes the meaning of an
anonymous class.  Whenever such a class becomes named by a typedef,
the class is not considered anonymous anymore, at least for the
purpose of type canonicalization.

* include/abg-ir.h (typedef_decl_wptr): New typedef.
(class_decl::{g,s}et_naming_typedef): Declare new member
functions.
* src/abg-dwarf-reader.cc (build_typedef_type): When the
underlying type of a typedef is an anonymous class, the class type
is said to have a naming typedef.
* src/abg-ir.cc (is_anonymous_type):  An anonymous class that has
a naming typedef is said to not be anonymous anymore.
(class_decl::priv::naming_typedef): New data member.
(class_decl::{g,s}et_naming_typedef): Define new member functions.
(class_decl::get_pretty_representation): When called for internal
purposes (e.g, for type canonicalization) compute the pretty
representation of the class by using its typedef name, when class
is anonymous and has a naming typedef.
* src/abg-reader.cc (build_class_decl): Read the new
"naming-typedef-id" attribute.
* src/abg-writer.cc (write_naming_typedef): New function.
(write_class_decl_opening_tag): Use the new write_naming_typedef
function.
* tests/data/test-read-dwarf/libtest23.so.abi: Adjust.
* tests/data/test-read-dwarf/libtest24-drop-fns-2.so.abi:
Likewise.
* tests/data/test-read-dwarf/libtest24-drop-fns.so.abi: Likewise.
* tests/data/test-read-dwarf/test10-pr18818-gcc.so.abi: Likewise.
* tests/data/test-read-dwarf/test11-pr18828.so.abi: Likewise.
* tests/data/test-read-dwarf/test12-pr18844.so.abi: Likewise.
* tests/data/test-read-dwarf/test13-pr18894.so.abi: Likewise.
* tests/data/test-read-dwarf/test14-pr18893.so.abi: Likewise.
* tests/data/test-read-dwarf/test15-pr18892.so.abi: Likewise.
* tests/data/test-read-dwarf/test16-pr18904.so.abi: Likewise.
* tests/data/test-read-dwarf/test21-pr19092.so.abi: Likewise.
* tests/data/test-read-dwarf/test22-pr19097-libstdc++.so.6.0.17.so.abi:
Likewise.
* tests/data/test-read-dwarf/test9-pr18818-clang.so.abi: Likewise.

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

index 045246678984e97b3b386c7118bbc385679239af..1e9b8e224d6d77e7a4333c9f56a3d9445cf5ed7e 100644 (file)
@@ -2102,6 +2102,9 @@ equals(const typedef_decl&, const typedef_decl&, change_kind*);
 /// Convenience typedef for a shared pointer on a @ref typedef_decl.
 typedef shared_ptr<typedef_decl> typedef_decl_sptr;
 
+/// Convenience typedef for a weak pointer on a @ref typedef_decl.
+typedef weak_ptr<typedef_decl> typedef_decl_wptr;
+
 /// The abstraction of a typedef declaration.
 class typedef_decl : public virtual type_base, public virtual decl_base
 {
@@ -3273,6 +3276,12 @@ public:
   virtual void
   set_alignment_in_bits(size_t);
 
+  typedef_decl_sptr
+  get_naming_typedef() const;
+
+  void
+  set_naming_typedef(const typedef_decl_sptr&);
+
   bool
   get_is_declaration_only() const;
 
index fbbc5ba5c80fadc1bc80969b7afe13cd4290e975..c6bc4e5f83239dc87518baf1bd64abe648d5a6dd 100644 (file)
@@ -11134,6 +11134,11 @@ build_typedef_type(read_context&       ctxt,
 
   result.reset(new typedef_decl(name, utype, loc, linkage_name));
   ctxt.associate_die_to_type(die, result, where_offset);
+
+  if (class_decl_sptr klass = is_class_type(utype))
+    if (is_anonymous_type(klass))
+      klass->set_naming_typedef(result);
+
   return result;
 }
 
index 97d417a4064d0ae8eb702fd25e7526f5d489b8e3..8bc6a21e7e3b47091846c35b7c7f11879e5802ef 100644 (file)
@@ -5729,6 +5729,14 @@ is_type(decl_base* decl)
 
 /// Test if a given type is anonymous.
 ///
+/// Note that this function considers that an anonymous class that is
+/// named by a typedef is not anonymous anymore.  This is the C idiom:
+///
+///       typedef struct {int member;} s_type;
+///
+/// The typedef s_type becomes the name of the originally anonymous
+/// struct.
+///
 /// @param t the type to consider.
 ///
 /// @return true iff @p t is anonymous.
@@ -5736,9 +5744,20 @@ bool
 is_anonymous_type(type_base* t)
 {
   decl_base* d = get_type_declaration(t);
-  if (!d)
-    return false;
-  return d->get_is_anonymous();
+  if (d)
+    if (d->get_is_anonymous())
+      {
+       if (class_decl *klass = is_class_type(t))
+         {
+           // An anonymous class that is named by a typedef is not
+           // considered anonymous anymore.
+           if (!klass->get_naming_typedef())
+             return true;
+         }
+       else
+         return true;
+      }
+  return false;
 }
 
 /// Test if a given type is anonymous.
@@ -12263,6 +12282,7 @@ function_decl::parameter::get_pretty_representation(bool internal) const
 // <class_or_union definitions>
 struct class_or_union::priv
 {
+  typedef_decl_wptr            naming_typedef_;
   decl_base_sptr               declaration_;
   class_or_union_wptr          definition_of_declaration_;
   member_types                 member_types_;
@@ -12766,6 +12786,40 @@ class_or_union::set_is_declaration_only(bool f)
       }
 }
 
+/// Getter for the naming typedef of the current class.
+///
+/// Consider the C idiom:
+///
+///    typedef struct {int member;} foo_type;
+///
+/// In that idiom, foo_type is the naming typedef of the anonymous
+/// struct that is declared.
+///
+/// @return the naming typedef, if any.  Otherwise, returns nil.
+typedef_decl_sptr
+class_or_union::get_naming_typedef() const
+{
+  if (priv_->naming_typedef_.expired())
+    return typedef_decl_sptr();
+  return typedef_decl_sptr(priv_->naming_typedef_);
+}
+
+/// Set the naming typedef of the current instance of @ref class_decl.
+///
+/// Consider the C idiom:
+///
+///    typedef struct {int member;} foo_type;
+///
+/// In that idiom, foo_type is the naming typedef of the anonymous
+/// struct that is declared.
+///
+/// @param typedef_type the new naming typedef.
+void
+class_or_union::set_naming_typedef(const typedef_decl_sptr& typedef_type)
+{
+  priv_->naming_typedef_ = typedef_type;
+}
+
 /// Set the definition of this declaration-only @ref class_or_union.
 ///
 /// @param d the new definition to set.
@@ -13647,6 +13701,18 @@ class_decl::get_pretty_representation(bool internal) const
   string cl = "class ";
   if (!internal && is_struct())
     cl = "struct ";
+
+  // When computing the pretty representation for internal purposes,
+  // if an anonymous class is named by a typedef, then consider that
+  // it has a name, which is the typedef name.
+  if (internal && get_is_anonymous())
+    if (typedef_decl_sptr d = get_naming_typedef())
+      {
+       string qualified_name =
+         decl_base::priv_->qualified_parent_name_ + "::" + d->get_name();
+       return cl + qualified_name;
+      }
+
   return cl + get_qualified_name(internal);
 }
 
index 45d89f2fdecc8bc4eb21be23768569f47f5ebc1f..235c816d15f0b1b25b6306d52ec73a4229216750 100644 (file)
@@ -3898,6 +3898,14 @@ build_class_decl(read_context&           ctxt,
   bool is_anonymous = false;
   read_is_anonymous(node, is_anonymous);
 
+  string naming_typedef_id;
+  typedef_decl_sptr naming_typedef;
+
+  if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "naming-typedef-id"))
+    naming_typedef_id = xml::unescape_xml_string(CHAR_STR(s));
+  if (!naming_typedef_id.empty())
+      naming_typedef = is_typedef(ctxt.get_type_decl(naming_typedef_id));
+
   assert(!id.empty());
   class_decl_sptr previous_definition, previous_declaration;
   const vector<type_base_sptr> *types_ptr = ctxt.get_all_type_decls(id);
@@ -3953,6 +3961,9 @@ build_class_decl(read_context&            ctxt,
       decl->set_is_anonymous(is_anonymous);
     }
 
+  if (naming_typedef)
+    decl->set_naming_typedef(naming_typedef);
+
   string def_id;
   bool is_def_of_decl = false;
   if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "def-of-decl-id"))
index 2ccf824a91033952e0bd2f7fb8a558f92ef134e9..192a89d07cb2aaea9b6d10c62af8d929ec8b9166 100644 (file)
@@ -519,6 +519,7 @@ static void write_class_or_union_is_declaration_only(const class_or_union_sptr&,
                                                     ostream&);
 static void write_is_struct(const class_decl_sptr&, ostream&);
 static void write_is_anonymous(const decl_base_sptr&, ostream&);
+static void write_naming_typedef(const class_decl_sptr&, write_context&);
 static bool write_decl(const decl_base_sptr&, write_context&, unsigned);
 static void write_decl_in_scope(const decl_base_sptr&,
                                write_context&, unsigned);
@@ -1095,6 +1096,27 @@ write_is_anonymous(const decl_base_sptr& decl, ostream& o)
     o << " is-anonymous='yes'";
 }
 
+/// Serialize the "naming-typedef-id" attribute, if the current
+/// instance of @ref class_decl has a naming typedef.
+///
+/// @param klass the @ref class_decl to consider.
+///
+/// @param ctxt the write context to use.
+static void
+write_naming_typedef(const class_decl_sptr& klass, write_context& ctxt)
+{
+  if (!klass)
+    return;
+
+  ostream &o = ctxt.get_ostream();
+
+  if (typedef_decl_sptr typedef_type = klass->get_naming_typedef())
+    {
+      string id = ctxt.get_id_for_type(typedef_type);
+      o << " naming-typedef-id='" << id << "'";
+    }
+}
+
 /// Serialize a pointer to an of decl_base into an output stream.
 ///
 /// @param decl the pointer to decl_base to serialize
@@ -2393,6 +2415,8 @@ write_class_decl_opening_tag(const class_decl_sptr&       decl,
 
   write_is_anonymous(decl, o);
 
+  write_naming_typedef(decl, ctxt);
+
   write_visibility(decl, o);
 
   write_location(decl, o);