From 9882fef92744d39c6825c0d4b652869e3576f083 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 14 May 2020 15:28:07 -0400
Subject: [PATCH 132/251] FIXME: initial WIP on state merging

---
 gcc/analyzer/constraint-manager2.cc |  14 +-
 gcc/analyzer/constraint-manager2.h  |   4 +-
 gcc/analyzer/program-state.cc       |  43 ++++-
 gcc/analyzer/program-state.h        |   3 +
 gcc/analyzer/region-model2.cc       | 276 ++++++++++++++--------------
 gcc/analyzer/region-model2.h        |  19 +-
 gcc/analyzer/store2.cc              | 193 ++++++++++++++++++-
 gcc/analyzer/store2.h               |  20 +-
 8 files changed, 408 insertions(+), 164 deletions(-)

diff --git a/gcc/analyzer/constraint-manager2.cc b/gcc/analyzer/constraint-manager2.cc
index e6a6c21653c..80cc79802f9 100644
--- a/gcc/analyzer/constraint-manager2.cc
+++ b/gcc/analyzer/constraint-manager2.cc
@@ -1355,18 +1355,22 @@ private:
   constraint_manager2 *m_out;
 };
 
+// FIXME
 /* Use MERGER to merge CM_A and CM_B into *OUT.
    If one thinks of a constraint_manager2 as a subset of N-dimensional
    space, this takes the union of the points of CM_A and CM_B, and
    expresses that into *OUT.  Alternatively, it can be thought of
    as the intersection of the constraints.  */
-#if 0
+
 void
 constraint_manager2::merge (const constraint_manager2 &cm_a,
-			   const constraint_manager2 &cm_b,
-			   constraint_manager2 *out,
-			   const model_merger &merger)
+			    const constraint_manager2 &cm_b,
+			    constraint_manager2 *out/*,
+			    const model_merger &merger*/)
 {
+  // TODO
+  // gcc_unreachable ();
+#if 0
   gcc_assert (merger.m_sval_mapping);
 
   /* Map svalue2_ids in each equiv class from both sources
@@ -1392,8 +1396,8 @@ constraint_manager2::merge (const constraint_manager2 &cm_a,
      and add those to *OUT.  */
   merger_fact_visitor v (&cleaned_cm_b, out);
   cleaned_cm_a.for_each_fact (&v);
-}
 #endif
+}
 
 /* A subroutine of constraint_manager2::merge.
    Use MAP_SVAL_TO_M to map equivalence classes and constraints from
diff --git a/gcc/analyzer/constraint-manager2.h b/gcc/analyzer/constraint-manager2.h
index 0e855dd3267..7c1fec5db6e 100644
--- a/gcc/analyzer/constraint-manager2.h
+++ b/gcc/analyzer/constraint-manager2.h
@@ -222,8 +222,8 @@ public:
 
   static void merge (const constraint_manager2 &cm_a,
 		     const constraint_manager2 &cm_b,
-		     constraint_manager2 *out,
-		     const model_merger &merger);
+		     constraint_manager2 *out/*,
+		     const model_merger &merger*/);
 
   void for_each_fact (fact2_visitor *) const;
 
diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc
index 505a378e212..ece7b7a58f2 100644
--- a/gcc/analyzer/program-state.cc
+++ b/gcc/analyzer/program-state.cc
@@ -1809,15 +1809,14 @@ program_state::can_merge_with_p (const program_state &other,
 {
   gcc_assert (out);
 
+  if (m_region_model2)
+    return can_merge_with2_p (other, ext_state, out);
+
   /* TODO:  initially I had an early reject here if there
      are sm-differences between the states.  However, this was
      falsely rejecting merger opportunities for states where the
      only difference was in svalue_id ordering.  */
 
-  // FIXME:
-  if (m_region_model2)
-    return false;
-
   /* Attempt to merge the region_models.  */
 
   svalue_id_merger_mapping sid_mapping (*m_region_model,
@@ -1877,14 +1876,46 @@ program_state::can_merge_with_p (const program_state &other,
 	}
     }
 
-  // TODO: m_checker_states2  
-
   impl_region_model_context ctxt (out, NULL, ext_state);
   out->m_region_model->canonicalize (&ctxt);
 
   return true;
 }
 
+/* Attempt to merge this state with OTHER, both using EXT_STATE.
+   Write the result to *OUT.
+   If the states were merged successfully, return true.  */
+
+bool
+program_state::can_merge_with2_p (const program_state &other,
+				  const extrinsic_state &ext_state,
+				  program_state *out) const
+{
+  gcc_assert (out);
+  gcc_assert (m_region_model2);
+
+  /* Early reject if there are sm-differences between the states.  */
+  int i;
+  sm_state_map *smap;
+  FOR_EACH_VEC_ELT (out->m_checker_states, i, smap)
+    if (m_checker_states[i] != other.m_checker_states[i])
+      return false;
+
+  /* Attempt to merge the region_models.  */
+  if (!m_region_model2->can_merge_with_p (*other.m_region_model2,
+					  out->m_region_model2))
+    return false;
+
+  /* Copy m_checker_states to OUT.  */
+  FOR_EACH_VEC_ELT (out->m_checker_states, i, smap)
+    {
+      delete smap;
+      out->m_checker_states[i] = m_checker_states[i]->clone ();
+    }
+
+  return true;
+}
+
 /* Assert that this object is valid.  */
 
 void
diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h
index 3cbdd916328..f9e5065756f 100644
--- a/gcc/analyzer/program-state.h
+++ b/gcc/analyzer/program-state.h
@@ -409,6 +409,9 @@ public:
   bool can_merge_with_p (const program_state &other,
 			 const extrinsic_state &ext_state,
 			 program_state *out) const;
+  bool can_merge_with2_p (const program_state &other,
+			  const extrinsic_state &ext_state,
+			  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 3344a800b5c..c679ec54947 100644
--- a/gcc/analyzer/region-model2.cc
+++ b/gcc/analyzer/region-model2.cc
@@ -2066,19 +2066,6 @@ root_region2::dump_to_pp (pretty_printer *pp, bool simple) const
     pp_string (pp, "root_region2()");
 }
 
-/* Implementation of region2::validate vfunc for root_region2.  */
-#if 0
-void
-root_region2::validate (const region_model2 &model) const
-{
-  region2::validate (model);
-  m_stack_reg.validate (model);
-  m_globals_reg.validate (model);
-  m_code_reg.validate (model);
-  m_heap_reg.validate (model);
-}
-#endif
-
 /* Create a new frame_region2 for a call to FUN and push it onto
    the stack.
 
@@ -2635,6 +2622,24 @@ region_model2_manager::get_or_create_constant_svalue2 (tree cst_expr)
   // TODO: cst equality vs tree pointer equality?
 }
 
+/* Return an svalue2 *for a unknown_svalue2 for CST_EXPR,
+   creating the unknown_svalue2 if necessary.
+   The unknown_svalue2 instances are reused, based on pointer equality
+   of trees  */
+
+const svalue2 *
+region_model2_manager::get_or_create_unknown_svalue2 (tree type)
+{
+  gcc_assert (type);
+
+  unknown_svalue2 **slot = m_unknowns_map.get (type);
+  if (slot)
+    return *slot;
+  unknown_svalue2 *sval = new unknown_svalue2 (type);
+  m_unknowns_map.put (type, sval);
+  return sval;
+}
+
 const svalue2 *
 region_model2_manager::get_or_create_initial_value (const region2 *reg)
 {
@@ -6277,23 +6282,6 @@ region_model2::apply_constraints_for_gswitch (const switch_cfg_superedge &edge,
     }
 }
 
-/* Get the root_region2 within this model (guaranteed to be non-null).  */
-#if 0
-root_region2 *
-region_model2::get_root_region2 () const
-{
-  return get_region2<root_region2> (m_root_reg);
-}
-
-/* Get the region2 *of this model's stack region2 (if any).  */
-
-region2_id
-region_model2::get_stack_region2 *() const
-{
-  return get_root_region2 ()->get_stack_region2 *();
-}
-#endif
-
 /* For use with push_frame when handling a top-level call within the analysis.
    PARAM has a defined but unknown initial value.
    Anything it points to has escaped, since the calling context "knows"
@@ -6802,59 +6790,32 @@ 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, and populating SID_MAPPING.  */
-#if 0
+   to OUT_MODEL.  */
+
 bool
 region_model2::can_merge_with_p (const region_model2 &other_model,
-				region_model2 *out_model,
-				svalue2_id_merger_mapping *sid_mapping) const
+				 region_model2 *out_model) const
 {
-  gcc_assert (m_root_reg == other_model.m_root_reg);
-  gcc_assert (m_root_reg.as_int () == 0);
-  gcc_assert (sid_mapping);
   gcc_assert (out_model);
+  gcc_assert (m_mgr == other_model.m_mgr);
+  gcc_assert (m_mgr == out_model->m_mgr);
 
-  model_merger merger (this, &other_model, out_model, sid_mapping);
+  if (m_current_frame != other_model.m_current_frame)
+    return false;
+  out_model->m_current_frame = m_current_frame;
 
-  if (!root_region2::can_merge_p (get_root_region2 (),
-				 other_model.get_root_region2 (),
-				 out_model->get_root_region2 (),
-				 &merger))
+  if (!store2::can_merge_p (&m_store, &other_model.m_store,
+			    &out_model->m_store))
     return false;
 
   /* Merge constraints.  */
-  constraint_manager::merge (*m_constraints,
-			     *other_model.m_constraints,
-			     out_model->m_constraints,
-			     merger);
-
-  out_model->validate ();
-
-  /* The merged model should be simpler (or as simple) as the inputs.  */
-#if 0
-  gcc_assert (out_model->m_svalue2s.length () <= m_svalue2s.length ());
-  gcc_assert (out_model->m_svalue2s.length ()
-	      <= other_model.m_svalue2s.length ());
-#endif
-  gcc_assert (out_model->m_region2s.length () <= m_region2s.length ());
-  gcc_assert (out_model->m_region2s.length ()
-	      <= other_model.m_region2s.length ());
-  // TODO: same, for constraints
+  constraint_manager2::merge (*m_constraints2,
+			      *other_model.m_constraints2,
+			      out_model->m_constraints2);
 
   return true;
 }
 
-/* As above, but supply a placeholder svalue2_id_merger_mapping
-   instance to be used and receive output.  For use in selftests.  */
-
-bool
-region_model2::can_merge_with_p (const region_model2 &other_model,
-				region_model2 *out_model) const
-{
-  svalue2_id_merger_mapping sid_mapping (*this, other_model);
-  return can_merge_with_p (other_model, out_model, &sid_mapping);
-}
-#endif
 /* For debugging purposes: look for a region2 within this region_model2
    for a decl named NAME (or an SSA_NAME for such a decl),
    returning its value, or svalue2_id::null if none are found.  */
@@ -8588,6 +8549,7 @@ test_model_equality_4 ()
 
   model.canonicalize (NULL);
 }
+#endif
 
 /* Assert that if we have two region_model2 instances
    with values VAL_A and VAL_B for EXPR that they are
@@ -8598,12 +8560,13 @@ test_model_equality_4 ()
 
 static void
 assert_region_model2s_merge (tree expr, tree val_a, tree val_b,
-			    region_model2 *out_merged_model,
-			    const svalue2 **out_merged_svalue2)
+			     region_model2 *out_merged_model,
+			     const svalue2 **out_merged_svalue2)
 {
   test_region_model2_context ctxt;
-  region_model2 model0;
-  region_model2 model1;
+  region_model2_manager *mgr = out_merged_model->get_manager ();
+  region_model2 model0 (mgr);
+  region_model2 model1 (mgr);
   if (val_a)
     model0.set_value (model0.get_lvalue (expr, &ctxt),
 		      model0.get_rvalue (val_a, &ctxt),
@@ -8615,9 +8578,7 @@ assert_region_model2s_merge (tree expr, tree val_a, tree val_b,
 
   /* They should be mergeable.  */
   ASSERT_TRUE (model0.can_merge_with_p (model1, out_merged_model));
-
-  const svalue2 *merged_svalue2_sval = out_merged_model->get_rvalue (expr, &ctxt);
-  *out_merged_svalue2 = out_merged_model->get_svalue2 (merged_svalue2_sval);
+  *out_merged_svalue2 = out_merged_model->get_rvalue (expr, &ctxt);
 }
 
 /* Verify that we can merge region_model2 instances.  */
@@ -8650,10 +8611,12 @@ test_state_merging ()
 		       get_identifier ("q"),
 		       ptr_type_node);
 
+  region_model2_manager mgr;
+
   {
-    region_model2 model0;
-    region_model2 model1;
-    region_model2 merged;
+    region_model2 model0 (&mgr);
+    region_model2 model1 (&mgr);
+    region_model2 merged (&mgr);
     /* Verify empty models can be merged.  */
     ASSERT_TRUE (model0.can_merge_with_p (model1, &merged));
     ASSERT_EQ (model0, merged);
@@ -8664,9 +8627,9 @@ test_state_merging ()
   /* TODO: verify that the merged model doesn't have a value for
      the global  */
   {
-    region_model2 model0;
-    region_model2 model1;
-    region_model2 merged;
+    region_model2 model0 (&mgr);
+    region_model2 model1 (&mgr);
+    region_model2 merged (&mgr);
     test_region_model2_context ctxt;
     model0.add_constraint (x, EQ_EXPR, int_42, &ctxt);
     model1.add_constraint (x, EQ_EXPR, int_113, &ctxt);
@@ -8675,16 +8638,20 @@ test_state_merging ()
     ASSERT_NE (model1, merged);
   }
 
+  // FIXME: uses "set_to_new_unknown_value"
+#if 0
   /* Verify handling of a PARM_DECL.  */
   {
     test_region_model2_context ctxt;
-    region_model2 model0;
-    region_model2 model1;
+    region_model2 model0 (&mgr);
+    region_model2 model1 (&mgr);
     ASSERT_EQ (model0.get_stack_depth (), 0);
     model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, &ctxt);
     ASSERT_EQ (model0.get_stack_depth (), 1);
+#if 0
     ASSERT_EQ (model0.get_function_at_depth (0),
 	       DECL_STRUCT_FUNCTION (test_fndecl));
+#endif
     model1.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, &ctxt);
 
     const svalue2 *sval_a
@@ -8694,23 +8661,29 @@ test_state_merging ()
 				     integer_type_node, &ctxt);
     ASSERT_EQ (model0, model1);
 
+    // TODO:
+#if 0
     /* Check that get_value_by_name works for locals.  */
     ASSERT_EQ (model0.get_value_by_name ("a"), sid_a);
+#endif
 
     /* They should be mergeable, and the result should be the same.  */
-    region_model2 merged;
+    region_model2 merged (&mgr);
     ASSERT_TRUE (model0.can_merge_with_p (model1, &merged));
     ASSERT_EQ (model0, merged);
     /* In particular, there should be an unknown value for "a".  */
-    const svalue2 *merged_a_sval = merged.get_svalue2 (merged.get_rvalue (a, &ctxt));
-    ASSERT_EQ (merged_a_sval->get_kind (), SK_UNKNOWN);
+    const svalue2 *merged_a_sval = merged.get_rvalue (a, &ctxt);
+    ASSERT_EQ (merged_a_sval->get_kind (), svalue2::SK_UNKNOWN);
   }
+#endif
 
+  // FIXME: uses "set_to_new_unknown_value"
+#if 0
   /* Verify handling of a global.  */
   {
     test_region_model2_context ctxt;
-    region_model2 model0;
-    region_model2 model1;
+    region_model2 model0 (&mgr);
+    region_model2 model1 (&mgr);
     const svalue2 *sval_x
       = model0.set_to_new_unknown_value (model0.get_lvalue (x, &ctxt),
 					 integer_type_node, &ctxt);
@@ -8718,60 +8691,73 @@ test_state_merging ()
 				     integer_type_node, &ctxt);
     ASSERT_EQ (model0, model1);
 
+    // TODO
+#if 0
     /* Check that get_value_by_name works for globals.  */
     ASSERT_EQ (model0.get_value_by_name ("x"), sid_x);
+#endif
 
     /* They should be mergeable, and the result should be the same.  */
-    region_model2 merged;
+    region_model2 merged (&mgr);
     ASSERT_TRUE (model0.can_merge_with_p (model1, &merged));
     ASSERT_EQ (model0, merged);
     /* In particular, there should be an unknown value for "x".  */
-    const svalue2 *merged_x_sval = merged.get_svalue2 (merged.get_rvalue (x, &ctxt));
-    ASSERT_EQ (merged_x_sval->get_kind (), SK_UNKNOWN);
+    const svalue2 *merged_x_sval = merged.get_rvalue (x, &ctxt);
+    ASSERT_EQ (merged_x_sval->get_kind (), svalue2::SK_UNKNOWN);
   }
+#endif
 
   /* Use global-handling to verify various combinations of values.  */
 
   /* Two equal constant values.  */
   {
-    region_model2 merged;
+    region_model2 merged (&mgr);
     const svalue2 *merged_x_sval;
     assert_region_model2s_merge (x, int_42, int_42, &merged, &merged_x_sval);
 
     /* In particular, there should be a constant value for "x".  */
-    ASSERT_EQ (merged_x_sval->get_kind (), SK_CONSTANT);
+    ASSERT_EQ (merged_x_sval->get_kind (), svalue2::SK_CONSTANT);
     ASSERT_EQ (merged_x_sval->dyn_cast_constant_svalue2 ()->get_constant (),
 	       int_42);
   }
 
   /* Two non-equal constant values.  */
   {
-    region_model2 merged;
+    region_model2 merged (&mgr);
     const svalue2 *merged_x_sval;
+    // FIXME:
+#if 0
     assert_region_model2s_merge (x, int_42, int_113, &merged, &merged_x_sval);
 
     /* In particular, there should be an unknown value for "x".  */
-    ASSERT_EQ (merged_x_sval->get_kind (), SK_UNKNOWN);
+    ASSERT_EQ (merged_x_sval->get_kind (), svalue2::SK_UNKNOWN);
+#endif
   }
 
   /* Uninit and constant.  */
   {
-    region_model2 merged;
+    region_model2 merged (&mgr);
     const svalue2 *merged_x_sval;
+    // FIXME:
+#if 0
     assert_region_model2s_merge (x, NULL_TREE, int_113, &merged, &merged_x_sval);
 
     /* In particular, there should be an unknown value for "x".  */
-    ASSERT_EQ (merged_x_sval->get_kind (), SK_UNKNOWN);
+    ASSERT_EQ (merged_x_sval->get_kind (), svalue2::SK_UNKNOWN);
+#endif
   }
 
   /* Constant and uninit.  */
   {
-    region_model2 merged;
+    region_model2 merged (&mgr);
     const svalue2 *merged_x_sval;
+    // FIXME:
+#if 0
     assert_region_model2s_merge (x, int_42, NULL_TREE, &merged, &merged_x_sval);
 
     /* In particular, there should be an unknown value for "x".  */
-    ASSERT_EQ (merged_x_sval->get_kind (), SK_UNKNOWN);
+    ASSERT_EQ (merged_x_sval->get_kind (), svalue2::SK_UNKNOWN);
+#endif
   }
 
   /* Unknown and constant.  */
@@ -8783,9 +8769,11 @@ test_state_merging ()
   /* Pointers: NULL and non-NULL.  */
   // TODO
 
+  // FIXME: uses "set_to_new_unknown_value"
+#if 0
   /* Pointers: non-NULL and non-NULL: ptr to a local.  */
   {
-    region_model2 model0;
+    region_model2 model0 (&mgr);
     model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL);
     model0.set_to_new_unknown_value (model0.get_lvalue (a, NULL),
 				     integer_type_node, NULL);
@@ -8796,76 +8784,80 @@ test_state_merging ()
     ASSERT_EQ (model0, model1);
 
     /* They should be mergeable, and the result should be the same.  */
-    region_model2 merged;
+    region_model2 merged (&mgr);
     ASSERT_TRUE (model0.can_merge_with_p (model1, &merged));
     ASSERT_EQ (model0, merged);
   }
+#endif
 
   /* Pointers: non-NULL and non-NULL: ptr to a global.  */
   {
-    region_model2 merged;
+    region_model2 merged (&mgr);
     /* p == &y in both input models.  */
     const svalue2 *merged_p_sval;
     assert_region_model2s_merge (p, addr_of_y, addr_of_y, &merged,
 				&merged_p_sval);
 
     /* We should get p == &y in the merged model.  */
-    ASSERT_EQ (merged_p_sval->get_kind (), SK_REGION2);
-    region_const svalue2 *merged_p_ptr = merged_p_sval->dyn_cast_region_svalue2 ();
+    ASSERT_EQ (merged_p_sval->get_kind (), svalue2::SK_REGION);
+    const region_svalue2 *merged_p_ptr
+      = merged_p_sval->dyn_cast_region_svalue2 ();
     const region2 *merged_p_star_reg = merged_p_ptr->get_pointee ();
     ASSERT_EQ (merged_p_star_reg, merged.get_lvalue (y, NULL));
   }
 
   /* Pointers: non-NULL ptrs to different globals: should be unknown.  */
   {
-    region_model2 merged;
+    region_model2 merged (&mgr);
     /* x == &y vs x == &z in the input models.  */
     const svalue2 *merged_x_sval;
+    // TODO:
+#if 0
     assert_region_model2s_merge (x, addr_of_y, addr_of_z, &merged,
 				&merged_x_sval);
 
     /* We should get x == unknown in the merged model.  */
-    ASSERT_EQ (merged_x_sval->get_kind (), SK_UNKNOWN);
+    ASSERT_EQ (merged_x_sval->get_kind (), svalue2::SK_UNKNOWN);
+#endif
   }
 
   /* Pointers: non-NULL and non-NULL: ptr to a heap region2.  */
   {
     test_region_model2_context ctxt;
-    region_model2 model0;
-    const region2 *new_reg = model0.add_new_malloc_region2 ();
-    const svalue2 *ptr_sval
-      = model0.get_or_create_ptr_svalue2 (ptr_type_node, new_reg);
+    region_model2 model0 (&mgr);
+    tree size = build_int_cst (integer_type_node, 1024);
+    const svalue2 *size_sval = mgr.get_or_create_constant_svalue2 (size);
+    const region2 *new_reg = model0.create_region_for_heap_alloc (size_sval);
+    const svalue2 *ptr_sval = mgr.get_ptr_svalue2 (ptr_type_node, new_reg);
     model0.set_value (model0.get_lvalue (p, &ctxt),
 		      ptr_sval, &ctxt);
-    model0.canonicalize (&ctxt);
 
     region_model2 model1 (model0);
 
     ASSERT_EQ (model0, model1);
 
-    region_model2 merged;
+    region_model2 merged (&mgr);
     ASSERT_TRUE (model0.can_merge_with_p (model1, &merged));
 
-    merged.canonicalize (&ctxt);
-
-    /* The merged model ought to be identical (after canonicalization,
-       at least).  */
+    /* The merged model ought to be identical.  */
     ASSERT_EQ (model0, merged);
   }
 
+  // FIXME: uses "set_to_new_unknown_value"
+#if 0
   /* Two region2s sharing the same unknown svalue2 should continue sharing
      an unknown svalue2 after self-merger.  */
   {
     test_region_model2_context ctxt;
-    region_model2 model0;
+    region_model2 model0 (&mgr);
     const svalue2 *sval
       = model0.set_to_new_unknown_value (model0.get_lvalue (x, &ctxt),
 					 integer_type_node, &ctxt);
-    model0.set_value (model0.get_lvalue (y, &ctxt), sid, &ctxt);
+    model0.set_value (model0.get_lvalue (y, &ctxt), sval, &ctxt);
     region_model2 model1 (model0);
 
     /* They should be mergeable, and the result should be the same.  */
-    region_model2 merged;
+    region_model2 merged (&mgr);
     ASSERT_TRUE (model0.can_merge_with_p (model1, &merged));
     ASSERT_EQ (model0, merged);
 
@@ -8873,11 +8865,12 @@ test_state_merging ()
     ASSERT_EQ (merged.eval_condition (x, EQ_EXPR, y, &ctxt),
 	       tristate (tristate::TS_TRUE));
   }
+#endif
 
 #if 0
   {
-    region_model2 model0;
-    region_model2 model1;
+    region_model2 model0 (&mgr);
+    region_model2 model1 (&mgr);
     test_region_model2_context ctxt;
     model0.add_constraint (x, EQ_EXPR, int_42, &ctxt);
     model1.add_constraint (x, NE_EXPR, int_42, &ctxt);
@@ -8885,8 +8878,8 @@ test_state_merging ()
   }
 
   {
-    region_model2 model0;
-    region_model2 model1;
+    region_model2 model0 (&mgr);
+    region_model2 model1 (&mgr);
     test_region_model2_context ctxt;
     model0.add_constraint (x, EQ_EXPR, int_42, &ctxt);
     model1.add_constraint (x, NE_EXPR, int_42, &ctxt);
@@ -8906,8 +8899,9 @@ test_state_merging ()
 
   /* Views.  */
   {
+#if 0
     test_region_model2_context ctxt;
-    region_model2 model0;
+    region_model2 model0 (&mgr);
 
     const region2 *x_reg = model0.get_lvalue (x, &ctxt);
     const region2 *x_as_ptr = model0.get_or_create_view (x_reg, ptr_type_node,
@@ -8918,14 +8912,16 @@ test_state_merging ()
     ASSERT_EQ (model1, model0);
 
     /* They should be mergeable, and the result should be the same.  */
-    region_model2 merged;
+    region_model2 merged (&mgr);
     ASSERT_TRUE (model0.can_merge_with_p (model1, &merged));
+#endif
   }
 
   /* Verify that we can merge a model in which a local in an older stack
      frame points to a local in a more recent stack frame.  */
   {
-    region_model2 model0;
+#if 0
+    region_model2 model0 (&mgr);
     model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL);
     const region2 *q_in_first_frame = model0.get_lvalue (q, NULL);
 
@@ -8950,29 +8946,27 @@ test_state_merging ()
 
     /* They should be mergeable, and the result should be the same
        (after canonicalization, at least).  */
-    region_model2 merged;
+    region_model2 merged (&mgr);
     ASSERT_TRUE (model0.can_merge_with_p (model1, &merged));
     merged.canonicalize (NULL);
     ASSERT_EQ (model0, merged);
+#endif
   }
 
   /* Verify that we can merge a model in which a local points to a global.  */
   {
-    region_model2 model0;
+    region_model2 model0 (&mgr);
     model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL);
     model0.set_value (model0.get_lvalue (q, NULL),
 		      model0.get_rvalue (addr_of_y, NULL), NULL);
 
-    model0.canonicalize (NULL);
-
     region_model2 model1 (model0);
     ASSERT_EQ (model0, model1);
 
     /* They should be mergeable, and the result should be the same
        (after canonicalization, at least).  */
-    region_model2 merged;
+    region_model2 merged (&mgr);
     ASSERT_TRUE (model0.can_merge_with_p (model1, &merged));
-    merged.canonicalize (NULL);
     ASSERT_EQ (model0, merged);
   }
 }
@@ -8990,10 +8984,13 @@ test_constraint_merging ()
   tree z = build_global_decl ("z", integer_type_node);
   tree n = build_global_decl ("n", integer_type_node);
 
+  region_model2_manager mgr;
   test_region_model2_context ctxt;
 
   /* model0: 0 <= (x == y) < n.  */
-  region_model2 model0;
+  region_model2 model0 (&mgr);
+  // FIXME: uses "set_to_new_unknown_value"
+#if 0
   model0.set_to_new_unknown_value (model0.get_lvalue (x, &ctxt),
 				   integer_type_node, &ctxt);
   model0.add_constraint (x, EQ_EXPR, y, &ctxt);
@@ -9001,7 +8998,7 @@ test_constraint_merging ()
   model0.add_constraint (x, LT_EXPR, n, NULL);
 
   /* model1: z != 5 && (0 <= x < n).  */
-  region_model2 model1;
+  region_model2 model1 (&mgr);
   model1.set_to_new_unknown_value (model1.get_lvalue (x, &ctxt),
 				   integer_type_node, &ctxt);
   model1.add_constraint (z, NE_EXPR, int_5, NULL);
@@ -9010,7 +9007,7 @@ test_constraint_merging ()
 
   /* They should be mergeable; the merged constraints should
      be: (0 <= x < n).  */
-  region_model2 merged;
+  region_model2 merged (&mgr);
   ASSERT_TRUE (model0.can_merge_with_p (model1, &merged));
 
   ASSERT_EQ (merged.eval_condition (x, GE_EXPR, int_0, &ctxt),
@@ -9022,8 +9019,11 @@ test_constraint_merging ()
 	     tristate (tristate::TS_UNKNOWN));
   ASSERT_EQ (merged.eval_condition (x, LT_EXPR, y, &ctxt),
 	     tristate (tristate::TS_UNKNOWN));
+#endif
 }
 
+#if 0
+
 /* Verify that if we mark a pointer to a malloc-ed region2 as non-NULL,
    all cast pointers to that region2 are also known to be non-NULL.  */
 
@@ -9380,8 +9380,10 @@ analyzer_region_model2_cc_tests ()
   test_model_equality_3 ();
 #if 0
   test_model_equality_4 ();
+#endif
   test_state_merging ();
   test_constraint_merging ();
+#if 0
   test_malloc_constraints ();
 #endif
   test_var ();
diff --git a/gcc/analyzer/region-model2.h b/gcc/analyzer/region-model2.h
index 91a2f0cfff0..d54e42720b5 100644
--- a/gcc/analyzer/region-model2.h
+++ b/gcc/analyzer/region-model2.h
@@ -1296,13 +1296,6 @@ private:
 #if 0
   void add_to_hash (inchash::hash &hstate) const FINAL OVERRIDE;
 #endif
-
-#if 0
-  region2_id m_stack_rid;
-  region2_id m_globals_rid;
-  region2_id m_code_rid;
-  region2_id m_heap_rid;
-#endif
 };
 
 } // namespace ana
@@ -1837,8 +1830,10 @@ class region_model2
   void clobber_region (const region2 *reg);
   void purge_region (const region2 *reg);
   void zero_fill_region (const region2 *reg);
+#if 0
   const svalue2 *set_to_new_unknown_value (const region2 *dst_reg, tree type,
 				     region_model2_context *ctxt);
+#endif
   void copy_region (const region2 *dst_reg, const region2 *src_reg,
 		    region_model2_context *ctxt);
   tristate eval_condition (const svalue2 *lhs,
@@ -1883,6 +1878,9 @@ class region_model2
   {
     return m_constraints2;
   }
+
+  region_model2_manager *get_manager () const { return m_mgr; }
+
 #if 0
   void get_descendents (region2_id rid, region2_id_set *out,
 			region2_id exclude_rid) const;
@@ -1891,13 +1889,10 @@ class region_model2
 				      enum poison_kind pkind,
 				      purge_stats *stats,
 				      logger *logger);
-
-  bool can_merge_with_p (const region_model2 &other_model,
-			 region_model2 *out_model,
-			 svalue2_id_merger_mapping *out) const;
+#endif
   bool can_merge_with_p (const region_model2 &other_model,
 			 region_model2 *out_model) const;
-
+#if 0
   svalue2_id get_value_by_name (const char *name) const;
 
   svalue2_id convert_byte_offset_to_array_index (tree ptr_type,
diff --git a/gcc/analyzer/store2.cc b/gcc/analyzer/store2.cc
index ec98d9e0d01..b6db23ffd6c 100644
--- a/gcc/analyzer/store2.cc
+++ b/gcc/analyzer/store2.cc
@@ -215,6 +215,44 @@ symbolic_binding2::dump_to_pp (pretty_printer *pp, bool simple) const
 
 /* class binding_cluster2.  */
 
+/* FIXME.  */
+
+binding_cluster2::binding_cluster2 (const binding_cluster2 &other)
+: m_base_region (other.m_base_region), m_map (other.m_map),
+  m_escaped (other.m_escaped)
+{
+#if 0
+  for (map_t::iterator iter = other.m_map.begin (); iter != other.m_map.end ();
+       ++iter)
+    {
+      const binding_key2 *key = (*iter).first;
+      const svalue2 *sval = (*iter).second;
+      m_map.put (key, sval);
+    }
+#endif
+}
+
+/* FIXME.  */
+
+binding_cluster2&
+binding_cluster2::operator= (const binding_cluster2 &other)
+{
+  gcc_assert (m_base_region == other.m_base_region);
+  /* For now, assume we only ever copy to an empty cluster.  */
+  gcc_assert (m_map.elements () == 0);
+  for (map_t::iterator iter = other.m_map.begin (); iter != other.m_map.end ();
+       ++iter)
+    {
+      const binding_key2 *key = (*iter).first;
+      const svalue2 *sval = (*iter).second;
+      m_map.put (key, sval);
+    }
+  m_escaped = other.m_escaped;
+  return *this;
+}
+
+/* FIXME.  */
+
 bool
 binding_cluster2::operator== (const binding_cluster2 &other) const
 {
@@ -457,6 +495,80 @@ binding_cluster2::remove_overlapping_bindings (store2_manager *mgr,
 
 /* FIXME.  */
 
+bool
+binding_cluster2::can_merge_p (const binding_cluster2 *cluster_a,
+			       const binding_cluster2 *cluster_b,
+			       binding_cluster2 *out_cluster)
+{
+  gcc_assert (out_cluster);
+
+  // FIXME: what about m_escaped?
+
+  /* At least one of CLUSTER_A and CLUSTER_B are non-NULL, but either
+     could be NULL.  Handle these cases.  */
+  if (cluster_a == NULL)
+    {
+      gcc_assert (cluster_b != NULL);
+      gcc_assert (cluster_b->m_base_region == out_cluster->m_base_region);
+      // FIXME: am not sure what the value should be
+      gcc_unreachable ();
+      *out_cluster = *cluster_b;
+      return true;
+    }
+  if (cluster_b == NULL)
+    {
+      gcc_assert (cluster_a != NULL);
+      gcc_assert (cluster_a->m_base_region == out_cluster->m_base_region);
+      // FIXME: am not sure what the value should be
+      gcc_unreachable ();
+      *out_cluster = *cluster_a;
+      return true;
+    }
+
+  /* The "both inputs are non-NULL" case.  */
+  gcc_assert (cluster_a != NULL && cluster_b != NULL);
+  gcc_assert (cluster_a->m_base_region == out_cluster->m_base_region);
+  gcc_assert (cluster_b->m_base_region == out_cluster->m_base_region);
+
+  hash_set<const binding_key2 *> keys;
+  for (map_t::iterator iter_a = cluster_a->m_map.begin ();
+       iter_a != cluster_a->m_map.end (); ++iter_a)
+    {
+      const binding_key2 *key_a = (*iter_a).first;
+      keys.add (key_a);
+    }
+  for (map_t::iterator iter_b = cluster_b->m_map.begin ();
+       iter_b != cluster_b->m_map.end (); ++iter_b)
+    {
+      const binding_key2 *key_b = (*iter_b).first;
+      keys.add (key_b);
+    }
+  for (hash_set<const binding_key2 *>::iterator iter = keys.begin ();
+       iter != keys.end (); ++iter)
+    {
+      const binding_key2 *key = *iter;
+      const svalue2 *sval_a = cluster_a->get_any_value (key);
+      const svalue2 *sval_b = cluster_b->get_any_value (key);
+
+      if (sval_a == sval_b)
+	{
+	  gcc_assert (sval_a);
+	  out_cluster->m_map.put (key, sval_a);
+	}
+      else
+	{
+	  /* FIXME: TODO: what should we do when we have non-equal
+	     values?  What does a merged value look like?  */
+	  return false;
+	}
+    }
+  // FIXME: what about overlaps?
+
+  return true;
+}
+
+/* FIXME.  */
+
 void
 binding_cluster2::on_escape_to_unknown_fncall (store2_manager *mgr)
 {
@@ -518,6 +630,16 @@ binding_cluster2::get_representative_path_vars (const region2 *base_reg,
     }
 }
 
+/* FIXME.  */
+
+const svalue2 *
+binding_cluster2::get_any_value (const binding_key2 *key) const
+{
+  if (const svalue2 **slot = const_cast <map_t &> (m_map).get (key))
+    return *slot;
+  return NULL;
+}
+
 /* class store2_manager.  */
 
 /* binding consolidation.  */
@@ -606,7 +728,11 @@ get_sorted_parent_regions (auto_vec<const region2 *> *out,
   const region2 *iter_reg;
   unsigned i;
   FOR_EACH_VEC_ELT (in, i, iter_reg)
-    parent_regions.add (iter_reg->get_parent_region ());
+    {
+      const region2 *parent_reg = iter_reg->get_parent_region ();
+      gcc_assert (parent_reg);
+      parent_regions.add (parent_reg);
+    }
 
   /* Write to OUT.  */
   for (hash_set<const region2 *>::iterator iter = parent_regions.begin();
@@ -681,6 +807,20 @@ store2::dump_to_pp (pretty_printer *pp, bool summarize, bool multiline) const
     }
 }
 
+/* Dump a multiline representation of this store2 to stderr.  */
+
+DEBUG_FUNCTION void
+store2::dump (bool simple) const
+{
+  pretty_printer pp;
+  pp_format_decoder (&pp) = default_tree_printer;
+  pp_show_color (&pp) = pp_show_color (global_dc->printer);
+  pp.buffer->stream = stderr;
+  dump_to_pp (&pp, simple, true);
+  pp_newline (&pp);
+  pp_flush (&pp);
+}
+
 const svalue2 *
 store2::get_any_binding (store2_manager *mgr, const region2 *reg)
 {
@@ -751,6 +891,20 @@ store2::zero_fill_region (store2_manager *mgr, const region2 *reg)
 
 /* FIXME.  */
 
+const binding_cluster2 *
+store2::get_cluster (const region2 *base_reg) const
+{
+  gcc_assert (base_reg);
+  gcc_assert (base_reg->get_base_region () == base_reg);
+  if (binding_cluster2 **slot
+        = const_cast <cluster_map_t &> (m_cluster_map).get (base_reg))
+    return *slot;
+  else
+    return NULL;
+}
+
+/* FIXME.  */
+
 binding_cluster2 *
 store2::get_cluster (const region2 *base_reg)
 {
@@ -779,6 +933,43 @@ store2::get_or_create_cluster (const region2 *base_reg)
   return cluster;
 }
 
+/* FIXME.  */
+
+bool
+store2::can_merge_p (const store2 *store_a, const store2 *store_b,
+		     store2 *out_store)
+{
+  /* Get the union of all base regions for STORE_A and STORE_B.  */
+  hash_set<const region2 *> base_regions;
+  for (cluster_map_t::iterator iter_a = store_a->m_cluster_map.begin ();
+       iter_a != store_a->m_cluster_map.end (); ++iter_a)
+    {
+      const region2 *base_reg_a = (*iter_a).first;
+      base_regions.add (base_reg_a);
+    }
+  for (cluster_map_t::iterator iter_b = store_b->m_cluster_map.begin ();
+       iter_b != store_b->m_cluster_map.end (); ++iter_b)
+    {
+      const region2 *base_reg_b = (*iter_b).first;
+      base_regions.add (base_reg_b);
+    }
+
+  for (hash_set<const region2 *>::iterator iter = base_regions.begin ();
+       iter != base_regions.end (); ++iter)
+    {
+      const region2 *base_reg = *iter;
+      const binding_cluster2 *cluster_a = store_a->get_cluster (base_reg);
+      const binding_cluster2 *cluster_b = store_b->get_cluster (base_reg);
+      /* At least one of cluster_a and cluster_b must be non-NULL.  */
+      binding_cluster2 *out_cluster
+	= out_store->get_or_create_cluster (base_reg);
+      if (!binding_cluster2::can_merge_p (cluster_a, cluster_b,
+					  out_cluster))
+	return false;
+    }
+  return true;
+}
+
 /* FIXME.
    For use when handling an unrecognized function call.  */
 /* FIXME: do we want to mark the cluster as having escaped?
diff --git a/gcc/analyzer/store2.h b/gcc/analyzer/store2.h
index 4b3dd3bc45e..952b274acc0 100644
--- a/gcc/analyzer/store2.h
+++ b/gcc/analyzer/store2.h
@@ -184,6 +184,8 @@ class binding_cluster2
 public:
   binding_cluster2 (const region2 *base_region)
   : m_base_region (base_region), m_map (), m_escaped (false) {}
+  binding_cluster2 (const binding_cluster2 &other);
+  binding_cluster2& operator=(const binding_cluster2 &other);
 
   bool operator== (const binding_cluster2 &other) const;
   bool operator!= (const binding_cluster2 &other) const
@@ -209,6 +211,7 @@ public:
 
   void remove_overlapping_bindings (store2_manager *mgr, const region2 *reg);
 
+#if 1
   template <typename T>
   void for_each_value (void (*cb) (const svalue2 *sval, T user_data),
 		       T user_data)
@@ -216,6 +219,11 @@ public:
     for (map_t::iterator iter = m_map.begin (); iter != m_map.end (); ++iter)
       cb ((*iter).second, user_data);
   }
+#endif
+
+  static bool can_merge_p (const binding_cluster2 *cluster_a,
+			   const binding_cluster2 *cluster_b,
+			   binding_cluster2 *out_cluster);
 
   void on_escape_to_unknown_fncall (store2_manager *mgr);
 
@@ -228,6 +236,7 @@ public:
 				     auto_vec<path_var> *out_pvs) const;
 
 private:
+  const svalue2 *get_any_value (const binding_key2 *key) const;
 #if 0
   void get_overlapping_bindings (store2_manager *mgr, const region2 *reg,
 				 auto_vec<const binding_range *> *out);
@@ -263,6 +272,8 @@ private:
 class store2
 {
 public:
+  typedef hash_map <const region2 *, binding_cluster2 *> cluster_map_t;
+
   store2 ();
   store2 (const store2 &other);
   ~store2 ();
@@ -274,6 +285,7 @@ public:
   }
 
   void dump_to_pp (pretty_printer *pp, bool summarize, bool multiline) const;
+  void dump (bool simple) const;
 
   const svalue2 *get_direct_binding (store2_manager *mgr, const region2 *reg);
   const svalue2 *get_default_binding (store2_manager *mgr, const region2 *reg);
@@ -285,6 +297,7 @@ public:
   void purge_region (store2_manager *mgr, const region2 *reg);
   void zero_fill_region (store2_manager *mgr, const region2 *reg);
 
+  const binding_cluster2 *get_cluster (const region2 *base_reg) const;
   binding_cluster2 *get_cluster (const region2 *base_reg);
   binding_cluster2 *get_or_create_cluster (const region2 *base_reg);
 
@@ -297,6 +310,9 @@ public:
       cb ((*iter).first, user_data);
   }
 
+  static bool can_merge_p (const store2 *store_a, const store2 *store_b,
+			   store2 *out_store);
+
   void on_escape_to_unknown_fncall (store2_manager *mgr,
 				    const region2 *base_reg);
 
@@ -305,10 +321,12 @@ public:
   void get_representative_path_vars (const svalue2 *sval,
 				     auto_vec<path_var> *out_pvs) const;
 
+  cluster_map_t::iterator begin () const { return m_cluster_map.begin (); }
+  cluster_map_t::iterator end () const { return m_cluster_map.end (); }
+
 private:
   void remove_overlapping_bindings (store2_manager *mgr, const region2 *reg);
 
-  typedef hash_map <const region2 *, binding_cluster2 *> cluster_map_t;
   cluster_map_t m_cluster_map; // TODO: deep copy of this!
 };
 
-- 
2.26.2

