On Tue, 2020-01-14 at 00:26 +0100, Jakub Jelinek wrote:
> On Mon, Jan 13, 2020 at 11:56:14PM +0100, Jakub Jelinek wrote:
> > > Some options:
> > > (a) the patch to fix hash_table::empty, and the analyzer kit
> > > (b) the analyzer kit with the following kludge
> > > (c) someone with better C++-fu than me figure out a way to get
> > > the
> > > memset optimization for hash_map with 0-valued empty (or to give
> > > me
> > > some suggestions)
> > > (d) not merge the analyzer for gcc 10
> > 
> > For (c), I see right now we have 37 mark_empty definitions:
> > find -type f | grep -v 'testsuite/\|ChangeLog' | xargs grep
> > mark_empty | wc -l
> > 37
> > > From quick skimming of them, most of them are just zeroing one or
> > > more
> > fields, exceptions are profile.c, attribs.c (this one only in self-
> > tests
> > and it is unclear why deleted is two NULLs and empty two ""s rather
> > than
> > vice versa), and gcov.c.  Also, int_hash can be zero or non-zero,
> > depending
> > on template argument, e.g.
> > typedef hash_map<int_hash <unsigned int, -1U>, unsigned int>
> > live_vars_map;
> > struct uid_hash : int_hash <int, -1, -2> {};
> > are not either.
> > Can't we add next to the mark_empty and is_empty methods also a
> > static const
> > bool data member empty_zero_p or similar and use it in in the two
> > hash-table.h spots where this would make a difference?
> > In alloc_entries the
> >   for (size_t i = 0; i < n; i++)
> >     mark_empty (nentries[i]);
> > loop could be conditionalized on !Descriptor::empty_zero_p because
> > both
> > allocation paths actually allocate cleared memory, and in the spot
> > you are talking about.
> 
> Just as a proof of concept, the following compiles (but haven't done
> any
> testing beoyond that, not even looked if/when the memcpy vs. loop is
> in
> there), the formatting/placement of the static data members could be
> adjusted, in graphite.c I just gave up (it is weird, empty is when
> one field
> is NULL, deleted when another field is NULL, but what is zeroed
> memory, is
> that both empty and deleted at the same time?).

Thanks.  Does it have warnings, though?

My attempt was similar, but ran into warnings from -Wclass-memaccess in
four places, like this:

../../src/gcc/hash-map-traits.h:102:12: warning: ‘void* memset(void*,
int, size_t)’ clearing an object of type ‘struct hash_map<tree_node*,
std::pair<tree_node*, tree_node*> >::hash_entry’ with no trivial copy-
assignment; use assignment or value-initialization instead [-Wclass-
memaccess]
  102 |     memset (entry, 0, sizeof (T) * count);
      |     ~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

where the types in question are:

(1)
  struct hash_map<tree_node*, std::pair<tree_node*, tree_node*>
>::hash_entry
  ../../src/gcc/tree-data-ref.c:844:17:   required from here

(2)
  struct hash_map<tree_node*, std::pair<int, unsigned int>
>::hash_entry
  ../../src/gcc/tree-ssa-strlen.c:5925:32:   required from here

(3)
  struct hash_map<edge_def*, auto_vec<edge_var_map> >::hash_entry
  ../../src/gcc/tree-ssa.c:130:27:   required from here

(4)
  struct hash_map<tree_decl_hash, class_decl_loc_t>::hash_entry
  ../../src/gcc/cp/parser.c:31073:20:   required from here


I tried to use std::is_pod, but got stuck.

Dave


> 
> diff --git a/gcc/attribs.c b/gcc/attribs.c
> index ca89443eb3e..c66d4ae2c06 100644
> --- a/gcc/attribs.c
> +++ b/gcc/attribs.c
> @@ -2048,6 +2048,8 @@ struct excl_hash_traits:
> typed_noop_remove<excl_pair>
>      x = value_type (NULL, NULL);
>    }
>  
> +  static const bool empty_zero_p = false;
> +
>    static void mark_empty (value_type &x)
>    {
>      x = value_type ("", "");
> diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
> index 3d764bb6928..f3aeb7475da 100644
> --- a/gcc/cp/cp-gimplify.c
> +++ b/gcc/cp/cp-gimplify.c
> @@ -3045,6 +3045,8 @@ struct source_location_table_entry_hash
>      ref.var = NULL_TREE;
>    }
>  
> +  static const bool empty_zero_p = true;
> +
>    static void
>    mark_empty (source_location_table_entry &ref)
>    {
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 98572bdbad1..c3ca4c8dace 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -900,6 +900,7 @@ struct named_decl_hash : ggc_remove <tree>
>    inline static hashval_t hash (const value_type decl);
>    inline static bool equal (const value_type existing, compare_type
> candidate);
>  
> +  static const bool empty_zero_p = true;
>    static inline void mark_empty (value_type &p) {p = NULL_TREE;}
>    static inline bool is_empty (value_type p) {return !p;}
>  
> @@ -1870,6 +1871,7 @@ struct named_label_hash : ggc_remove
> <named_label_entry *>
>    inline static hashval_t hash (value_type);
>    inline static bool equal (const value_type, compare_type);
>  
> +  static const bool empty_zero_p = true;
>    inline static void mark_empty (value_type &p) {p = NULL;}
>    inline static bool is_empty (value_type p) {return !p;}
>  
> diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
> index a641667991f..042d6fa12df 100644
> --- a/gcc/cp/decl2.c
> +++ b/gcc/cp/decl2.c
> @@ -120,6 +120,7 @@ struct mangled_decl_hash : ggc_remove <tree>
>      return candidate == name;
>    }
>  
> +  static const bool empty_zero_p = true;
>    static inline void mark_empty (value_type &p) {p = NULL_TREE;}
>    static inline bool is_empty (value_type p) {return !p;}
>  
> diff --git a/gcc/gcov.c b/gcc/gcov.c
> index 1dca3049777..a291bac3e9e 100644
> --- a/gcc/gcov.c
> +++ b/gcc/gcov.c
> @@ -1225,6 +1225,8 @@ struct function_start_pair_hash :
> typed_noop_remove <function_start>
>      ref.start_line = ~1U;
>    }
>  
> +  static const bool empty_zero_p = false;
> +
>    static void
>    mark_empty (function_start &ref)
>    {
> diff --git a/gcc/graphite.c b/gcc/graphite.c
> index 0ac46766b15..27f1e486e1f 100644
> --- a/gcc/graphite.c
> +++ b/gcc/graphite.c
> @@ -233,6 +233,7 @@ struct sese_scev_hash : typed_noop_remove
> <seir_cache_key>
>           && operand_equal_p (key1.expr, key2.expr, 0));
>    }
>    static void mark_deleted (seir_cache_key &key) { key.expr =
> NULL_TREE; }
> +  static const bool empty_zero_p = false;
>    static void mark_empty (seir_cache_key &key) { key.entry_dest = 0;
> }
>    static bool is_deleted (const seir_cache_key &key) { return
> !key.expr; }
>    static bool is_empty (const seir_cache_key &key) { return
> key.entry_dest == 0; }
> diff --git a/gcc/hash-map-traits.h b/gcc/hash-map-traits.h
> index 4764380b364..3b16be35f7d 100644
> --- a/gcc/hash-map-traits.h
> +++ b/gcc/hash-map-traits.h
> @@ -36,6 +36,7 @@ struct simple_hashmap_traits
>    static inline hashval_t hash (const key_type &);
>    static inline bool equal_keys (const key_type &, const key_type
> &);
>    template <typename T> static inline void remove (T &);
> +  static const bool empty_zero_p = H::empty_zero_p;
>    template <typename T> static inline bool is_empty (const T &);
>    template <typename T> static inline bool is_deleted (const T &);
>    template <typename T> static inline void mark_empty (T &);
> @@ -113,6 +114,7 @@ template <typename Value>
>  struct unbounded_hashmap_traits
>  {
>    template <typename T> static inline void remove (T &);
> +  static const bool empty_zero_p = default_hash_traits
> <Value>::empty_zero_p;
>    template <typename T> static inline bool is_empty (const T &);
>    template <typename T> static inline bool is_deleted (const T &);
>    template <typename T> static inline void mark_empty (T &);
> diff --git a/gcc/hash-map.h b/gcc/hash-map.h
> index 7cb466767ea..5b8fd184e32 100644
> --- a/gcc/hash-map.h
> +++ b/gcc/hash-map.h
> @@ -66,6 +66,7 @@ class GTY((user)) hash_map
>               return Traits::is_deleted (e);
>        }
>  
> +    static const bool empty_zero_p = Traits::empty_zero_p;
>      static void mark_empty (hash_entry &e) { Traits::mark_empty (e);
> }
>      static bool is_empty (const hash_entry &e) { return
> Traits::is_empty (e); }
>  
> diff --git a/gcc/hash-set-tests.c b/gcc/hash-set-tests.c
> index 696e35e9be0..bb32094be20 100644
> --- a/gcc/hash-set-tests.c
> +++ b/gcc/hash-set-tests.c
> @@ -199,6 +199,8 @@ struct value_hash_traits: int_hash<int, -1, -2>
>      base_type::mark_deleted (v.val);
>    }
>  
> +  static const bool empty_zero_p = false;
> +
>    static void mark_empty (value_type &v)
>    {
>      base_type::mark_empty (v.val);
> diff --git a/gcc/hash-table.h b/gcc/hash-table.h
> index e0ddac5f578..a1423c78112 100644
> --- a/gcc/hash-table.h
> +++ b/gcc/hash-table.h
> @@ -713,8 +713,9 @@ hash_table<Descriptor, Lazy,
>      nentries = ::ggc_cleared_vec_alloc<value_type> (n
> PASS_MEM_STAT);
>  
>    gcc_assert (nentries != NULL);
> -  for (size_t i = 0; i < n; i++)
> -    mark_empty (nentries[i]);
> +  if (!Descriptor::empty_zero_p)
> +    for (size_t i = 0; i < n; i++)
> +      mark_empty (nentries[i]);
>  
>    return nentries;
>  }
> @@ -867,8 +868,11 @@ hash_table<Descriptor, Lazy,
> Allocator>::empty_slow ()
>        m_size = nsize;
>        m_size_prime_index = nindex;
>      }
> -  else
> +  else if (Descriptor::empty_zero_p)
>      memset ((void *) entries, 0, size * sizeof (value_type));
> +  else
> +    for (size_t i = 0; i < size; i++)
> +      mark_empty (entries[i]);
>  
>    m_n_deleted = 0;
>    m_n_elements = 0;
> diff --git a/gcc/hash-traits.h b/gcc/hash-traits.h
> index d259a41a418..3bca74c56ea 100644
> --- a/gcc/hash-traits.h
> +++ b/gcc/hash-traits.h
> @@ -88,6 +88,7 @@ struct int_hash : typed_noop_remove <Type>
>    static inline hashval_t hash (value_type);
>    static inline bool equal (value_type existing, value_type
> candidate);
>    static inline void mark_deleted (Type &);
> +  static const bool empty_zero_p = Empty == 0;
>    static inline void mark_empty (Type &);
>    static inline bool is_deleted (Type);
>    static inline bool is_empty (Type);
> @@ -150,6 +151,7 @@ struct pointer_hash
>    static inline bool equal (const value_type &existing,
>                           const compare_type &candidate);
>    static inline void mark_deleted (Type *&);
> +  static const bool empty_zero_p = true;
>    static inline void mark_empty (Type *&);
>    static inline bool is_deleted (Type *);
>    static inline bool is_empty (Type *);
> @@ -323,6 +325,7 @@ struct pair_hash
>    static inline bool equal (const value_type &, const compare_type
> &);
>    static inline void remove (value_type &);
>    static inline void mark_deleted (value_type &);
> +  static const bool empty_zero_p = T1::empty_zero_p;
>    static inline void mark_empty (value_type &);
>    static inline bool is_deleted (const value_type &);
>    static inline bool is_empty (const value_type &);
> diff --git a/gcc/ipa-devirt.c b/gcc/ipa-devirt.c
> index b888186134c..f0031957375 100644
> --- a/gcc/ipa-devirt.c
> +++ b/gcc/ipa-devirt.c
> @@ -150,6 +150,7 @@ struct default_hash_traits <type_pair>
>    {
>      return TYPE_UID (p.first) ^ TYPE_UID (p.second);
>    }
> +  static const bool empty_zero_p = true;
>    static bool
>    is_empty (type_pair p)
>    {
> diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
> index 6dc3cf6b355..12cdb95cf2a 100644
> --- a/gcc/ipa-prop.c
> +++ b/gcc/ipa-prop.c
> @@ -78,6 +78,7 @@ struct ipa_bit_ggc_hash_traits : public
> ggc_cache_remove <ipa_bits *>
>      {
>        return a->value == b->value && a->mask == b->mask;
>      }
> +  static const bool empty_zero_p = true;
>    static void
>    mark_empty (ipa_bits *&p)
>      {
> @@ -123,6 +124,7 @@ struct ipa_vr_ggc_hash_traits : public
> ggc_cache_remove <value_range *>
>      {
>        return a->equal_p (*b);
>      }
> +  static const bool empty_zero_p = true;
>    static void
>    mark_empty (value_range *&p)
>      {
> diff --git a/gcc/profile.c b/gcc/profile.c
> index e124dc6173a..6a2de21c3bd 100644
> --- a/gcc/profile.c
> +++ b/gcc/profile.c
> @@ -932,6 +932,8 @@ struct location_triplet_hash : typed_noop_remove
> <location_triplet>
>      ref.lineno = -1;
>    }
>  
> +  static const bool empty_zero_p = false;
> +
>    static void
>    mark_empty (location_triplet &ref)
>    {
> diff --git a/gcc/sanopt.c b/gcc/sanopt.c
> index 8a29abe99e2..619aae45a15 100644
> --- a/gcc/sanopt.c
> +++ b/gcc/sanopt.c
> @@ -129,6 +129,8 @@ struct sanopt_tree_triplet_hash :
> typed_noop_remove <sanopt_tree_triplet>
>      ref.t1 = reinterpret_cast<tree> (1);
>    }
>  
> +  static const bool empty_zero_p = true;
> +
>    static void
>    mark_empty (sanopt_tree_triplet &ref)
>    {
> @@ -184,6 +186,8 @@ struct sanopt_tree_couple_hash :
> typed_noop_remove <sanopt_tree_couple>
>      ref.ptr = reinterpret_cast<tree> (1);
>    }
>  
> +  static const bool empty_zero_p = true;
> +
>    static void
>    mark_empty (sanopt_tree_couple &ref)
>    {
> diff --git a/gcc/tree-hasher.h b/gcc/tree-hasher.h
> index 787d1ad6a62..9194d6227a2 100644
> --- a/gcc/tree-hasher.h
> +++ b/gcc/tree-hasher.h
> @@ -40,6 +40,7 @@ struct int_tree_hasher
>      }
>    static void mark_deleted (value_type &v) { v.to =
> reinterpret_cast<tree> (0x1); }
>    static bool is_empty (const value_type &v) { return v.to == NULL;
> }
> +  static const bool empty_zero_p = true;
>    static void mark_empty (value_type &v) { v.to = NULL; }
>    static void remove (value_type &) {}
>  };
> diff --git a/gcc/tree-ssa-sccvn.c b/gcc/tree-ssa-sccvn.c
> index 6f6b19eb165..3b27c50ef75 100644
> --- a/gcc/tree-ssa-sccvn.c
> +++ b/gcc/tree-ssa-sccvn.c
> @@ -335,6 +335,7 @@ struct vn_ssa_aux_hasher : typed_noop_remove
> <vn_ssa_aux_t>
>    static inline hashval_t hash (const value_type &);
>    static inline bool equal (const value_type &, const compare_type
> &);
>    static inline void mark_deleted (value_type &) {}
> +  static const bool empty_zero_p = true;
>    static inline void mark_empty (value_type &e) { e = NULL; }
>    static inline bool is_deleted (value_type &) { return false; }
>    static inline bool is_empty (value_type &e) { return e == NULL; }
> diff --git a/gcc/tree-vect-slp.c b/gcc/tree-vect-slp.c
> index 9cb724b95ae..d164937b4b0 100644
> --- a/gcc/tree-vect-slp.c
> +++ b/gcc/tree-vect-slp.c
> @@ -1193,6 +1193,7 @@ struct bst_traits
>    static inline bool equal (value_type existing, value_type
> candidate);
>    static inline bool is_empty (value_type x) { return !x.exists ();
> }
>    static inline bool is_deleted (value_type x) { return !x.exists
> (); }
> +  static const bool empty_zero_p = true;
>    static inline void mark_empty (value_type &x) { x.release (); }
>    static inline void mark_deleted (value_type &x) { x.release (); }
>    static inline void remove (value_type &x) { x.release (); }
> diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h
> index 375fba28d20..ed7fcb0b825 100644
> --- a/gcc/tree-vectorizer.h
> +++ b/gcc/tree-vectorizer.h
> @@ -232,6 +232,8 @@ struct
> default_hash_traits<scalar_cond_masked_key>
>             && operand_equal_p (existing.op1, candidate.op1, 0));
>    }
>  
> +  static const bool empty_zero_p = true;
> +
>    static inline void
>    mark_empty (value_type &v)
>    {
> 
> 
>       Jakub

Reply via email to