From f3d142ea695ba8aa4230bd1e937639bff95965d8 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Wed, 10 Jun 2020 14:56:40 -0400
Subject: [PATCH 225/377] FIXME: WIP on widening

---
 gcc/analyzer/engine.cc        | 11 ++---
 gcc/analyzer/program-point.cc | 48 ++++++++++++++++++
 gcc/analyzer/program-point.h  | 46 ++++++-----------
 gcc/analyzer/program-state.cc | 14 ++++--
 gcc/analyzer/program-state.h  |  2 +
 gcc/analyzer/region-model2.cc | 59 ++++++++++++++--------
 gcc/analyzer/region-model2.h  | 93 ++++++++++++++++++++++++++++++++---
 gcc/analyzer/store2.cc        | 12 +++--
 gcc/analyzer/store2.h         |  6 ++-
 9 files changed, 218 insertions(+), 73 deletions(-)

diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 377052cc8db..0dec15f933c 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -2595,9 +2595,7 @@ exploded_graph::exploded_graph (const supergraph &sg, logger *logger,
   m_functionless_stats (m_sg.num_nodes ()),
   m_PK_AFTER_SUPERNODE_per_snode (m_sg.num_nodes ())
 {
-  m_origin = get_or_create_node (program_point (function_point (NULL, NULL,
-								0, PK_ORIGIN),
-						call_string ()),
+  m_origin = get_or_create_node (program_point::origin (),
 				 program_state (ext_state), NULL, NULL);
   for (int i = 0; i < m_sg.num_nodes (); i++)
     m_PK_AFTER_SUPERNODE_per_snode.quick_push (i);
@@ -2750,6 +2748,7 @@ exploded_graph::get_or_create_node (const program_point &point,
 
 	  program_state merged_state (m_ext_state);
 	  if (pruned_state.can_merge_with_p (existing_state, m_ext_state,
+					     point,
 					     &merged_state))
 	    {
 	      if (logger)
@@ -3039,6 +3038,7 @@ exploded_graph::process_worklist ()
 
 	    if (node->get_point () == node_2->get_point ())
 	      {
+		const program_point &point = node->get_point ();
 		if (logger)
 		  {
 		    format f (false);
@@ -3047,10 +3047,9 @@ exploded_graph::process_worklist ()
 		    logger->log_partial
 		      ("got potential merge EN: %i and EN: %i at ",
 		       node->m_index, node_2->m_index);
-		    node->get_point ().print (pp, f);
+		    point.print (pp, f);
 		    logger->end_log_line ();
 		  }
-
 		const program_state &state = node->get_state ();
 		const program_state &state_2 = node_2->get_state ();
 
@@ -3061,7 +3060,7 @@ exploded_graph::process_worklist ()
 		program_state merged_state (m_ext_state);
 		state_change change;
 		if (state.can_merge_with_p (state_2, m_ext_state,
-					    &merged_state))
+					    point, &merged_state))
 		  {
 		    if (logger)
 		      logger->log ("merging EN: %i and EN: %i",
diff --git a/gcc/analyzer/program-point.cc b/gcc/analyzer/program-point.cc
index ee07a141cc0..a07f236fd71 100644
--- a/gcc/analyzer/program-point.cc
+++ b/gcc/analyzer/program-point.cc
@@ -84,6 +84,22 @@ point_kind_to_string (enum point_kind pk)
 
 /* class function_point.  */
 
+function_point::function_point (const supernode *supernode,
+				const superedge *from_edge,
+				unsigned stmt_idx,
+				enum point_kind kind)
+: m_supernode (supernode), m_from_edge (from_edge),
+  m_stmt_idx (stmt_idx), m_kind (kind)
+{
+  if (from_edge)
+    {
+      gcc_checking_assert (m_kind == PK_BEFORE_SUPERNODE);
+      gcc_checking_assert (from_edge->get_kind () == SUPEREDGE_CFG_EDGE);
+    }
+  if (stmt_idx)
+    gcc_checking_assert (m_kind == PK_BEFORE_STMT);
+}
+
 /* Print this function_point to PP.  */
 
 void
@@ -160,6 +176,17 @@ function_point::hash () const
   return hstate.end ();
 }
 
+/* FIXME.  */
+
+function *
+function_point::get_function () const
+{
+  if (m_supernode)
+    return m_supernode->m_fun;
+  else
+    return NULL;
+}
+
 /* Get the gimple stmt for this function_point, if any.  */
 
 const gimple *
@@ -185,6 +212,27 @@ function_point::get_location () const
   return UNKNOWN_LOCATION;
 }
 
+/* FIXME.  */
+
+function_point
+function_point::from_function_entry (const supergraph &sg,
+				     function *fun)
+{
+  return before_supernode (sg.get_node_for_function_entry (fun),
+			   NULL);
+}
+
+/* FIXME.  */
+
+function_point
+function_point::before_supernode (const supernode *supernode,
+				  const superedge *from_edge)
+{
+  if (from_edge && from_edge->get_kind () != SUPEREDGE_CFG_EDGE)
+    from_edge = NULL;
+  return function_point (supernode, from_edge, 0, PK_BEFORE_SUPERNODE);
+}
+
 /* A subclass of diagnostic_context for use by
    program_point::print_source_line.  */
 
diff --git a/gcc/analyzer/program-point.h b/gcc/analyzer/program-point.h
index c110d5003c5..8a9fd019260 100644
--- a/gcc/analyzer/program-point.h
+++ b/gcc/analyzer/program-point.h
@@ -21,6 +21,12 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_ANALYZER_PROGRAM_POINT_H
 #define GCC_ANALYZER_PROGRAM_POINT_H
 
+#if 0
+#include "digraph.h" // FIXME
+#include "analyzer/supergraph.h" // FIXME
+#endif
+#include "analyzer/call-string.h" // FIXME
+
 namespace ana {
 
 class exploded_graph;
@@ -72,18 +78,7 @@ public:
   function_point (const supernode *supernode,
 		  const superedge *from_edge,
 		  unsigned stmt_idx,
-		  enum point_kind kind)
-  : m_supernode (supernode), m_from_edge (from_edge),
-    m_stmt_idx (stmt_idx), m_kind (kind)
-  {
-    if (from_edge)
-      {
-	gcc_checking_assert (m_kind == PK_BEFORE_SUPERNODE);
-	gcc_checking_assert (from_edge->get_kind () == SUPEREDGE_CFG_EDGE);
-      }
-    if (stmt_idx)
-      gcc_checking_assert (m_kind == PK_BEFORE_STMT);
-  }
+		  enum point_kind kind);
 
   void print (pretty_printer *pp, const format &f) const;
   void print_source_line (pretty_printer *pp) const;
@@ -102,13 +97,7 @@ public:
   /* Accessors.  */
 
   const supernode *get_supernode () const { return m_supernode; }
-  function *get_function () const
-  {
-    if (m_supernode)
-      return m_supernode->m_fun;
-    else
-      return NULL;
-  }
+  function *get_function () const;
   const gimple *get_stmt () const;
   location_t get_location () const;
   enum point_kind get_kind () const { return m_kind; }
@@ -125,19 +114,10 @@ public:
   /* Factory functions for making various kinds of program_point.  */
 
   static function_point from_function_entry (const supergraph &sg,
-					    function *fun)
-  {
-    return before_supernode (sg.get_node_for_function_entry (fun),
-			     NULL);
-  }
+					     function *fun);
 
   static function_point before_supernode (const supernode *supernode,
-					  const superedge *from_edge)
-  {
-    if (from_edge && from_edge->get_kind () != SUPEREDGE_CFG_EDGE)
-      from_edge = NULL;
-    return function_point (supernode, from_edge, 0, PK_BEFORE_SUPERNODE);
-  }
+					  const superedge *from_edge);
 
   static function_point before_stmt (const supernode *supernode,
 				     unsigned stmt_idx)
@@ -259,6 +239,12 @@ public:
   }
 
   /* Factory functions for making various kinds of program_point.  */
+  static program_point origin ()
+  {
+    return program_point (function_point (NULL, NULL,
+					  0, PK_ORIGIN),
+			  call_string ());
+  }
 
   static program_point from_function_entry (const supergraph &sg,
 					    function *fun)
diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc
index ebe4452e475..2fead3e446d 100644
--- a/gcc/analyzer/program-state.cc
+++ b/gcc/analyzer/program-state.cc
@@ -1800,16 +1800,18 @@ program_state::get_representative_tree2 (const svalue2 *sval) const
 /* Attempt to merge this state with OTHER, both using EXT_STATE.
    Write the result to *OUT.
    If the states were merged successfully, return true.  */
+// FIXME: POINT
 
 bool
 program_state::can_merge_with_p (const program_state &other,
 				 const extrinsic_state &ext_state,
+				 const program_point &point,
 				 program_state *out) const
 {
   gcc_assert (out);
 
   if (m_region_model2)
-    return can_merge_with2_p (other, ext_state, out);
+    return can_merge_with2_p (other, ext_state, point, out);
 
   /* TODO:  initially I had an early reject here if there
      are sm-differences between the states.  However, this was
@@ -1884,10 +1886,12 @@ program_state::can_merge_with_p (const program_state &other,
 /* Attempt to merge this state with OTHER, both using EXT_STATE.
    Write the result to *OUT.
    If the states were merged successfully, return true.  */
+// FIXME: POINT
 
 bool
 program_state::can_merge_with2_p (const program_state &other,
 				  const extrinsic_state &ext_state,
+				  const program_point &point,
 				  program_state *out) const
 {
   gcc_assert (out);
@@ -1902,6 +1906,7 @@ program_state::can_merge_with2_p (const program_state &other,
 
   /* Attempt to merge the region_models.  */
   if (!m_region_model2->can_merge_with_p (*other.m_region_model2,
+					  point,
 					  out->m_region_model2))
     return false;
 
@@ -2461,6 +2466,8 @@ test_program_state_dumping_2 ()
 static void
 test_program_state_merging ()
 {
+  program_point point (program_point::origin ());
+
   /* Create a program_state for a global ptr "p" that has
      malloc sm-state, pointing to a region on the heap.  */
   tree p = build_global_decl ("p", ptr_type_node);
@@ -2496,7 +2503,7 @@ test_program_state_merging ()
      with the given sm-state.
      They ought to be mergeable, preserving the sm-state.  */
   program_state merged (ext_state);
-  ASSERT_TRUE (s0.can_merge_with_p (s1, ext_state, &merged));
+  ASSERT_TRUE (s0.can_merge_with_p (s1, ext_state, point, &merged));
   merged.validate (ext_state);
 
   /* Verify that the merged state has the sm-state for "p".  */
@@ -2524,6 +2531,7 @@ test_program_state_merging ()
 static void
 test_program_state_merging_2 ()
 {
+  program_point point (program_point::origin ());
   auto_delete_vec <state_machine> checkers;
   checkers.safe_push (make_signal_state_machine (NULL));
   extrinsic_state ext_state (checkers);
@@ -2548,7 +2556,7 @@ test_program_state_merging_2 ()
 
   /* They ought to not be mergeable.  */
   program_state merged (ext_state);
-  ASSERT_FALSE (s0.can_merge_with_p (s1, ext_state, &merged));
+  ASSERT_FALSE (s0.can_merge_with_p (s1, ext_state, point, &merged));
 }
 
 /* Tests for sm_state_map2.  */
diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h
index 22072ac3c98..4586edbc618 100644
--- a/gcc/analyzer/program-state.h
+++ b/gcc/analyzer/program-state.h
@@ -404,9 +404,11 @@ public:
 
   bool can_merge_with_p (const program_state &other,
 			 const extrinsic_state &ext_state,
+			 const program_point &point,
 			 program_state *out) const;
   bool can_merge_with2_p (const program_state &other,
 			  const extrinsic_state &ext_state,
+			  const program_point &point,
 			  program_state *out) const;
 
   void validate (const extrinsic_state &ext_state) const;
diff --git a/gcc/analyzer/region-model2.cc b/gcc/analyzer/region-model2.cc
index ac6932ff378..75189c7be2e 100644
--- a/gcc/analyzer/region-model2.cc
+++ b/gcc/analyzer/region-model2.cc
@@ -240,7 +240,8 @@ svalue2::unwrap_any_unmergeable () const
 
 const svalue2 *
 svalue2::can_merge_p (const svalue2 *other,
-		      region_model2_manager *mgr) const
+		      region_model2_manager *mgr,
+		      const program_point &point) const
 {
   if (!(get_type () && other->get_type ()))
     return NULL;
@@ -254,7 +255,7 @@ svalue2::can_merge_p (const svalue2 *other,
       || (other->get_kind () == svalue2::SK_UNMERGEABLE))
     return NULL;
 
-#if 0
+#if WIDENING
   /* Widening.  */
   /* Merge: (new_cst, existing_cst) -> widen (existing, new).  */
   if (tree cst0 = maybe_get_constant ())
@@ -263,7 +264,7 @@ svalue2::can_merge_p (const svalue2 *other,
 	/* FIXME: we also need to distinguish these via the stmt (or
 	   program_point?) so we can handle nested loops.  */
 	return mgr->get_or_create_widening_svalue2 (other->get_type (),
-						    other, this);
+						    point, other, this);
       }
   /* TODO: also detect widening symbolically via binary ops
      e.g. for (i = start; i < end; i+= step).  */
@@ -784,6 +785,8 @@ widening_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
   if (simple)
     {
       pp_string (pp, "WIDENING(");
+      m_point.print (pp, format (false));
+      pp_string (pp, ", ");
       m_base_sval->dump_to_pp (pp, simple);
       pp_string (pp, ", ");
       m_iter_sval->dump_to_pp (pp, simple);
@@ -794,6 +797,8 @@ widening_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
       pp_string (pp, "widening_svalue2 (");
       // TODO: type?
       pp_string (pp, ", ");
+      m_point.print (pp, format (false));
+      pp_string (pp, ", ");
       m_base_sval->dump_to_pp (pp, simple);
       pp_string (pp, ", ");
       m_iter_sval->dump_to_pp (pp, simple);
@@ -3418,14 +3423,15 @@ region_model2_manager::get_or_create_unmergeable (const svalue2 *arg)
 
 const svalue2 *
 region_model2_manager::get_or_create_widening_svalue2 (tree type,
+						       const program_point &point,
 						       const svalue2 *base_sval,
 						       const svalue2 *iter_sval)
 {
-  widening_svalue2::key_t key (type, base_sval, iter_sval);
+  widening_svalue2::key_t key (type, point, base_sval, iter_sval);
   if (widening_svalue2 **slot = m_widening_values_map.get (key))
     return *slot;
   widening_svalue2 *widening_sval
-    = new widening_svalue2 (type, base_sval, iter_sval);
+    = new widening_svalue2 (type, point, base_sval, iter_sval);
   m_widening_values_map.put (key, widening_sval);
   return widening_sval;
 }
@@ -5408,6 +5414,9 @@ region_model2::handle_phi (const gphi *phi,
 
   const svalue2 *rhs_sval = get_rvalue (rhs, ctxt);
 
+  /* FIXME: WIP on replacing this with widening_svalue2, which ought
+     to be a more general solution.  */
+#if !WIDENING
   if (is_back_edge && rhs_sval->get_kind () != svalue2::SK_UNKNOWN)
     {
       //FIXME:
@@ -5422,6 +5431,7 @@ region_model2::handle_phi (const gphi *phi,
       mark_region_as_unknown (get_lvalue (lhs, ctxt));
     }
   else
+#endif
     set_value (get_lvalue (lhs, ctxt), rhs_sval, ctxt);
 
   if (ctxt)
@@ -7505,11 +7515,14 @@ region_model2::poison_any_pointers_to_bad_region2s (const region2_id_set &
       }
 }
 #endif
+
 /* Attempt to merge THIS with OTHER_MODEL, writing the result
-   to OUT_MODEL.  */
+   to OUT_MODEL.
+   FIXME: POINT  */
 
 bool
 region_model2::can_merge_with_p (const region_model2 &other_model,
+				 const program_point &point,
 				 region_model2 *out_model) const
 {
   gcc_assert (out_model);
@@ -7521,7 +7534,8 @@ region_model2::can_merge_with_p (const region_model2 &other_model,
   out_model->m_current_frame = m_current_frame;
 
   if (!store2::can_merge_p (&m_store, &other_model.m_store,
-			    &out_model->m_store, m_mgr->get_store2_manager ()))
+			    &out_model->m_store, m_mgr->get_store2_manager (),
+			    point))
     return false;
 
   /* Merge constraints.  */
@@ -9434,6 +9448,7 @@ assert_region_model2s_merge (tree expr, tree val_a, tree val_b,
 			     region_model2 *out_merged_model,
 			     const svalue2 **out_merged_svalue2)
 {
+  program_point point (program_point::origin ());
   test_region_model2_context ctxt;
   region_model2_manager *mgr = out_merged_model->get_manager ();
   region_model2 model0 (mgr);
@@ -9448,7 +9463,7 @@ assert_region_model2s_merge (tree expr, tree val_a, tree val_b,
 		      &ctxt);
 
   /* They should be mergeable.  */
-  ASSERT_TRUE (model0.can_merge_with_p (model1, out_merged_model));
+  ASSERT_TRUE (model0.can_merge_with_p (model1, point, out_merged_model));
   *out_merged_svalue2 = out_merged_model->get_rvalue (expr, &ctxt);
 }
 
@@ -9482,6 +9497,7 @@ test_state_merging ()
 		       get_identifier ("q"),
 		       ptr_type_node);
 
+  program_point point (program_point::origin ());
   region_model2_manager mgr;
 
   {
@@ -9489,7 +9505,7 @@ test_state_merging ()
     region_model2 model1 (&mgr);
     region_model2 merged (&mgr);
     /* Verify empty models can be merged.  */
-    ASSERT_TRUE (model0.can_merge_with_p (model1, &merged));
+    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &merged));
     ASSERT_EQ (model0, merged);
   }
 
@@ -9504,7 +9520,7 @@ test_state_merging ()
     test_region_model2_context ctxt;
     model0.add_constraint (x, EQ_EXPR, int_42, &ctxt);
     model1.add_constraint (x, EQ_EXPR, int_113, &ctxt);
-    ASSERT_TRUE (model0.can_merge_with_p (model1, &merged));
+    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &merged));
     ASSERT_NE (model0, merged);
     ASSERT_NE (model1, merged);
   }
@@ -9536,7 +9552,7 @@ test_state_merging ()
 
     /* They should be mergeable, and the result should be the same.  */
     region_model2 merged (&mgr);
-    ASSERT_TRUE (model0.can_merge_with_p (model1, &merged));
+    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &merged));
     ASSERT_EQ (model0, merged);
     /* In particular, "a" should have the placeholder value.  */
     ASSERT_EQ (merged.get_rvalue (a, &ctxt), &test_sval);
@@ -9562,7 +9578,7 @@ test_state_merging ()
 
     /* They should be mergeable, and the result should be the same.  */
     region_model2 merged (&mgr);
-    ASSERT_TRUE (model0.can_merge_with_p (model1, &merged));
+    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &merged));
     ASSERT_EQ (model0, merged);
     /* In particular, "x" should have the placeholder value.  */
     ASSERT_EQ (merged.get_rvalue (x, &ctxt), &test_sval);
@@ -9639,7 +9655,7 @@ test_state_merging ()
 
     /* They should be mergeable, and the result should be the same.  */
     region_model2 merged (&mgr);
-    ASSERT_TRUE (model0.can_merge_with_p (model1, &merged));
+    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &merged));
     ASSERT_EQ (model0, merged);
   }
 
@@ -9689,7 +9705,7 @@ test_state_merging ()
     ASSERT_EQ (model0, model1);
 
     region_model2 merged (&mgr);
-    ASSERT_TRUE (model0.can_merge_with_p (model1, &merged));
+    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &merged));
 
     /* The merged model ought to be identical.  */
     ASSERT_EQ (model0, merged);
@@ -9710,7 +9726,7 @@ test_state_merging ()
 
     /* They should be mergeable, and the result should be the same.  */
     region_model2 merged (&mgr);
-    ASSERT_TRUE (model0.can_merge_with_p (model1, &merged));
+    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &merged));
     ASSERT_EQ (model0, merged);
 
     /* In particular, we should have x == y.  */
@@ -9726,7 +9742,7 @@ test_state_merging ()
     model0.add_constraint (x, EQ_EXPR, int_42, &ctxt);
     model1.add_constraint (x, NE_EXPR, int_42, &ctxt);
     region_model2 merged (&mgr);
-    ASSERT_TRUE (model0.can_merge_with_p (model1, &merged));
+    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &merged));
   }
 
   {
@@ -9737,7 +9753,7 @@ test_state_merging ()
     model1.add_constraint (x, NE_EXPR, int_42, &ctxt);
     model1.add_constraint (x, EQ_EXPR, int_113, &ctxt);
     region_model2 merged (&mgr);
-    ASSERT_TRUE (model0.can_merge_with_p (model1, &merged));
+    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &merged));
   }
 
   // TODO: what can't we merge? need at least one such test
@@ -9765,7 +9781,7 @@ test_state_merging ()
 
     /* They should be mergeable, and the result should be the same.  */
     region_model2 merged (&mgr);
-    ASSERT_TRUE (model0.can_merge_with_p (model1, &merged));
+    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &merged));
 #endif
   }
 
@@ -9799,7 +9815,7 @@ test_state_merging ()
     /* They should be mergeable, and the result should be the same
        (after canonicalization, at least).  */
     region_model2 merged (&mgr);
-    ASSERT_TRUE (model0.can_merge_with_p (model1, &merged));
+    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &merged));
     merged.canonicalize (NULL);
     ASSERT_EQ (model0, merged);
 #endif
@@ -9818,7 +9834,7 @@ test_state_merging ()
     /* They should be mergeable, and the result should be the same
        (after canonicalization, at least).  */
     region_model2 merged (&mgr);
-    ASSERT_TRUE (model0.can_merge_with_p (model1, &merged));
+    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &merged));
     ASSERT_EQ (model0, merged);
   }
 }
@@ -9853,8 +9869,9 @@ test_constraint_merging ()
 
   /* They should be mergeable; the merged constraints should
      be: (0 <= x < n).  */
+  program_point point (program_point::origin ());
   region_model2 merged (&mgr);
-  ASSERT_TRUE (model0.can_merge_with_p (model1, &merged));
+  ASSERT_TRUE (model0.can_merge_with_p (model1, point, &merged));
 
   ASSERT_EQ (merged.eval_condition (x, GE_EXPR, int_0, &ctxt),
 	     tristate (tristate::TS_TRUE));
diff --git a/gcc/analyzer/region-model2.h b/gcc/analyzer/region-model2.h
index 8640b4d0e33..43ffe6e78b0 100644
--- a/gcc/analyzer/region-model2.h
+++ b/gcc/analyzer/region-model2.h
@@ -23,6 +23,9 @@ along with GCC; see the file COPYING3.  If not see
 
 #include "analyzer/uniq-manager.h" // FIXME
 #include "analyzer/store2.h" // FIXME
+#include "analyzer/program-point.h" // FIXME
+
+#define WIDENING 0
 
 /* Implementation of the region-based ternary model described in:
      "A Memory Model for Static Analysis of C Programs"
@@ -209,7 +212,8 @@ public:
   const svalue2 *unwrap_any_unmergeable () const;
 
   const svalue2 *can_merge_p (const svalue2 *other,
-			      region_model2_manager *mgr) const;
+			      region_model2_manager *mgr,
+			      const program_point &point) const;
 
  protected:
   svalue2 (tree type) : m_type (type) {}
@@ -896,8 +900,63 @@ public:
   /* A support class for uniquifying instances of widening_svalue2.  */
   struct key_t
   {
-    key_t (tree type, const svalue2 *base_sval, const svalue2 *iter_sval)
-    : m_type (type), m_base_sval (base_sval), m_iter_sval (iter_sval)
+#if 0
+    struct hash_map_traits
+    {
+      typedef key_t key_type;
+      typedef const widening_svalue2 *value_type;
+      typedef const widening_svalue2 *compare_type;
+
+      static inline hashval_t hash (const key_type &k)
+      {
+	gcc_assert (k != NULL);
+	gcc_assert (k != reinterpret_cast<key_type> (1));
+	return k->hash ();
+      }
+      static inline bool equal_keys (const key_type &k1, const key_type &k2)
+      {
+	gcc_assert (k1 != NULL);
+	gcc_assert (k2 != NULL);
+	gcc_assert (k1 != reinterpret_cast<key_type> (1));
+	gcc_assert (k2 != reinterpret_cast<key_type> (1));
+	if (k1 && k2)
+	  return *k1 == *k2;
+	else
+	  /* Otherwise they must both be non-NULL.  */
+	  return k1 == k2;
+      }
+      template <typename T>
+      static inline void remove (T &)
+      {
+	/* empty; the nodes are handled elsewhere.  */
+      }
+      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 = false;
+    };
+#endif
+    key_t (tree type, const program_point &point,
+	   const svalue2 *base_sval, const svalue2 *iter_sval)
+      : m_type (type), m_point (point.get_function_point ()),
+	m_base_sval (base_sval), m_iter_sval (iter_sval)
     {}
 
     hashval_t hash () const
@@ -908,6 +967,7 @@ public:
     bool operator== (const key_t &other) const
     {
       return (m_type == other.m_type
+	      && m_point == other.m_point
 	      && m_base_sval == other.m_base_sval
 	      && m_iter_sval == other.m_iter_sval);
     }
@@ -918,12 +978,19 @@ public:
     bool is_empty () const { return m_type == NULL_TREE; }
 
     tree m_type;
+#if 0
+    program_point m_point;
+#else
+    // FIXME: for now, just the function_point, as it's easier to hash this
+    function_point m_point;
+#endif
     const svalue2 *m_base_sval;
     const svalue2 *m_iter_sval;
   };
-  widening_svalue2 (tree type, const svalue2 *base_sval,
-		    const svalue2 *iter_sval)
-  : svalue2 (type), m_base_sval (base_sval), m_iter_sval (iter_sval)
+  widening_svalue2 (tree type, const program_point &point,
+		    const svalue2 *base_sval, const svalue2 *iter_sval)
+  : svalue2 (type), m_point (point.get_function_point ()),
+    m_base_sval (base_sval), m_iter_sval (iter_sval)
   {
   }
 
@@ -942,6 +1009,11 @@ public:
   const svalue2 *get_iter_svalue2 () const { return m_iter_sval; }
 
  private:
+#if 0
+  program_point m_point;
+#else
+  function_point m_point;
+#endif
   const svalue2 *m_base_sval;
   const svalue2 *m_iter_sval;
 };
@@ -1216,6 +1288,8 @@ public:
   const region2 *get_region_for_local (region_model2_manager *mgr,
 				       tree expr) const;
 
+  unsigned get_num_locals () const { return m_locals.elements (); }
+
  private:
   const frame_region2 *m_calling_frame;
   function *m_fun;
@@ -1867,6 +1941,7 @@ public:
 					    const region2 *subregion);
   const svalue2 *get_or_create_unmergeable (const svalue2 *arg);
   const svalue2 *get_or_create_widening_svalue2 (tree type,
+						 const program_point &point,
 						 const svalue2 *base_svalue,
 						 const svalue2 *iter_svalue);
 
@@ -1955,7 +2030,10 @@ private:
 		   unmergeable_svalue2 *> unmergeable_values_map_t;
   unmergeable_values_map_t m_unmergeable_values_map;
 
-  typedef hash_map<widening_svalue2::key_t, widening_svalue2 *> widening_values_map_t;
+  typedef hash_map<widening_svalue2::key_t,
+		   widening_svalue2 */*,
+	           widening_svalue2::key_t::hash_map_traits*/>
+    widening_values_map_t;
   widening_values_map_t m_widening_values_map;
 
   /* region2 consolidation.  */
@@ -2193,6 +2271,7 @@ class region_model2
 				      logger *logger);
 #endif
   bool can_merge_with_p (const region_model2 &other_model,
+			 const program_point &point,
 			 region_model2 *out_model) const;
 #if 0
   svalue2_id get_value_by_name (const char *name) const;
diff --git a/gcc/analyzer/store2.cc b/gcc/analyzer/store2.cc
index e61c13301ab..a87cb330e63 100644
--- a/gcc/analyzer/store2.cc
+++ b/gcc/analyzer/store2.cc
@@ -602,7 +602,8 @@ bool
 binding_cluster2::can_merge_p (const binding_cluster2 *cluster_a,
 			       const binding_cluster2 *cluster_b,
 			       binding_cluster2 *out_cluster,
-			       store2_manager *mgr)
+			       store2_manager *mgr,
+			       const program_point &point)
 {
   gcc_assert (out_cluster);
 
@@ -663,7 +664,7 @@ binding_cluster2::can_merge_p (const binding_cluster2 *cluster_a,
 	    {
 	      region_model2_manager *sval_mgr = mgr->get_svalue2_manager ();
 	      if (const svalue2 *merged_sval
-		  = sval_a->can_merge_p (sval_b, sval_mgr))
+		  = sval_a->can_merge_p (sval_b, sval_mgr, point))
 		{
 		  out_cluster->m_map.put (key, merged_sval);
 		  continue;
@@ -1296,6 +1297,7 @@ store2::zero_fill_region (store2_manager *mgr, const region2 *reg)
 {
   const region2 *base_reg = reg->get_base_region ();
   if (base_reg->symbolic_for_unknown_ptr_p ())
+    // FIXME: should we also invalidate existing clusters?
     return;
   binding_cluster2 *cluster = get_or_create_cluster (base_reg);
   cluster->zero_fill_region (mgr, reg);
@@ -1308,6 +1310,7 @@ store2::mark_region_as_unknown (store2_manager *mgr, const region2 *reg)
 {
   const region2 *base_reg = reg->get_base_region ();
   if (base_reg->symbolic_for_unknown_ptr_p ())
+    // FIXME: should we also invalidate existing clusters?
     return;
   binding_cluster2 *cluster = get_or_create_cluster (base_reg);
   cluster->mark_region_as_unknown (mgr, reg);
@@ -1365,7 +1368,8 @@ store2::get_or_create_cluster (const region2 *base_reg)
 
 bool
 store2::can_merge_p (const store2 *store_a, const store2 *store_b,
-		     store2 *out_store, store2_manager *mgr)
+		     store2 *out_store, store2_manager *mgr,
+		     const program_point &point)
 {
   /* Get the union of all base regions for STORE_A and STORE_B.  */
   hash_set<const region2 *> base_regions;
@@ -1392,7 +1396,7 @@ store2::can_merge_p (const store2 *store_a, const store2 *store_b,
       binding_cluster2 *out_cluster
 	= out_store->get_or_create_cluster (base_reg);
       if (!binding_cluster2::can_merge_p (cluster_a, cluster_b,
-					  out_cluster, mgr))
+					  out_cluster, mgr, point))
 	return false;
     }
   return true;
diff --git a/gcc/analyzer/store2.h b/gcc/analyzer/store2.h
index 88516b06140..04f5b68a5fc 100644
--- a/gcc/analyzer/store2.h
+++ b/gcc/analyzer/store2.h
@@ -321,7 +321,8 @@ public:
   static bool can_merge_p (const binding_cluster2 *cluster_a,
 			   const binding_cluster2 *cluster_b,
 			   binding_cluster2 *out_cluster,
-			   store2_manager *mgr);
+			   store2_manager *mgr,
+			   const program_point &point);
   void make_unknown_relative_to (const binding_cluster2 *other_cluster,
 				 store2_manager *mgr);
 
@@ -419,7 +420,8 @@ public:
   }
 
   static bool can_merge_p (const store2 *store_a, const store2 *store_b,
-			   store2 *out_store, store2_manager *mgr);
+			   store2 *out_store, store2_manager *mgr,
+			   const program_point &point);
 
   void on_escape_to_unknown_fncall (store2_manager *mgr,
 				    const region2 *base_reg);
-- 
2.26.2

