Enable sanitization for hash tables.
authorMartin Liska <mliska@suse.cz>
Fri, 7 Jun 2019 12:13:13 +0000 (14:13 +0200)
committerMartin Liska <marxin@gcc.gnu.org>
Fri, 7 Jun 2019 12:13:13 +0000 (12:13 +0000)
2019-06-07  Martin Liska  <mliska@suse.cz>

* cselib.c (cselib_init): Disable hash table
sanitization.
* hash-set.h: Pass new default argument to m_table.
* hash-table.c: Add global variable with hash table
sanitization limit.
* hash-table.h (Allocator>::hash_table): Add new argument
to ctor.
(hashtab_chk_error): New.
* params.def (PARAM_HASH_TABLE_VERIFICATION_LIMIT): New.
* toplev.c (process_options): Set hash_table_sanitize_eq_limit
from the PARAM_HASH_TABLE_VERIFICATION_LIMIT value.

From-SVN: r272038

gcc/ChangeLog
gcc/cselib.c
gcc/hash-set.h
gcc/hash-table.c
gcc/hash-table.h
gcc/params.def
gcc/toplev.c

index dbcf512b643bc7b51d93922c88e10b0231fbbc6c..a1cffe10a32d5612cba275c2c5e12128285f91a8 100644 (file)
@@ -1,3 +1,17 @@
+2019-06-07  Martin Liska  <mliska@suse.cz>
+
+       * cselib.c (cselib_init): Disable hash table
+       sanitization.
+       * hash-set.h: Pass new default argument to m_table.
+       * hash-table.c: Add global variable with hash table
+       sanitization limit.
+       * hash-table.h (Allocator>::hash_table): Add new argument
+       to ctor.
+       (hashtab_chk_error): New.
+       * params.def (PARAM_HASH_TABLE_VERIFICATION_LIMIT): New.
+       * toplev.c (process_options): Set hash_table_sanitize_eq_limit
+       from the PARAM_HASH_TABLE_VERIFICATION_LIMIT value.
+
 2019-06-07  Jan Hubicka  <hubicka@ucw.cz>
 
        * common.opt (flto-odr-type-merging): Ignore.
index 84c17c23f6d54de3e62be8d9478473bc13909cad..a1cbdec97181a88d248530489a500a717d455ea6 100644 (file)
@@ -2858,9 +2858,14 @@ cselib_init (int record_what)
     }
   used_regs = XNEWVEC (unsigned int, cselib_nregs);
   n_used_regs = 0;
-  cselib_hash_table = new hash_table<cselib_hasher> (31);
+  /* FIXME: enable sanitization (PR87845) */
+  cselib_hash_table
+    = new hash_table<cselib_hasher> (31, /* ggc */ false,
+                                    /* sanitize_eq_and_hash */ false);
   if (cselib_preserve_constants)
-    cselib_preserved_hash_table = new hash_table<cselib_hasher> (31);
+    cselib_preserved_hash_table
+      = new hash_table<cselib_hasher> (31, /* ggc */ false,
+                                      /* sanitize_eq_and_hash */ false);
   next_uid = 1;
 }
 
index de3532f5f6806cf4fab1a11564724c42d789a056..d891ed782975d3de00340e8d1c70047b344ddb58 100644 (file)
@@ -28,7 +28,7 @@ class hash_set
 public:
   typedef typename Traits::value_type Key;
   explicit hash_set (size_t n = 13, bool ggc = false CXX_MEM_STAT_INFO)
-    : m_table (n, ggc, GATHER_STATISTICS, HASH_SET_ORIGIN PASS_MEM_STAT) {}
+    : m_table (n, ggc, true, GATHER_STATISTICS, HASH_SET_ORIGIN PASS_MEM_STAT) {}
 
   /* Create a hash_set in gc memory with space for at least n elements.  */
 
index 646a7a1c49754bb30b5e7c42e9c7ce88ee38580e..8e86fffa36fe7f21dda1f87145042bb513dbb12c 100644 (file)
@@ -74,6 +74,9 @@ struct prime_ent const prime_tab[] = {
   { 0xfffffffb, 0x00000006, 0x00000008, 31 }
 };
 
+/* Limit number of comparisons when calling hash_table<>::verify.  */
+unsigned int hash_table_sanitize_eq_limit;
+
 /* The following function returns an index into the above table of the
    nearest prime number which is greater than N, and near a power of two. */
 
index 4178616478e2055da39367b45ba42c62fed316f9..686a13dbd4b212063be1262c1e02ceb49034471d 100644 (file)
@@ -295,6 +295,8 @@ struct prime_ent
 
 extern struct prime_ent const prime_tab[];
 
+/* Limit number of comparisons when calling hash_table<>::verify.  */
+extern unsigned int hash_table_sanitize_eq_limit;
 
 /* Functions for computing hash table indexes.  */
 
@@ -371,10 +373,12 @@ class hash_table
 
 public:
   explicit hash_table (size_t, bool ggc = false,
+                      bool sanitize_eq_and_hash = true,
                       bool gather_mem_stats = GATHER_STATISTICS,
                       mem_alloc_origin origin = HASH_TABLE_ORIGIN
                       CXX_MEM_STAT_INFO);
   explicit hash_table (const hash_table &, bool ggc = false,
+                      bool sanitize_eq_and_hash = true,
                       bool gather_mem_stats = GATHER_STATISTICS,
                       mem_alloc_origin origin = HASH_TABLE_ORIGIN
                       CXX_MEM_STAT_INFO);
@@ -516,6 +520,7 @@ private:
 
   value_type *alloc_entries (size_t n CXX_MEM_STAT_INFO) const;
   value_type *find_empty_slot_for_expand (hashval_t);
+  void verify (const compare_type &comparable, hashval_t hash);
   bool too_empty_p (unsigned int);
   void expand ();
   static bool is_deleted (value_type &v)
@@ -564,6 +569,9 @@ private:
   /* if m_entries is stored in ggc memory.  */
   bool m_ggc;
 
+  /* True if the table should be sanitized for equal and hash functions.  */
+  bool m_sanitize_eq_and_hash;
+
   /* If we should gather memory statistics for the table.  */
 #if GATHER_STATISTICS
   bool m_gather_mem_stats;
@@ -586,12 +594,13 @@ extern void dump_hash_table_loc_statistics (void);
 template<typename Descriptor, bool Lazy,
         template<typename Type> class Allocator>
 hash_table<Descriptor, Lazy, Allocator>::hash_table (size_t size, bool ggc,
+                                                    bool sanitize_eq_and_hash,
                                                     bool gather_mem_stats
                                                     ATTRIBUTE_UNUSED,
                                                     mem_alloc_origin origin
                                                     MEM_STAT_DECL) :
   m_n_elements (0), m_n_deleted (0), m_searches (0), m_collisions (0),
-  m_ggc (ggc)
+  m_ggc (ggc), m_sanitize_eq_and_hash (sanitize_eq_and_hash)
 #if GATHER_STATISTICS
   , m_gather_mem_stats (gather_mem_stats)
 #endif
@@ -617,12 +626,14 @@ template<typename Descriptor, bool Lazy,
         template<typename Type> class Allocator>
 hash_table<Descriptor, Lazy, Allocator>::hash_table (const hash_table &h,
                                                     bool ggc,
+                                                    bool sanitize_eq_and_hash,
                                                     bool gather_mem_stats
                                                     ATTRIBUTE_UNUSED,
                                                     mem_alloc_origin origin
                                                     MEM_STAT_DECL) :
   m_n_elements (h.m_n_elements), m_n_deleted (h.m_n_deleted),
-  m_searches (0), m_collisions (0), m_ggc (ggc)
+  m_searches (0), m_collisions (0), m_ggc (ggc),
+  m_sanitize_eq_and_hash (sanitize_eq_and_hash)
 #if GATHER_STATISTICS
   , m_gather_mem_stats (gather_mem_stats)
 #endif
@@ -912,7 +923,13 @@ hash_table<Descriptor, Lazy, Allocator>
       entry = &m_entries[index];
       if (is_empty (*entry)
           || (!is_deleted (*entry) && Descriptor::equal (*entry, comparable)))
-        return *entry;
+       {
+#if CHECKING_P
+         if (m_sanitize_eq_and_hash)
+           verify (comparable, hash);
+#endif
+         return *entry;
+       }
     }
 }
 
@@ -941,8 +958,12 @@ hash_table<Descriptor, Lazy, Allocator>
   if (insert == INSERT && m_size * 3 <= m_n_elements * 4)
     expand ();
 
-  m_searches++;
+#if CHECKING_P
+  if (m_sanitize_eq_and_hash)
+    verify (comparable, hash);
+#endif
 
+  m_searches++;
   value_type *first_deleted_slot = NULL;
   hashval_t index = hash_table_mod1 (hash, m_size_prime_index);
   hashval_t hash2 = hash_table_mod2 (hash, m_size_prime_index);
@@ -989,6 +1010,37 @@ hash_table<Descriptor, Lazy, Allocator>
   return &m_entries[index];
 }
 
+/* Report a hash table checking error.  */
+
+ATTRIBUTE_NORETURN ATTRIBUTE_COLD
+static void
+hashtab_chk_error ()
+{
+  fprintf (stderr, "hash table checking failed: "
+          "equal operator returns true for a pair "
+          "of values with a different hash value\n");
+  gcc_unreachable ();
+}
+
+/* Verify that all existing elements in th hash table which are
+   equal to COMPARABLE have an equal HASH value provided as argument.  */
+
+template<typename Descriptor, bool Lazy,
+        template<typename Type> class Allocator>
+void
+hash_table<Descriptor, Lazy, Allocator>
+::verify (const compare_type &comparable, hashval_t hash)
+{
+  for (size_t i = 0; i < MIN (hash_table_sanitize_eq_limit, m_size); i++)
+    {
+      value_type *entry = &m_entries[i];
+      if (!is_empty (*entry) && !is_deleted (*entry)
+         && hash != Descriptor::hash (*entry)
+         && Descriptor::equal (*entry, comparable))
+       hashtab_chk_error ();
+    }
+}
+
 /* This function deletes an element with the given COMPARABLE value
    from hash table starting with the given HASH.  If there is no
    matching element in the hash table, this function does nothing. */
index b4a4e4a4190b4876b5a6952ed059a8d05f9db195..0db6095141396cefc7cde4c979b5cf842dc5596e 100644 (file)
@@ -1431,6 +1431,12 @@ DEFPARAM(PARAM_GIMPLE_FE_COMPUTED_HOT_BB_THRESHOLD,
         " The parameter is used only in GIMPLE FE.",
         0, 0, 0)
 
+DEFPARAM(PARAM_HASH_TABLE_VERIFICATION_LIMIT,
+        "hash-table-verification-limit",
+        "The number of elements for which hash table verification is done for "
+        "each searched element.",
+        100, 0, 0)
+
 /*
 
 Local variables:
index d300ac2ec894ee947156616e71796d55d9d04307..116be7be395545d2233d355d0ab60149f2b0afe7 100644 (file)
@@ -1799,6 +1799,10 @@ process_options (void)
   optimization_default_node = build_optimization_node (&global_options);
   optimization_current_node = optimization_default_node;
 
+  if (flag_checking >= 2)
+    hash_table_sanitize_eq_limit
+      = PARAM_VALUE (PARAM_HASH_TABLE_VERIFICATION_LIMIT);
+
   /* Please don't change global_options after this point, those changes won't
      be reflected in optimization_{default,current}_node.  */
 }