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