From 1fae8805984229f7f24226852178b8fd6743008d Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 18 Feb 2021 18:05:37 -0500
Subject: [PATCH 16/23] FIXME: further hacking up deduplication: WIP on adding
 dedupe_set

---
 gcc/analyzer/diagnostic-manager.cc | 167 ++++++++++++++++++-----------
 gcc/analyzer/diagnostic-manager.h  | 116 ++++++++++++++++++--
 2 files changed, 208 insertions(+), 75 deletions(-)

diff --git a/gcc/analyzer/diagnostic-manager.cc b/gcc/analyzer/diagnostic-manager.cc
index ddda1c1a666..4955de388f2 100644
--- a/gcc/analyzer/diagnostic-manager.cc
+++ b/gcc/analyzer/diagnostic-manager.cc
@@ -69,23 +69,20 @@ namespace ana {
 /* saved_diagnostic's ctor.
    Take ownership of D and STMT_FINDER.  */
 
-saved_diagnostic::saved_diagnostic (const state_machine *sm,
+saved_diagnostic::saved_diagnostic (const dedupe_key *key,
 				    const exploded_node *enode,
-				    const supernode *snode, const gimple *stmt,
 				    stmt_finder *stmt_finder,
-				    tree var,
 				    const svalue *sval,
-				    state_machine::state_t state,
 				    pending_diagnostic *d)
-: m_sm (sm), m_enode (enode), m_snode (snode), m_stmt (stmt),
+: m_dedupe_key (key), m_enode (enode),
  /* stmt_finder could be on-stack; we want our own copy that can
     outlive that.  */
   m_stmt_finder (stmt_finder ? stmt_finder->clone () : NULL),
-  m_var (var), m_sval (sval), m_state (state),
+  m_sval (sval),
   m_d (d), m_trailing_eedge (NULL),
   m_status (STATUS_NEW), m_epath_length (0), m_problem (NULL)
 {
-  gcc_assert (m_stmt || m_stmt_finder);
+  gcc_assert (m_dedupe_key->m_stmt || m_stmt_finder);
 
   /* We must have an enode in order to be able to look for paths
      through the exploded_graph to this diagnostic.  */
@@ -101,6 +98,7 @@ saved_diagnostic::~saved_diagnostic ()
   delete m_problem;
 }
 
+#if 0
 bool
 saved_diagnostic::operator== (const saved_diagnostic &other) const
 {
@@ -114,6 +112,7 @@ saved_diagnostic::operator== (const saved_diagnostic &other) const
 	  && m_d->equal_p (*other.m_d)
 	  && m_trailing_eedge == other.m_trailing_eedge);
 }
+#endif
 
 /* Return a new json::object of the form
    {"sm": optional str,
@@ -129,6 +128,7 @@ saved_diagnostic::to_json () const
 {
   json::object *sd_obj = new json::object ();
 
+#if 0
   if (m_sm)
     sd_obj->set ("sm", new json::string (m_sm->get_name ()));
   sd_obj->set ("enode", new json::integer_number (m_enode->m_index));
@@ -139,7 +139,8 @@ saved_diagnostic::to_json () const
     sd_obj->set ("state", m_state->to_json ());
   sd_obj->set ("path_length", new json::integer_number (m_epath_length));
   sd_obj->set ("pending_diagnostic", new json::string (m_d->get_kind ()));
-
+#endif
+  // FIXME:
   /* We're not yet JSONifying the following fields:
      const gimple *m_stmt;
      stmt_finder *m_stmt_finder;
@@ -174,7 +175,7 @@ public:
 
   pending_diagnostic *get_pending_diagnostic () const
   {
-    return m_sd.m_d;
+    return m_sd.get_pending_diagnostic ();
   }
 
   bool reachable_from_p (const exploded_node *src_enode) const
@@ -189,7 +190,7 @@ public:
     return m_feasibility_problem;
   }
 
-  const state_machine *get_sm () const { return m_sd.m_sm; }
+  const state_machine *get_sm () const { return m_sd.get_sm (); }
 
 private:
   typedef reachability<eg_traits> enode_reachability;
@@ -207,6 +208,63 @@ private:
   const feasibility_problem *m_feasibility_problem;
 };
 
+/* struct dedupe_key.  */
+
+dedupe_key::dedupe_key (const state_machine *sm,
+			const supernode *snode,
+			const gimple *stmt,
+			tree var,
+			state_machine::state_t state,
+			const pending_diagnostic *d)
+: m_sm (sm), m_snode (snode), m_stmt (stmt),
+  m_var (var), m_state (state), m_d (d)
+{
+}
+
+hashval_t
+dedupe_key::hash () const
+{
+  inchash::hash hstate;
+  hstate.add_ptr (m_stmt);
+  // TODO: m_sd
+  return hstate.end ();
+}
+
+bool
+dedupe_key::operator== (const dedupe_key &other) const
+{
+  return (m_sm == other.m_sm
+	  && m_snode == other.m_snode
+	  && m_stmt == other.m_stmt
+	  && pending_diagnostic::same_tree_p (m_var, other.m_var)
+	  && m_state == other.m_state
+	  && m_d->equal_p (*other.m_d));
+  //&& m_trailing_eedge == other.m_trailing_eedge);
+}
+
+/* class dedupe_set.  */
+
+/* FIXME.  */
+
+dedupe_set::dedupe_set (const dedupe_key &key)
+: m_key (key)
+{
+}
+
+/* FIXME.  */
+
+void
+dedupe_set::add_diagnostic (const exploded_node *enode,
+			    stmt_finder *finder,
+			    const svalue *sval,
+			    pending_diagnostic *d)
+{
+  /* FIXME: we take owndership of D here, I think
+     The first one in the dedupe_set is also borrowed for the key.  */
+  saved_diagnostic *sd = new saved_diagnostic (&m_key, enode, finder, sval, d);
+  m_saved_diagnostics.safe_push (sd);
+}
+
 /* class diagnostic_manager.  */
 
 /* diagnostic_manager's ctor.  */
@@ -235,11 +293,10 @@ diagnostic_manager::add_diagnostic (const state_machine *sm,
      through the exploded_graph to the diagnostic.  */
   gcc_assert (enode);
 
-  gcc_unreachable ();
+  dedupe_set *set
+    = get_or_create_dedupe_set (sm, snode, stmt, var, state, d);
+  set->add_diagnostic (enode, finder, sval, d);
 #if 0
-  saved_diagnostic *sd
-    = new saved_diagnostic (sm, enode, snode, stmt, finder, var, sval,
-			    state, d);
   m_saved_diagnostics.safe_push (sd);
   if (get_logger ())
     log ("adding saved diagnostic %i at SN %i: %qs",
@@ -248,6 +305,25 @@ diagnostic_manager::add_diagnostic (const state_machine *sm,
 #endif
 }
 
+/* FIXME.  */
+
+dedupe_set *
+diagnostic_manager::get_or_create_dedupe_set (const state_machine *sm,
+					      const supernode *snode,
+					      const gimple *stmt,
+					      tree var, //const svalue *sval,
+					      state_machine::state_t state,
+					      const pending_diagnostic *d)
+{
+  const dedupe_key key (sm, snode, stmt, var, state, d);
+  if (dedupe_set **slot = m_dedupe_map.get (&key))
+    return *slot;
+
+  dedupe_set *set = new dedupe_set (key);
+  m_dedupe_map.put (set->get_key (), set);
+  return set;
+}
+
 /* Queue pending_diagnostic D at ENODE for later emission.  */
 
 void
@@ -282,6 +358,7 @@ diagnostic_manager::to_json () const
   return dm_obj;
 }
 
+#if 0
 /* A class for identifying sets of duplicated pending_diagnostic.
 
    We want to find the simplest dedupe_candidate amongst those that share a
@@ -348,7 +425,9 @@ public:
   const saved_diagnostic &m_sd;
   const gimple *m_stmt;
 };
+#endif
 
+#if 0
 /* The value of a slot for a dedupe_key within dedupe_winners:
    the exploded_path for the best candidate for that key, and the
    number of duplicates seen so far.  */
@@ -375,51 +454,9 @@ private:
 public:
   int m_num_dupes;
 };
+#endif
 
-/* Traits for use by dedupe_winners.  */
-
-class dedupe_hash_map_traits
-{
-public:
-  typedef const dedupe_key *key_type;
-  typedef dedupe_candidate *value_type;
-  typedef dedupe_candidate *compare_type;
-
-  static inline hashval_t hash (const key_type &v)
-  {
-    return v->hash ();
-  }
-  static inline bool equal_keys (const key_type &k1, const key_type &k2)
-  {
-    return *k1 == *k2;
-  }
-  template <typename T>
-  static inline void remove (T &)
-  {
-    // TODO
-  }
-  template <typename T>
-  static inline void mark_deleted (T &entry)
-  {
-    entry.m_key = reinterpret_cast<key_type> (1);
-  }
-  template <typename T>
-  static inline void mark_empty (T &entry)
-  {
-    entry.m_key = NULL;
-  }
-  template <typename T>
-  static inline bool is_deleted (const T &entry)
-  {
-    return entry.m_key == reinterpret_cast<key_type> (1);
-  }
-  template <typename T>
-  static inline bool is_empty (const T &entry)
-  {
-    return entry.m_key == NULL;
-  }
-  static const bool empty_zero_p = true;
-};
+#if 0
 
 /* FIXME: a way to debug the states within exploded_path::feasible_p.  */
 
@@ -641,6 +678,7 @@ private:
 		   dedupe_hash_map_traits> map_t;
   map_t m_map;
 };
+#endif
 
 /* Emit all saved diagnostics.  */
 
@@ -696,7 +734,8 @@ diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg,
 					   int num_dupes)
 {
   LOG_SCOPE (get_logger ());
-  log ("sd: %qs at SN: %i", sd.m_d->get_kind (), sd.m_snode->m_index);
+  log ("sd: %qs at SN: %i",
+       sd.get_pending_diagnostic ()->get_kind (), sd.get_snode ()->m_index);
   log ("num dupes: %i", num_dupes);
 
   pretty_printer *pp = global_dc->printer->clone ();
@@ -712,14 +751,14 @@ diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg,
   build_emission_path (pb, epath, &emission_path);
 
   /* Now prune it to just cover the most pertinent events.  */
-  prune_path (&emission_path, sd.m_sm, sd.m_sval, sd.m_state);
+  prune_path (&emission_path, sd.get_sm (), sd.m_sval, sd.get_state ());
 
   /* Add a final event to the path, covering the diagnostic itself.
      We use the final enode from the epath, which might be different from
      the sd.m_enode, as the dedupe code doesn't care about enodes, just
      snodes.  */
-  emission_path.add_final_event (sd.m_sm, epath.get_final_enode (), stmt,
-				 sd.m_var, sd.m_state);
+  emission_path.add_final_event (sd.get_sm (), epath.get_final_enode (), stmt,
+				 sd.get_var (), sd.get_state ());
 
   /* The "final" event might not be final; if the saved_diagnostic has a
      trailing eedge stashed, add any events for it.  This is for use
@@ -729,7 +768,7 @@ diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg,
 
   emission_path.prepare_for_emission (sd.m_d);
 
-  location_t loc = get_stmt_location (stmt, sd.m_snode->m_fun);
+  location_t loc = get_stmt_location (stmt, sd.get_snode ()->m_fun);
 
   /* Allow the pending_diagnostic to fix up the primary location
      and any locations for events.  */
@@ -740,7 +779,7 @@ diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg,
   rich_loc.set_path (&emission_path);
 
   auto_diagnostic_group d;
-  auto_cfun sentinel (sd.m_snode->m_fun);
+  auto_cfun sentinel (sd.get_snode ()->m_fun);
   if (sd.m_d->emit (&rich_loc))
     {
       if (flag_analyzer_show_duplicate_count && num_dupes > 0)
diff --git a/gcc/analyzer/diagnostic-manager.h b/gcc/analyzer/diagnostic-manager.h
index 8852fc95568..95a9b2d2ff4 100644
--- a/gcc/analyzer/diagnostic-manager.h
+++ b/gcc/analyzer/diagnostic-manager.h
@@ -23,6 +23,29 @@ along with GCC; see the file COPYING3.  If not see
 
 namespace ana {
 
+/* FIXME.  */
+
+struct dedupe_key
+{
+public:
+  dedupe_key (const state_machine *sm,
+	      const supernode *snode,
+	      const gimple *stmt,
+	      tree var,
+	      state_machine::state_t state,
+	      const pending_diagnostic *d);
+
+  hashval_t hash () const;
+  bool operator== (const dedupe_key &other) const;
+
+  const state_machine *m_sm;
+  const supernode *m_snode;
+  const gimple *m_stmt;
+  tree m_var;
+  state_machine::state_t m_state;
+  const pending_diagnostic *m_d;
+};
+
 /* A to-be-emitted diagnostic stored within diagnostic_manager.  */
 
 class saved_diagnostic
@@ -35,12 +58,10 @@ public:
    STATUS_FEASIBLE_PATH
   };
 
-  saved_diagnostic (const state_machine *sm,
+  saved_diagnostic (const dedupe_key *key,
 		    const exploded_node *enode,
-		    const supernode *snode, const gimple *stmt,
 		    stmt_finder *stmt_finder,
-		    tree var, const svalue *sval,
-		    state_machine::state_t state,
+		    const svalue *sval,
 		    pending_diagnostic *d);
   ~saved_diagnostic ();
 
@@ -48,6 +69,16 @@ public:
 
   json::object *to_json () const;
 
+  pending_diagnostic *get_pending_diagnostic () const
+  {
+    return m_d;
+  }
+
+  const state_machine *get_sm () const { return m_dedupe_key->m_sm; }
+  const supernode *get_snode () const { return m_dedupe_key->m_snode; }
+  tree get_var () const { return m_dedupe_key->m_var; }
+  state_machine::state_t get_state () const { return m_dedupe_key->m_state; }
+
   void set_feasible ()
   {
     gcc_assert (m_status == STATUS_NEW);
@@ -70,14 +101,10 @@ public:
   unsigned get_epath_length () const { return m_epath_length; }
 
   //private:
-  const state_machine *m_sm;
+  const dedupe_key *m_dedupe_key; // or simply pass it around as extrinsic state?
   const exploded_node *m_enode;
-  const supernode *m_snode;
-  const gimple *m_stmt;
   stmt_finder *m_stmt_finder;
-  tree m_var;
   const svalue *m_sval;
-  state_machine::state_t m_state;
   pending_diagnostic *m_d;
   exploded_edge *m_trailing_eedge;
 
@@ -90,14 +117,69 @@ private:
 };
 
 /* FIXME.
-   An equivalence of saved_diagnostics.  */
+   An equivalence class of saved_diagnostics.  */
 
 class dedupe_set
 {
 public:
+  dedupe_set (const dedupe_key &key);
+  void add_diagnostic (const exploded_node *enode,
+		       stmt_finder *finder,
+		       const svalue *sval,
+		       pending_diagnostic *d);
+
+  const dedupe_key *get_key () const { return &m_key; }
+
+private:
+  dedupe_key m_key;
   auto_delete_vec<saved_diagnostic> m_saved_diagnostics;
 };
 
+/* Traits for use by diagnostic_manager::m_dedupe_map.  */
+
+class dedupe_hash_map_traits
+{
+public:
+  typedef const dedupe_key *key_type;
+  typedef dedupe_set *value_type;
+  typedef dedupe_set *compare_type;
+
+  static inline hashval_t hash (const key_type &v)
+  {
+    return v->hash ();
+  }
+  static inline bool equal_keys (const key_type &k1, const key_type &k2)
+  {
+    return *k1 == *k2;
+  }
+  template <typename T>
+  static inline void remove (T &)
+  {
+    // TODO
+  }
+  template <typename T>
+  static inline void mark_deleted (T &entry)
+  {
+    entry.m_key = reinterpret_cast<key_type> (1);
+  }
+  template <typename T>
+  static inline void mark_empty (T &entry)
+  {
+    entry.m_key = NULL;
+  }
+  template <typename T>
+  static inline bool is_deleted (const T &entry)
+  {
+    return entry.m_key == reinterpret_cast<key_type> (1);
+  }
+  template <typename T>
+  static inline bool is_empty (const T &entry)
+  {
+    return entry.m_key == NULL;
+  }
+  static const bool empty_zero_p = true;
+};
+
 class path_builder;
 
 /* A class with responsibility for saving pending diagnostics, so that
@@ -156,6 +238,13 @@ public:
 #endif
 
 private:
+  dedupe_set *
+  get_or_create_dedupe_set (const state_machine *sm,
+			    const supernode *snode, const gimple *stmt,
+			    tree var, //const svalue *sval,
+			    state_machine::state_t state,
+			    const pending_diagnostic *d);
+
   void build_emission_path (const path_builder &pb,
 			    const exploded_path &epath,
 			    checker_path *emission_path) const;
@@ -190,7 +279,12 @@ private:
   void finish_pruning (checker_path *path) const;
 
   engine *m_eng;
-  auto_delete_vec<dedupe_set> m_dedupe_sets;
+
+  /* This maps from each dedupe_key to a dedupe_set.  */
+  typedef hash_map<const dedupe_key *, dedupe_set *,
+		   dedupe_hash_map_traits> map_t;
+  map_t m_dedupe_map;
+
   const int m_verbosity;
 };
 
-- 
2.26.2

