From b8f3bec126a455e8130c008ab4e12445b78a8fc5 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 4 Aug 2020 14:30:07 -0400
Subject: [PATCH 11/18] FIXME: move new impl into place of old impl

---
 gcc/Makefile.in                            |     2 -
 gcc/analyzer/analyzer-selftests.cc         |     2 -
 gcc/analyzer/analyzer-selftests.h          |     2 -
 gcc/analyzer/checker-path.h                |     2 +-
 gcc/analyzer/constraint-manager.cc         |  2919 ++++-
 gcc/analyzer/constraint-manager.h          |   275 +-
 gcc/analyzer/constraint-manager2.cc        |  2897 -----
 gcc/analyzer/constraint-manager2.h         |   297 -
 gcc/analyzer/engine.cc                     |     1 -
 gcc/analyzer/exploded-graph.h              |     2 +-
 gcc/analyzer/program-state.cc              |     3 -
 gcc/analyzer/region-model.cc               | 10965 +++++++++++++++++-
 gcc/analyzer/region-model.h                |  3190 +++++-
 gcc/analyzer/region-model2-impl-calls.cc   |     2 +-
 gcc/analyzer/region-model2-reachability.cc |     4 +-
 gcc/analyzer/region-model2.cc              | 11064 -------------------
 gcc/analyzer/region-model2.h               |  3146 ------
 gcc/analyzer/sm-file.cc                    |     2 +-
 gcc/analyzer/sm-malloc.cc                  |     2 +-
 gcc/analyzer/store2.cc                     |     3 +-
 20 files changed, 17135 insertions(+), 17645 deletions(-)
 delete mode 100644 gcc/analyzer/constraint-manager2.cc
 delete mode 100644 gcc/analyzer/constraint-manager2.h
 delete mode 100644 gcc/analyzer/region-model2.cc
 delete mode 100644 gcc/analyzer/region-model2.h

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 46cbcc94ac2..9f736dbeee8 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1231,7 +1231,6 @@ ANALYZER_OBJS = \
 	analyzer/call-string.o \
 	analyzer/checker-path.o \
 	analyzer/constraint-manager.o \
-	analyzer/constraint-manager2.o \
 	analyzer/diagnostic-manager.o \
 	analyzer/engine.o \
 	analyzer/function-set.o \
@@ -1240,7 +1239,6 @@ ANALYZER_OBJS = \
 	analyzer/program-point.o \
 	analyzer/program-state.o \
 	analyzer/region-model.o \
-	analyzer/region-model2.o \
 	analyzer/region-model2-impl-calls.o \
 	analyzer/region-model2-reachability.o \
 	analyzer/sm.o \
diff --git a/gcc/analyzer/analyzer-selftests.cc b/gcc/analyzer/analyzer-selftests.cc
index 908c97b2746..c93f894b788 100644
--- a/gcc/analyzer/analyzer-selftests.cc
+++ b/gcc/analyzer/analyzer-selftests.cc
@@ -53,13 +53,11 @@ run_analyzer_selftests ()
 #if ENABLE_ANALYZER
   analyzer_store2_cc_tests (); // FIXME: keep alphabetized? or by dependency
   analyzer_constraint_manager_cc_tests ();
-  analyzer_constraint_manager2_cc_tests ();
   analyzer_function_set_cc_tests ();
   analyzer_immutable_hash_map_cc_tests ();
   analyzer_program_point_cc_tests ();
   analyzer_program_state_cc_tests ();
   analyzer_region_model_cc_tests ();
-  analyzer_region_model2_cc_tests ();
   analyzer_sm_file_cc_tests ();
   analyzer_sm_signal_cc_tests ();
   //analyzer_store2_cc_tests ();
diff --git a/gcc/analyzer/analyzer-selftests.h b/gcc/analyzer/analyzer-selftests.h
index 4bbb6c12ad9..5a8363ce031 100644
--- a/gcc/analyzer/analyzer-selftests.h
+++ b/gcc/analyzer/analyzer-selftests.h
@@ -35,13 +35,11 @@ extern void run_analyzer_selftests ();
    alphabetical order.  */
 extern void analyzer_checker_script_cc_tests ();
 extern void analyzer_constraint_manager_cc_tests ();
-extern void analyzer_constraint_manager2_cc_tests ();
 extern void analyzer_function_set_cc_tests ();
 extern void analyzer_immutable_hash_map_cc_tests ();
 extern void analyzer_program_point_cc_tests ();
 extern void analyzer_program_state_cc_tests ();
 extern void analyzer_region_model_cc_tests ();
-extern void analyzer_region_model2_cc_tests ();
 extern void analyzer_sm_file_cc_tests ();
 extern void analyzer_sm_signal_cc_tests ();
 extern void analyzer_store2_cc_tests ();
diff --git a/gcc/analyzer/checker-path.h b/gcc/analyzer/checker-path.h
index a8fa5147109..6654bd2dfd5 100644
--- a/gcc/analyzer/checker-path.h
+++ b/gcc/analyzer/checker-path.h
@@ -21,7 +21,7 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_ANALYZER_CHECKER_PATH_H
 #define GCC_ANALYZER_CHECKER_PATH_H
 
-#include "region-model2.h" // FIXME
+#include "region-model.h" // FIXME
 
 namespace ana {
 
diff --git a/gcc/analyzer/constraint-manager.cc b/gcc/analyzer/constraint-manager.cc
index 723a0c161b0..be51e79a4b9 100644
--- a/gcc/analyzer/constraint-manager.cc
+++ b/gcc/analyzer/constraint-manager.cc
@@ -45,19 +45,2936 @@ along with GCC; see the file COPYING3.  If not see
 #include "analyzer/constraint-manager.h"
 #include "analyzer/analyzer-selftests.h"
 
+/* TODO:
+   presumably best to drop equiv_class_id in favor of just equiv_class *,
+   so that there's no ordering.
+   But maybe a deeper reworking is needed?  */
+
 #if ENABLE_ANALYZER
 
-namespace ana {
+namespace ana {
+
+template <typename T>
+class one_way_id_map
+{
+ public:
+  one_way_id_map (int num_ids);
+  void put (T src, T dst);
+  T get_dst_for_src (T src) const;
+  void dump_to_pp (pretty_printer *pp) const;
+  void dump () const;
+  void update (T *) const;
+
+ private:
+  auto_vec<T> m_src_to_dst;
+ };
+
+/* class one_way_id_map.  */
+
+/* one_way_id_map's ctor, which populates the map with dummy null values.  */
+
+template <typename T>
+inline one_way_id_map<T>::one_way_id_map (int num_svalues)
+: m_src_to_dst (num_svalues)
+{
+  for (int i = 0; i < num_svalues; i++)
+    m_src_to_dst.quick_push (T::null ());
+}
+
+/* Record that SRC is to be mapped to DST.  */
+
+template <typename T>
+inline void
+one_way_id_map<T>::put (T src, T dst)
+{
+  m_src_to_dst[src.as_int ()] = dst;
+}
+
+/* Get the new value for SRC within the map.  */
+
+template <typename T>
+inline T
+one_way_id_map<T>::get_dst_for_src (T src) const
+{
+  if (src.null_p ())
+    return src;
+  return m_src_to_dst[src.as_int ()];
+}
+
+/* Dump this map to PP.  */
+
+template <typename T>
+inline void
+one_way_id_map<T>::dump_to_pp (pretty_printer *pp) const
+{
+  pp_string (pp, "src to dst: {");
+  unsigned i;
+  T *dst;
+  FOR_EACH_VEC_ELT (m_src_to_dst, i, dst)
+    {
+      if (i > 0)
+	pp_string (pp, ", ");
+      T src (T::from_int (i));
+      src.print (pp);
+      pp_string (pp, " -> ");
+      dst->print (pp);
+    }
+  pp_string (pp, "}");
+  pp_newline (pp);
+}
+
+/* Dump this map to stderr.  */
+
+template <typename T>
+DEBUG_FUNCTION inline void
+one_way_id_map<T>::dump () const
+{
+  pretty_printer pp;
+  pp.buffer->stream = stderr;
+  dump_to_pp (&pp);
+  pp_flush (&pp);
+}
+
+/* Update *ID from the old value to its new value in this map.  */
+
+template <typename T>
+inline void
+one_way_id_map<T>::update (T *id) const
+{
+  *id = get_dst_for_src (*id);
+}
+
+static tristate
+compare_constants (tree lhs_const, enum tree_code op, tree rhs_const)
+{
+  tree comparison
+    = fold_binary (op, boolean_type_node, lhs_const, rhs_const);
+  if (comparison == boolean_true_node)
+    return tristate (tristate::TS_TRUE);
+  if (comparison == boolean_false_node)
+    return tristate (tristate::TS_FALSE);
+  return tristate (tristate::TS_UNKNOWN);
+}
+
+/* struct bound2.  */
+
+/* Ensure that this bound2 is closed by converting an open bound2 to a
+   closed one.  */
+
+void
+bound2::ensure_closed (bool is_upper)
+{
+  if (!m_closed)
+    {
+      /* Offset by 1 in the appropriate direction.
+	 For example, convert 3 < x into 4 <= x,
+	 and convert x < 5 into x <= 4.  */
+      gcc_assert (CONSTANT_CLASS_P (m_constant));
+      m_constant = fold_build2 (is_upper ? MINUS_EXPR : PLUS_EXPR,
+				TREE_TYPE (m_constant),
+				m_constant, integer_one_node);
+      gcc_assert (CONSTANT_CLASS_P (m_constant));
+      m_closed = true;
+    }
+}
+
+/* Get "<=" vs "<" for this bound2.  */
+
+const char *
+bound2::get_relation_as_str () const
+{
+  if (m_closed)
+    return "<=";
+  else
+    return "<";
+}
+
+/* struct range2.  */
+
+/* Dump this range2 to PP, which must support %E for tree.  */
+
+void
+range2::dump_to_pp (pretty_printer *pp) const
+{
+  if (m_lower_bound.m_constant)
+    {
+      if (m_upper_bound.m_constant)
+	pp_printf (pp, "%qE %s x %s %qE",
+		   m_lower_bound.m_constant,
+		   m_lower_bound.get_relation_as_str (),
+		   m_upper_bound.get_relation_as_str (),
+		   m_upper_bound.m_constant);
+      else
+	pp_printf (pp, "%qE %s x",
+		   m_lower_bound.m_constant,
+		   m_lower_bound.get_relation_as_str ());
+    }
+  else
+    {
+      if (m_upper_bound.m_constant)
+	pp_printf (pp, "x %s %qE",
+		   m_upper_bound.get_relation_as_str (),
+		   m_upper_bound.m_constant);
+      else
+	pp_string (pp, "x");
+    }
+}
+
+/* Dump this range2 to stderr.  */
+
+DEBUG_FUNCTION void
+range2::dump () 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);
+  pp_newline (&pp);
+  pp_flush (&pp);
+}
+
+/* Determine if there is only one possible value for this range2.
+   If so, return the constant; otherwise, return NULL_TREE.  */
+
+tree
+range2::constrained_to_single_element ()
+{
+  if (m_lower_bound.m_constant == NULL_TREE
+      || m_upper_bound.m_constant == NULL_TREE)
+    return NULL_TREE;
+
+  if (!INTEGRAL_TYPE_P (TREE_TYPE (m_lower_bound.m_constant)))
+    return NULL_TREE;
+  if (!INTEGRAL_TYPE_P (TREE_TYPE (m_upper_bound.m_constant)))
+    return NULL_TREE;
+
+  /* Convert any open bounds to closed bounds.  */
+  m_lower_bound.ensure_closed (false);
+  m_upper_bound.ensure_closed (true);
+
+  // Are they equal?
+  tree comparison = fold_binary (EQ_EXPR, boolean_type_node,
+				 m_lower_bound.m_constant,
+				 m_upper_bound.m_constant);
+  if (comparison == boolean_true_node)
+    return m_lower_bound.m_constant;
+  else
+    return NULL_TREE;
+}
+
+/* Eval the condition "X OP RHS_CONST" for X within the range.  */
+
+tristate
+range2::eval_condition (enum tree_code op,
+			tree rhs_const) const
+{
+  range2 copy (*this);
+  if (tree single_element = copy.constrained_to_single_element ())
+    return compare_constants (single_element, op, rhs_const);
+
+  switch (op)
+    {
+    case EQ_EXPR:
+      if (below_lower_bound (rhs_const))
+	return tristate (tristate::TS_FALSE);
+      if (above_upper_bound (rhs_const))
+	return tristate (tristate::TS_FALSE);
+      break;
+
+    case LT_EXPR:
+      // TODO: distinction between LT and GE
+    case LE_EXPR:
+      /* Qn: "X < RHS_CONST".  */
+      /* If RHS_CONST > upper bound, then it's true.
+	 If RHS_CONST < lower bound, then it's false.
+	 Otherwise unknown.  */
+      if (above_upper_bound (rhs_const))
+	return tristate (tristate::TS_TRUE);
+      if (below_lower_bound (rhs_const))
+	return tristate (tristate::TS_FALSE);
+      break;
+
+    case NE_EXPR:
+      /* Qn: "X != RHS_CONST".  */
+      /* If RHS_CONST < lower bound, then it's true.
+	 If RHS_CONST > upper bound, then it's false.
+	 Otherwise unknown.  */
+      if (below_lower_bound (rhs_const))
+	return tristate (tristate::TS_TRUE);
+      if (above_upper_bound (rhs_const))
+	return tristate (tristate::TS_TRUE);
+      break;
+
+    case GE_EXPR:
+      // TODO: distinction between GE and GT
+    case GT_EXPR:
+      /* Qn: "X > RHS_CONST".  */
+      if (above_upper_bound (rhs_const))
+	return tristate (tristate::TS_FALSE);
+      if (below_lower_bound (rhs_const))
+	return tristate (tristate::TS_TRUE);
+      break;
+
+    default:
+      gcc_unreachable (); // TODO
+      break;
+    }
+  return tristate (tristate::TS_UNKNOWN);
+}
+
+/* Return true if RHS_CONST is below the lower bound of this range.  */
+
+bool
+range2::below_lower_bound (tree rhs_const) const
+{
+  if (!m_lower_bound.m_constant)
+    return false;
+
+  return compare_constants (rhs_const,
+			    m_lower_bound.m_closed ? LT_EXPR : LE_EXPR,
+			    m_lower_bound.m_constant).is_true ();
+}
+
+/* Return true if RHS_CONST is above the upper bound of this range.  */
+
+bool
+range2::above_upper_bound (tree rhs_const) const
+{
+  if (!m_upper_bound.m_constant)
+    return false;
+
+  return compare_constants (rhs_const,
+			    m_upper_bound.m_closed ? GT_EXPR : GE_EXPR,
+			    m_upper_bound.m_constant).is_true ();
+}
+
+/* class equiv_class2.  */
+
+/* equiv_class2's default ctor.  */
+
+equiv_class2::equiv_class2 ()
+: m_constant (NULL_TREE), m_cst_sval (NULL), m_vars ()
+{
+}
+
+/* equiv_class2's copy ctor.  */
+
+equiv_class2::equiv_class2 (const equiv_class2 &other)
+: m_constant (other.m_constant), m_cst_sval (other.m_cst_sval),
+  m_vars (other.m_vars.length ())
+{
+  int i;
+  const svalue2 *sval;
+  FOR_EACH_VEC_ELT (other.m_vars, i, sval)
+    m_vars.quick_push (sval);
+}
+
+/* Print an all-on-one-line representation of this equiv_class2 to PP,
+   which must support %E for trees.  */
+
+void
+equiv_class2::print (pretty_printer *pp) const
+{
+  pp_character (pp, '{');
+  int i;
+  const svalue2 *sval;
+  FOR_EACH_VEC_ELT (m_vars, i, sval)
+    {
+      if (i > 0)
+	pp_string (pp, " == ");
+      sval->dump_to_pp (pp, true);
+    }
+  if (m_constant)
+    {
+      if (i > 0)
+	pp_string (pp, " == ");
+      pp_printf (pp, "[m_constant]%qE", m_constant);
+    }
+  pp_character (pp, '}');
+}
+
+/* Generate a hash value for this equiv_class2.  */
+
+hashval_t
+equiv_class2::hash () const
+{
+  inchash::hash hstate;
+  int i;
+  const svalue2 *sval;
+
+  inchash::add_expr (m_constant, hstate);
+  // TODO:
+#if 0
+  FOR_EACH_VEC_ELT (m_vars, i, sval)
+    inchash::add (sval, hstate);
+#endif
+  return hstate.end ();
+}
+
+/* Equality operator for equiv_class2.  */
+
+bool
+equiv_class2::operator== (const equiv_class2 &other)
+{
+  if (m_constant != other.m_constant)
+    return false; // TODO: use tree equality here?
+
+  /* FIXME: should we compare m_cst_sval?  */
+
+  if (m_vars.length () != other.m_vars.length ())
+    return false;
+
+  int i;
+  const svalue2 *sval;
+  FOR_EACH_VEC_ELT (m_vars, i, sval)
+    if (sval != other.m_vars[i])
+      return false;
+  // FIXME: ^^^ doesn't this rely on ordering
+
+  return true;
+}
+
+/* Add SID to this equiv_class2, using CM to check if it's a constant.  */
+
+void
+equiv_class2::add (const svalue2 *sval)
+{
+  gcc_assert (sval);
+  if (tree cst = sval->maybe_get_constant ())
+    {
+      gcc_assert (CONSTANT_CLASS_P (cst));
+      /* FIXME: should we canonicalize which svalue2 is the constant
+	 when there are multiple equal constants?  */
+      m_constant = cst;
+      m_cst_sval = sval;
+    }
+  m_vars.safe_push (sval);
+}
+
+/* Remove SID from this equivalence class.
+   Return true if SID was the last var in the equivalence class (suggesting
+   a possible leak).  */
+
+bool
+equiv_class2::del (const svalue2 *sval)
+{
+  gcc_assert (sval);
+  gcc_assert (sval != m_cst_sval);
+
+  int i;
+  const svalue2 *iv;
+  FOR_EACH_VEC_ELT (m_vars, i, iv)
+    {
+      if (iv == sval)
+	{
+	  m_vars[i] = m_vars[m_vars.length () - 1];
+	  m_vars.pop ();
+	  return m_vars.length () == 0;
+	}
+    }
+
+  /* SVAL must be in the class.  */
+  gcc_unreachable ();
+  return false;
+}
+
+/* Get a representative member of this class, for handling cases
+   where the IDs can change mid-traversal.  */
+
+const svalue2 *
+equiv_class2::get_representative () const
+{
+  gcc_assert (m_vars.length () > 0);
+  return m_vars[0];
+}
+
+/* Comparator for use by equiv_class2::canonicalize.  */
+
+static int
+svalue2_cmp_by_ptr (const void *p1, const void *p2)
+{
+  const svalue2 *sval1 = *(const svalue2 * const *)p1;
+  const svalue2 *sval2 = *(const svalue2 * const *)p2;
+  if (sval1 < sval2)
+    return 1;
+  if (sval1 > sval2)
+    return -1;
+  return 0;
+}
+
+/* Sort the svalue2s within this equiv_class2.  */
+
+void
+equiv_class2::canonicalize ()
+{
+  m_vars.qsort (svalue2_cmp_by_ptr);
+}
+
+/* Get a debug string for C_OP.  */
+
+const char *
+constraint2_op_code (enum constraint2_op c_op)
+{
+  switch (c_op)
+    {
+    default:
+      gcc_unreachable ();
+    case CONSTRAINT2_NE: return "!=";
+    case CONSTRAINT2_LT: return "<";
+    case CONSTRAINT2_LE: return "<=";
+    }
+}
+
+/* Convert C_OP to an enum tree_code.  */
+
+enum tree_code
+constraint2_tree_code (enum constraint2_op c_op)
+{
+  switch (c_op)
+    {
+    default:
+      gcc_unreachable ();
+    case CONSTRAINT2_NE: return NE_EXPR;
+    case CONSTRAINT2_LT: return LT_EXPR;
+    case CONSTRAINT2_LE: return LE_EXPR;
+    }
+}
+
+/* Given "lhs C_OP rhs", determine "lhs T_OP rhs".
+
+   For example, given "x < y", then "x > y" is false.  */
+
+static tristate
+eval_constraint_op_for_op (enum constraint2_op c_op, enum tree_code t_op)
+{
+  switch (c_op)
+    {
+    default:
+      gcc_unreachable ();
+    case CONSTRAINT2_NE:
+      if (t_op == EQ_EXPR)
+	return tristate (tristate::TS_FALSE);
+      if (t_op == NE_EXPR)
+	return tristate (tristate::TS_TRUE);
+      break;
+    case CONSTRAINT2_LT:
+      if (t_op == LT_EXPR || t_op == LE_EXPR || t_op == NE_EXPR)
+	return tristate (tristate::TS_TRUE);
+      if (t_op == EQ_EXPR || t_op == GT_EXPR || t_op == GE_EXPR)
+	return tristate (tristate::TS_FALSE);
+      break;
+    case CONSTRAINT2_LE:
+      if (t_op == LE_EXPR)
+	return tristate (tristate::TS_TRUE);
+      if (t_op == GT_EXPR)
+	return tristate (tristate::TS_FALSE);
+      break;
+    }
+  return tristate (tristate::TS_UNKNOWN);
+}
+
+/* class constraint2.  */
+
+/* Print this constraint2 to PP (which must support %E for trees),
+   using CM to look up equiv_class2 instances from ids.  */
+
+void
+constraint2::print (pretty_printer *pp, const constraint_manager2 &cm) const
+{
+  m_lhs.print (pp);
+  pp_string (pp, ": ");
+  m_lhs.get_obj (cm).print (pp);
+  pp_string (pp, " ");
+  pp_string (pp, constraint2_op_code (m_op));
+  pp_string (pp, " ");
+  m_rhs.print (pp);
+  pp_string (pp, ": ");
+  m_rhs.get_obj (cm).print (pp);
+}
+
+/* Generate a hash value for this constraint2.  */
+
+hashval_t
+constraint2::hash () const
+{
+  inchash::hash hstate;
+  hstate.add_int (m_lhs.m_idx);
+  hstate.add_int (m_op);
+  hstate.add_int (m_rhs.m_idx);
+  return hstate.end ();
+}
+
+/* Equality operator for constraint2s.  */
+
+bool
+constraint2::operator== (const constraint2 &other) const
+{
+  if (m_lhs != other.m_lhs)
+    return false;
+  if (m_op != other.m_op)
+    return false;
+  if (m_rhs != other.m_rhs)
+    return false;
+  return true;
+}
+
+/* Return true if this constraint is implied by OTHER.  */
+
+bool
+constraint2::implied_by (const constraint2 &other,
+			 const constraint_manager2 &cm) const
+{
+  if (m_lhs == other.m_lhs)
+    if (tree rhs_const = m_rhs.get_obj (cm).get_any_constant ())
+      if (tree other_rhs_const = other.m_rhs.get_obj (cm).get_any_constant ())
+	if (m_lhs.get_obj (cm).get_any_constant () == NULL_TREE)
+	  if (m_op == other.m_op)
+	    switch (m_op)
+	      {
+	      default:
+		break;
+	      case CONSTRAINT2_LE:
+	      case CONSTRAINT2_LT:
+		if (compare_constants (rhs_const,
+				       GE_EXPR,
+				       other_rhs_const).is_true ())
+		  return true;
+		break;
+	      }
+  return false;
+}
+
+/* class equiv_class2_id.  */
+
+/* Get the underlying equiv_class2 for this ID from CM.  */
+
+const equiv_class2 &
+equiv_class2_id::get_obj (const constraint_manager2 &cm) const
+{
+  return cm.get_equiv_class2_by_index (m_idx);
+}
+
+/* Access the underlying equiv_class2 for this ID from CM.  */
+
+equiv_class2 &
+equiv_class2_id::get_obj (constraint_manager2 &cm) const
+{
+  return cm.get_equiv_class2_by_index (m_idx);
+}
+
+/* Print this equiv_class2_id to PP.  */
+
+void
+equiv_class2_id::print (pretty_printer *pp) const
+{
+  if (null_p ())
+    pp_printf (pp, "null");
+  else
+    pp_printf (pp, "ec%i", m_idx);
+}
+
+/* class constraint_manager2.  */
+
+/* constraint_manager2's copy ctor.  */
+
+constraint_manager2::constraint_manager2 (const constraint_manager2 &other)
+: m_equiv_classes (other.m_equiv_classes.length ()),
+  m_constraints (other.m_constraints.length ()),
+  m_mgr (other.m_mgr)
+{
+  int i;
+  equiv_class2 *ec;
+  FOR_EACH_VEC_ELT (other.m_equiv_classes, i, ec)
+    m_equiv_classes.quick_push (new equiv_class2 (*ec));
+  constraint2 *c;
+  FOR_EACH_VEC_ELT (other.m_constraints, i, c)
+    m_constraints.quick_push (*c);
+}
+
+/* constraint_manager2's assignment operator.  */
+
+constraint_manager2&
+constraint_manager2::operator= (const constraint_manager2 &other)
+{
+  gcc_assert (m_equiv_classes.length () == 0);
+  gcc_assert (m_constraints.length () == 0);
+
+  int i;
+  equiv_class2 *ec;
+  m_equiv_classes.reserve (other.m_equiv_classes.length ());
+  FOR_EACH_VEC_ELT (other.m_equiv_classes, i, ec)
+    m_equiv_classes.quick_push (new equiv_class2 (*ec));
+  constraint2 *c;
+  m_constraints.reserve (other.m_constraints.length ());
+  FOR_EACH_VEC_ELT (other.m_constraints, i, c)
+    m_constraints.quick_push (*c);
+
+  return *this;
+}
+
+/* Generate a hash value for this constraint_manager2.  */
+
+hashval_t
+constraint_manager2::hash () const
+{
+  inchash::hash hstate;
+  int i;
+  equiv_class2 *ec;
+  constraint2 *c;
+
+  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+    hstate.merge_hash (ec->hash ());
+  FOR_EACH_VEC_ELT (m_constraints, i, c)
+    hstate.merge_hash (c->hash ());
+  return hstate.end ();
+}
+
+/* Equality operator for constraint_manager2.  */
+
+bool
+constraint_manager2::operator== (const constraint_manager2 &other) const
+{
+  if (m_equiv_classes.length () != other.m_equiv_classes.length ())
+    return false;
+  if (m_constraints.length () != other.m_constraints.length ())
+    return false;
+
+  int i;
+  equiv_class2 *ec;
+
+  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+    if (!(*ec == *other.m_equiv_classes[i]))
+      return false;
+
+  constraint2 *c;
+
+  FOR_EACH_VEC_ELT (m_constraints, i, c)
+    if (!(*c == other.m_constraints[i]))
+      return false;
+
+  return true;
+}
+
+/* Print this constraint_manager2 to PP (which must support %E for trees).  */
+
+void
+constraint_manager2::print (pretty_printer *pp) const
+{
+  pp_string (pp, "{");
+  int i;
+  equiv_class2 *ec;
+  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+    {
+      if (i > 0)
+	pp_string (pp, ", ");
+      equiv_class2_id (i).print (pp);
+      pp_string (pp, ": ");
+      ec->print (pp);
+    }
+  pp_string (pp, "  |  ");
+  constraint2 *c;
+  FOR_EACH_VEC_ELT (m_constraints, i, c)
+    {
+      if (i > 0)
+	pp_string (pp, " && ");
+      c->print (pp, *this);
+    }
+  pp_printf (pp, "}");
+}
+
+/* Dump a representation of this constraint_manager2 to PP
+   (which must support %E for trees).  */
+
+void
+constraint_manager2::dump_to_pp (pretty_printer *pp, bool multiline) const
+{
+  // TODO
+  if (multiline)
+    pp_string (pp, "  ");
+  pp_string (pp, "equiv classes:");
+  if (multiline)
+    pp_newline (pp);
+  else
+    pp_string (pp, " {");
+  int i;
+  equiv_class2 *ec;
+  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+    {
+      if (multiline)
+	pp_string (pp, "    ");
+      else if (i > 0)
+	pp_string (pp, ", ");
+      equiv_class2_id (i).print (pp);
+      pp_string (pp, ": ");
+      ec->print (pp);
+      if (multiline)
+	pp_newline (pp);
+    }
+  if (multiline)
+    pp_string (pp, "  ");
+  else
+    pp_string (pp, "}");
+  pp_string (pp, "constraints:");
+  if (multiline)
+    pp_newline (pp);
+  else
+    pp_string (pp, "{");
+  constraint2 *c;
+  FOR_EACH_VEC_ELT (m_constraints, i, c)
+    {
+      if (multiline)
+	pp_string (pp, "    ");
+      pp_printf (pp, "%i: ", i);
+      c->print (pp, *this);
+      if (multiline)
+	pp_newline (pp);
+    }
+  if (!multiline)
+    pp_string (pp, "}");
+}
+
+/* Dump a multiline representation of this constraint_manager2 to FP.  */
+
+void
+constraint_manager2::dump (FILE *fp) const
+{
+  pretty_printer pp;
+  pp_format_decoder (&pp) = default_tree_printer;
+  pp_show_color (&pp) = pp_show_color (global_dc->printer);
+  pp.buffer->stream = fp;
+  dump_to_pp (&pp, true);
+  pp_flush (&pp);
+}
+
+/* Dump a multiline representation of this constraint_manager2 to stderr.  */
+
+DEBUG_FUNCTION void
+constraint_manager2::dump () const
+{
+  dump (stderr);
+}
+
+/* Dump a multiline representation of CM to stderr.  */
+
+DEBUG_FUNCTION void
+debug (const constraint_manager2 &cm)
+{
+  cm.dump ();
+}
+
+/* Attempt to add the constraint LHS OP RHS to this constraint_manager2.
+   Return true if the constraint could be added (or is already true).
+   Return false if the constraint contradicts existing knowledge.  */
+
+bool
+constraint_manager2::add_constraint (const svalue2 *lhs,
+				     enum tree_code op,
+				     const svalue2 *rhs)
+{
+  lhs = lhs->unwrap_any_unmergeable ();
+  rhs = rhs->unwrap_any_unmergeable ();
+
+  /* Nothing can be known about unknown values.  */
+  if (lhs->get_kind () == svalue2::SK_UNKNOWN
+      || rhs->get_kind () == svalue2::SK_UNKNOWN)
+    /* Not a contradiction.  */
+    return true;
+
+  /* Check the conditions on svalues.  */
+  {
+    tristate t_cond = eval_condition (lhs, op, rhs);
+
+    /* If we already have the condition, do nothing.  */
+    if (t_cond.is_true ())
+      return true;
+
+    /* Reject a constraint that would contradict existing knowledge, as
+       unsatisfiable.  */
+    if (t_cond.is_false ())
+      return false;
+  }
+
+  equiv_class2_id lhs_ec_id = get_or_add_equiv_class2 (lhs);
+  equiv_class2_id rhs_ec_id = get_or_add_equiv_class2 (rhs);
+
+  /* Check the stronger conditions on ECs.  */
+  {
+    tristate t = eval_condition (lhs_ec_id, op, rhs_ec_id);
+
+    /* Discard constraints that are already known.  */
+    if (t.is_true ())
+      return true;
+
+    /* Reject unsatisfiable constraints.  */
+    if (t.is_false ())
+      return false;
+  }
+
+  add_unknown_constraint (lhs_ec_id, op, rhs_ec_id);
+  return true;
+}
+
+/* Attempt to add the constraint LHS_EC_ID OP RHS_EC_ID to this
+   constraint_manager2.
+   Return true if the constraint could be added (or is already true).
+   Return false if the constraint contradicts existing knowledge.  */
+
+bool
+constraint_manager2::add_constraint (equiv_class2_id lhs_ec_id,
+				     enum tree_code op,
+				     equiv_class2_id rhs_ec_id)
+{
+  tristate t = eval_condition (lhs_ec_id, op, rhs_ec_id);
+
+  /* Discard constraints that are already known.  */
+  if (t.is_true ())
+    return true;
+
+  /* Reject unsatisfiable constraints.  */
+  if (t.is_false ())
+    return false;
+
+  add_unknown_constraint (lhs_ec_id, op, rhs_ec_id);
+  return true;
+}
+
+/* Add the constraint LHS_EC_ID OP RHS_EC_ID to this constraint_manager2,
+   where the constraint has already been checked for being "unknown".  */
+
+void
+constraint_manager2::add_unknown_constraint (equiv_class2_id lhs_ec_id,
+					     enum tree_code op,
+					     equiv_class2_id rhs_ec_id)
+{
+  gcc_assert (lhs_ec_id != rhs_ec_id);
+
+  /* For now, simply accumulate constraints, without attempting any further
+     optimization.  */
+  switch (op)
+    {
+    case EQ_EXPR:
+      {
+	/* Merge rhs_ec into lhs_ec.  */
+	equiv_class2 &lhs_ec_obj = lhs_ec_id.get_obj (*this);
+	const equiv_class2 &rhs_ec_obj = rhs_ec_id.get_obj (*this);
+
+	int i;
+	const svalue2 *sval;
+	FOR_EACH_VEC_ELT (rhs_ec_obj.m_vars, i, sval)
+	  lhs_ec_obj.add (sval);
+
+	if (rhs_ec_obj.m_constant)
+	  {
+	    lhs_ec_obj.m_constant = rhs_ec_obj.m_constant;
+	    lhs_ec_obj.m_cst_sval = rhs_ec_obj.m_cst_sval;
+	  }
+
+	/* Drop rhs equivalence class, overwriting it with the
+	   final ec (which might be the same one).  */
+	equiv_class2_id final_ec_id = m_equiv_classes.length () - 1;
+	equiv_class2 *old_ec = m_equiv_classes[rhs_ec_id.m_idx];
+	equiv_class2 *final_ec = m_equiv_classes.pop ();
+	if (final_ec != old_ec)
+	  m_equiv_classes[rhs_ec_id.m_idx] = final_ec;
+	delete old_ec;
+
+	/* Update the constraints.  */
+	constraint2 *c;
+	FOR_EACH_VEC_ELT (m_constraints, i, c)
+	  {
+	    /* Update references to the rhs_ec so that
+	       they refer to the lhs_ec.  */
+	    if (c->m_lhs == rhs_ec_id)
+	      c->m_lhs = lhs_ec_id;
+	    if (c->m_rhs == rhs_ec_id)
+	      c->m_rhs = lhs_ec_id;
+
+	    /* Renumber all constraints that refer to the final rhs_ec
+	       to the old rhs_ec, where the old final_ec now lives.  */
+	    if (c->m_lhs == final_ec_id)
+	      c->m_lhs = rhs_ec_id;
+	    if (c->m_rhs == final_ec_id)
+	      c->m_rhs = rhs_ec_id;
+	  }
+
+	/* We may now have self-comparisons due to the merger; these
+	   constraints should be removed.  */
+	unsigned read_index, write_index;
+	VEC_ORDERED_REMOVE_IF (m_constraints, read_index, write_index, c,
+			       (c->m_lhs == c->m_rhs));
+      }
+      break;
+    case GE_EXPR:
+      add_constraint_internal (rhs_ec_id, CONSTRAINT2_LE, lhs_ec_id);
+      break;
+    case LE_EXPR:
+      add_constraint_internal (lhs_ec_id, CONSTRAINT2_LE, rhs_ec_id);
+      break;
+    case NE_EXPR:
+      add_constraint_internal (lhs_ec_id, CONSTRAINT2_NE, rhs_ec_id);
+      break;
+    case GT_EXPR:
+      add_constraint_internal (rhs_ec_id, CONSTRAINT2_LT, lhs_ec_id);
+      break;
+    case LT_EXPR:
+      add_constraint_internal (lhs_ec_id, CONSTRAINT2_LT, rhs_ec_id);
+      break;
+    default:
+      /* do nothing.  */
+      break;
+    }
+  validate ();
+}
+
+/* Subroutine of constraint_manager2::add_constraint, for handling all
+   operations other than equality (for which equiv classes are merged).  */
+
+void
+constraint_manager2::add_constraint_internal (equiv_class2_id lhs_id,
+					      enum constraint2_op c_op,
+					      equiv_class2_id rhs_id)
+{
+  constraint2 new_c (lhs_id, c_op, rhs_id);
+
+  /* Remove existing constraints that would be implied by the
+     new constraint.  */
+  unsigned read_index, write_index;
+  constraint2 *c;
+  VEC_ORDERED_REMOVE_IF (m_constraints, read_index, write_index, c,
+			 (c->implied_by (new_c, *this)));
+
+  /* Add the constraint.  */
+  m_constraints.safe_push (new_c);
+
+  if (!flag_analyzer_transitivity)
+    return;
+
+  if (c_op != CONSTRAINT2_NE)
+    {
+      /* The following can potentially add EQ_EXPR facts, which could lead
+	 to ECs being merged, which would change the meaning of the EC IDs.
+	 Hence we need to do this via representatives.  */
+      const svalue2 *lhs = lhs_id.get_obj (*this).get_representative ();
+      const svalue2 *rhs = rhs_id.get_obj (*this).get_representative ();
+
+      /* We have LHS </<= RHS */
+
+      /* Handle transitivity of ordering by adding additional constraints
+	 based on what we already knew.
+
+	 So if we have already have:
+	   (a < b)
+	   (c < d)
+	 Then adding:
+	   (b < c)
+	 will also add:
+	   (a < c)
+	   (b < d)
+	 We need to recurse to ensure we also add:
+	   (a < d).
+	 We call the checked add_constraint to avoid adding constraints
+	 that are already present.  Doing so also ensures termination
+	 in the case of cycles.
+
+	 We also check for single-element ranges, adding EQ_EXPR facts
+	 where we discover them.  For example 3 < x < 5 implies
+	 that x == 4 (if x is an integer).  */
+      for (unsigned i = 0; i < m_constraints.length (); i++)
+	{
+	  const constraint2 *other = &m_constraints[i];
+	  if (other->is_ordering_p ())
+	    {
+	      /* Refresh the EC IDs, in case any mergers have happened.  */
+	      lhs_id = get_or_add_equiv_class2 (lhs);
+	      rhs_id = get_or_add_equiv_class2 (rhs);
+
+	      tree lhs_const = lhs_id.get_obj (*this).m_constant;
+	      tree rhs_const = rhs_id.get_obj (*this).m_constant;
+	      tree other_lhs_const
+		= other->m_lhs.get_obj (*this).m_constant;
+	      tree other_rhs_const
+		= other->m_rhs.get_obj (*this).m_constant;
+
+	      /* We have "LHS </<= RHS" and "other.lhs </<= other.rhs".  */
+
+	      /* If we have LHS </<= RHS and RHS </<= LHS, then we have a
+		 cycle.  */
+	      if (rhs_id == other->m_lhs
+		  && other->m_rhs == lhs_id)
+		{
+		  /* We must have equality for this to be possible.  */
+		  gcc_assert (c_op == CONSTRAINT2_LE
+			      && other->m_op == CONSTRAINT2_LE);
+		  add_constraint (lhs_id, EQ_EXPR, rhs_id);
+		  /* Adding an equality will merge the two ECs and potentially
+		     reorganize the constraints.  Stop iterating.  */
+		  return;
+		}
+	      /* Otherwise, check for transitivity.  */
+	      if (rhs_id == other->m_lhs)
+		{
+		  /* With RHS == other.lhs, we have:
+		     "LHS </<= (RHS, other.lhs) </<= other.rhs"
+		     and thus this implies "LHS </<= other.rhs".  */
+
+		  /* Do we have a tightly-constrained range?  */
+		  if (lhs_const
+		      && !rhs_const
+		      && other_rhs_const)
+		    {
+		      range2 r (bound2 (lhs_const, c_op == CONSTRAINT2_LE),
+			       bound2 (other_rhs_const,
+				      other->m_op == CONSTRAINT2_LE));
+		      if (tree constant = r.constrained_to_single_element ())
+			{
+			  const svalue2 *cst_sval
+			    = m_mgr->get_or_create_constant_svalue2 (constant);
+			  add_constraint
+			    (rhs_id, EQ_EXPR,
+			     get_or_add_equiv_class2 (cst_sval));
+			  return;
+			}
+		    }
+
+		  /* Otherwise, add the constraint implied by transitivity.  */
+		  enum tree_code new_op
+		    = ((c_op == CONSTRAINT2_LE && other->m_op == CONSTRAINT2_LE)
+		       ? LE_EXPR : LT_EXPR);
+		  add_constraint (lhs_id, new_op, other->m_rhs);
+		}
+	      else if (other->m_rhs == lhs_id)
+		{
+		  /* With other.rhs == LHS, we have:
+		     "other.lhs </<= (other.rhs, LHS) </<= RHS"
+		     and thus this implies "other.lhs </<= RHS".  */
+
+		  /* Do we have a tightly-constrained range?  */
+		  if (other_lhs_const
+		      && !lhs_const
+		      && rhs_const)
+		    {
+		      range2 r (bound2 (other_lhs_const,
+				      other->m_op == CONSTRAINT2_LE),
+			       bound2 (rhs_const,
+				      c_op == CONSTRAINT2_LE));
+		      if (tree constant = r.constrained_to_single_element ())
+			{
+			  const svalue2 *cst_sval
+			    = m_mgr->get_or_create_constant_svalue2 (constant);
+			  add_constraint
+			    (lhs_id, EQ_EXPR,
+			     get_or_add_equiv_class2 (cst_sval));
+			  return;
+			}
+		    }
+
+		  /* Otherwise, add the constraint implied by transitivity.  */
+		  enum tree_code new_op
+		    = ((c_op == CONSTRAINT2_LE && other->m_op == CONSTRAINT2_LE)
+		       ? LE_EXPR : LT_EXPR);
+		  add_constraint (other->m_lhs, new_op, rhs_id);
+		}
+	    }
+	}
+    }
+}
+
+/* Look for SVAL within the equivalence classes of this constraint_manager2;
+   if found, return true, writing the id to *OUT if OUT is non-NULL,
+   otherwise return false.  */
+
+bool
+constraint_manager2::get_equiv_class2_by_svalue2 (const svalue2 *sval,
+						  equiv_class2_id *out) const
+{
+  /* TODO: should we have a map, rather than these searches?  */
+  int i;
+  equiv_class2 *ec;
+  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+    {
+      int j;
+      const svalue2 *iv;
+      FOR_EACH_VEC_ELT (ec->m_vars, j, iv)
+	if (iv == sval)
+	  {
+	    if (out)
+	      *out = equiv_class2_id (i);
+	    return true;
+	  }
+    }
+  return false;
+}
+
+/* Ensure that SVAL has an equivalence class within this constraint_manager2;
+   return the ID of the class.  */
+
+equiv_class2_id
+constraint_manager2::get_or_add_equiv_class2 (const svalue2 *sval)
+{
+  equiv_class2_id result (-1);
+
+  gcc_assert (sval->get_kind () != svalue2::SK_UNKNOWN);
+
+  /* Convert all NULL pointers to (void *) to avoid state explosions
+     involving all of the various (foo *)NULL vs (bar *)NULL.  */
+#if NULL_POINTER_TYPE_CONSOLIDATION
+  if (POINTER_TYPE_P (sval->get_type ()))
+    if (tree cst = sval->maybe_get_constant ())
+      if (zerop (cst))
+	sval = m_mgr->get_or_create_constant_svalue2 (null_pointer_node);
+#endif
+
+  /* Try svalue2 match.  */
+  if (get_equiv_class2_by_svalue2 (sval, &result))
+    return result;
+
+  /* Try equality of constants.  */
+  if (tree cst = sval->maybe_get_constant ())
+    {
+      int i;
+      equiv_class2 *ec;
+      FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+	if (ec->m_constant
+	    && types_compatible_p (TREE_TYPE (cst),
+				   TREE_TYPE (ec->m_constant)))
+	  {
+	    tree eq = fold_binary (EQ_EXPR, boolean_type_node,
+				   cst, ec->m_constant);
+	    if (eq == boolean_true_node)
+	      {
+		ec->add (sval);
+		return equiv_class2_id (i);
+	      }
+	  }
+    }
+
+
+  /* Not found.  */
+  equiv_class2 *new_ec = new equiv_class2 ();
+  new_ec->add (sval);
+  m_equiv_classes.safe_push (new_ec);
+
+  equiv_class2_id new_id (m_equiv_classes.length () - 1);
+
+  if (sval->maybe_get_constant ())
+    {
+      /* If we have a new EC for a constant, add constraints comparing this
+	 to other constants we may have (so that we accumulate the transitive
+	 closure of all constraints on constants as the constants are
+	 added).  */
+      for (equiv_class2_id other_id (0); other_id.m_idx < new_id.m_idx;
+	   other_id.m_idx++)
+	{
+	  const equiv_class2 &other_ec = other_id.get_obj (*this);
+	  if (other_ec.m_constant
+	      && types_compatible_p (TREE_TYPE (new_ec->m_constant),
+				     TREE_TYPE (other_ec.m_constant)))
+	    {
+	      /* If we have two ECs, both with constants, the constants must be
+		 non-equal (or they would be in the same EC).
+		 Determine the direction of the inequality, and record that
+		 fact.  */
+	      tree lt
+		= fold_binary (LT_EXPR, boolean_type_node,
+			       new_ec->m_constant, other_ec.m_constant);
+	      if (lt == boolean_true_node)
+		add_constraint_internal (new_id, CONSTRAINT2_LT, other_id);
+	      else if (lt == boolean_false_node)
+		add_constraint_internal (other_id, CONSTRAINT2_LT, new_id);
+	      /* Refresh new_id, in case ECs were merged.  SVAL should always
+		 be present by now, so this should never lead to a
+		 recursion.  */
+	      new_id = get_or_add_equiv_class2 (sval);
+	    }
+	}
+    }
+
+  return new_id;
+}
+
+/* Evaluate the condition LHS_EC OP RHS_EC.  */
+
+tristate
+constraint_manager2::eval_condition (equiv_class2_id lhs_ec,
+				    enum tree_code op,
+				    equiv_class2_id rhs_ec) const
+{
+  if (lhs_ec == rhs_ec)
+    {
+      switch (op)
+	{
+	case EQ_EXPR:
+	case GE_EXPR:
+	case LE_EXPR:
+	  return tristate (tristate::TS_TRUE);
+
+	case NE_EXPR:
+	case GT_EXPR:
+	case LT_EXPR:
+	  return tristate (tristate::TS_FALSE);
+	default:
+	  break;
+	}
+    }
+
+  tree lhs_const = lhs_ec.get_obj (*this).get_any_constant ();
+  tree rhs_const = rhs_ec.get_obj (*this).get_any_constant ();
+  if (lhs_const && rhs_const)
+    {
+      tristate result_for_constants
+	= compare_constants (lhs_const, op, rhs_const);
+      if (result_for_constants.is_known ())
+	return result_for_constants;
+    }
+
+  enum tree_code swapped_op = swap_tree_comparison (op);
+
+  int i;
+  constraint2 *c;
+  FOR_EACH_VEC_ELT (m_constraints, i, c)
+    {
+      if (c->m_lhs == lhs_ec
+	  && c->m_rhs == rhs_ec)
+	{
+	  tristate result_for_constraint
+	    = eval_constraint_op_for_op (c->m_op, op);
+	  if (result_for_constraint.is_known ())
+	    return result_for_constraint;
+	}
+      /* Swapped operands.  */
+      if (c->m_lhs == rhs_ec
+	  && c->m_rhs == lhs_ec)
+	{
+	  tristate result_for_constraint
+	    = eval_constraint_op_for_op (c->m_op, swapped_op);
+	  if (result_for_constraint.is_known ())
+	    return result_for_constraint;
+	}
+    }
+
+  return tristate (tristate::TS_UNKNOWN);
+}
+
+range2
+constraint_manager2::get_ec_bounds (equiv_class2_id ec_id) const
+{
+  range2 result;
+
+  int i;
+  constraint2 *c;
+  FOR_EACH_VEC_ELT (m_constraints, i, c)
+    {
+      if (c->m_lhs == ec_id)
+	{
+	  if (tree other_cst = c->m_rhs.get_obj (*this).get_any_constant ())
+	    switch (c->m_op)
+	      {
+	      default:
+		gcc_unreachable ();
+	      case CONSTRAINT2_NE:
+		continue;
+
+	      case CONSTRAINT2_LT:
+		/* We have "EC_ID < OTHER_CST".  */
+		result.m_upper_bound = bound2 (other_cst, false);
+		break;
+
+	      case CONSTRAINT2_LE:
+		/* We have "EC_ID <= OTHER_CST".  */
+		result.m_upper_bound = bound2 (other_cst, true);
+		break;
+	      }
+	}
+      if (c->m_rhs == ec_id)
+	{
+	  if (tree other_cst = c->m_lhs.get_obj (*this).get_any_constant ())
+	    switch (c->m_op)
+	      {
+	      default:
+		gcc_unreachable ();
+	      case CONSTRAINT2_NE:
+		continue;
+
+	      case CONSTRAINT2_LT:
+		/* We have "OTHER_CST < EC_ID"
+		   i.e. "EC_ID > OTHER_CST".  */
+		result.m_lower_bound = bound2 (other_cst, false);
+		break;
+
+	      case CONSTRAINT2_LE:
+		/* We have "OTHER_CST <= EC_ID"
+		   i.e. "EC_ID >= OTHER_CST".  */
+		result.m_lower_bound = bound2 (other_cst, true);
+		break;
+	      }
+	}
+    }
+
+  return result;
+}
+
+
+/* Evaluate the condition LHS_EC OP RHS_CONST, avoiding the creation
+   of equiv_class2 instances.  */
+
+tristate
+constraint_manager2::eval_condition (equiv_class2_id lhs_ec,
+				     enum tree_code op,
+				     tree rhs_const) const
+{
+  gcc_assert (!lhs_ec.null_p ());
+  gcc_assert (CONSTANT_CLASS_P (rhs_const));
+
+  if (tree lhs_const = lhs_ec.get_obj (*this).get_any_constant ())
+    return compare_constants (lhs_const, op, rhs_const);
+
+  /* Check for known inequalities of the form
+       (LHS_EC != OTHER_CST) or (OTHER_CST != LHS_EC).
+     If RHS_CONST == OTHER_CST, then we also know that LHS_EC != OTHER_CST.
+     For example, we might have the constraint
+       ptr != (void *)0
+     so we want the condition
+       ptr == (foo *)0
+     to be false.  */
+  int i;
+  constraint2 *c;
+  FOR_EACH_VEC_ELT (m_constraints, i, c)
+    {
+      if (c->m_op == CONSTRAINT2_NE)
+	{
+	  if (c->m_lhs == lhs_ec)
+	    {
+	      if (tree other_cst = c->m_rhs.get_obj (*this).get_any_constant ())
+		if (compare_constants
+		      (rhs_const, EQ_EXPR, other_cst).is_true ())
+		  {
+		    switch (op)
+		      {
+		      case EQ_EXPR:
+			return tristate (tristate::TS_FALSE);
+		      case NE_EXPR:
+			return tristate (tristate::TS_TRUE);
+		      default:
+			break;
+		      }
+		  }
+	    }
+	  if (c->m_rhs == lhs_ec)
+	    {
+	      if (tree other_cst = c->m_lhs.get_obj (*this).get_any_constant ())
+		if (compare_constants
+		      (rhs_const, EQ_EXPR, other_cst).is_true ())
+		  {
+		    switch (op)
+		      {
+		      case EQ_EXPR:
+			return tristate (tristate::TS_FALSE);
+		      case NE_EXPR:
+			return tristate (tristate::TS_TRUE);
+		      default:
+			break;
+		      }
+		  }
+	    }
+	}
+    }
+  /* Look at existing bounds on LHS_EC.  */
+  range2 lhs_bounds = get_ec_bounds (lhs_ec);
+  return lhs_bounds.eval_condition (op, rhs_const);
+}
+
+// FIXME:
+/* Evaluate the condition LHS OP RHS, avoiding the creation
+   of equiv_class2 instances.  */
+
+tristate
+constraint_manager2::eval_condition (const svalue2 *lhs,
+				     enum tree_code op,
+				     const svalue2 *rhs) const
+{
+  lhs = lhs->unwrap_any_unmergeable ();
+  rhs = rhs->unwrap_any_unmergeable ();
+
+  /* Nothing can be known about unknown or poisoned values.  */
+  if (lhs->get_kind () == svalue2::SK_UNKNOWN
+      || lhs->get_kind () == svalue2::SK_POISONED
+      || rhs->get_kind () == svalue2::SK_UNKNOWN
+      || rhs->get_kind () == svalue2::SK_POISONED)
+    return tristate (tristate::TS_UNKNOWN);
+
+  if (lhs == rhs
+      && !(FLOAT_TYPE_P (lhs->get_type ())
+	   || FLOAT_TYPE_P (rhs->get_type ())))
+    {
+      switch (op)
+	{
+	case EQ_EXPR:
+	case GE_EXPR:
+	case LE_EXPR:
+	  return tristate (tristate::TS_TRUE);
+
+	case NE_EXPR:
+	case GT_EXPR:
+	case LT_EXPR:
+	  return tristate (tristate::TS_FALSE);
+	default:
+	  break;
+	}
+    }
+
+  equiv_class2_id lhs_ec (-1);
+  equiv_class2_id rhs_ec (-1);
+  get_equiv_class2_by_svalue2 (lhs, &lhs_ec);
+  get_equiv_class2_by_svalue2 (rhs, &rhs_ec);
+  if (!lhs_ec.null_p () && !rhs_ec.null_p ())
+    {
+      tristate result_for_ecs
+	= eval_condition (lhs_ec, op, rhs_ec);
+      if (result_for_ecs.is_known ())
+	return result_for_ecs;
+    }
+
+  /* If at least one is not in an EC, we have no constraints
+     comparing LHS and RHS yet.
+     They might still be comparable if one (or both) is a constant.
+
+     Alternatively, we can also get here if we had ECs but they weren't
+     comparable.  Again, constant comparisons might give an answer.  */
+  tree lhs_const = lhs->maybe_get_constant ();
+  tree rhs_const = rhs->maybe_get_constant ();
+  // TODO: enable this:
+#if 0
+  if (lhs_const && rhs_const)
+    {
+      tristate result_for_constants
+	= compare_constants (lhs_const, op, rhs_const);
+      if (result_for_constants.is_known ())
+	return result_for_constants;
+    }
+#endif
+
+  if (!lhs_ec.null_p ())
+    {
+      if (rhs_const)
+	return eval_condition (lhs_ec, op, rhs_const);
+    }
+  if (!rhs_ec.null_p ())
+    {
+      if (lhs_const)
+	{
+	  enum tree_code swapped_op = swap_tree_comparison (op);
+	  return eval_condition (rhs_ec, swapped_op, lhs_const);
+	}
+    }
+
+  return tristate (tristate::TS_UNKNOWN);
+}
+
+/* Delete any information about svalue2_id instances identified by P.
+   Such instances are removed from equivalence classes, and any
+   redundant ECs and constraints are also removed.
+   Accumulate stats into STATS.  */
+
+template <typename PurgeCriteria>
+void
+constraint_manager2::purge (const PurgeCriteria &p, purge_stats2 *stats)
+{
+  /* Delete any svalue2_ids identified by P within the various equivalence
+     classes.  */
+  for (unsigned ec_idx = 0; ec_idx < m_equiv_classes.length (); )
+    {
+      equiv_class2 *ec = m_equiv_classes[ec_idx];
+
+      int i;
+      const svalue2 *sval;
+      bool delete_ec = false;
+      FOR_EACH_VEC_ELT (ec->m_vars, i, sval)
+	{
+	  if (sval == ec->m_cst_sval)
+	    continue;
+	  if (p.should_purge_p (sval))
+	    {
+	      if (ec->del (sval))
+		if (!ec->m_constant)
+		  delete_ec = true;
+	    }
+	}
+
+      if (delete_ec)
+	{
+	  delete ec;
+	  m_equiv_classes.ordered_remove (ec_idx);
+	  if (stats)
+	    stats->m_num_equiv_classes++;
+
+	  /* Update the constraints, potentially removing some.  */
+	  for (unsigned con_idx = 0; con_idx < m_constraints.length (); )
+	    {
+	      constraint2 *c = &m_constraints[con_idx];
+
+	      /* Remove constraints that refer to the deleted EC.  */
+	      if (c->m_lhs == ec_idx
+		  || c->m_rhs == ec_idx)
+		{
+		  m_constraints.ordered_remove (con_idx);
+		  if (stats)
+		    stats->m_num_constraints++;
+		}
+	      else
+		{
+		  /* Renumber constraints that refer to ECs that have
+		     had their idx changed.  */
+		  c->m_lhs.update_for_removal (ec_idx);
+		  c->m_rhs.update_for_removal (ec_idx);
+
+		  con_idx++;
+		}
+	    }
+	}
+      else
+	ec_idx++;
+    }
+
+  /* Now delete any constraints that are purely between constants.  */
+  for (unsigned con_idx = 0; con_idx < m_constraints.length (); )
+    {
+      constraint2 *c = &m_constraints[con_idx];
+      if (m_equiv_classes[c->m_lhs.m_idx]->m_vars.length () == 0
+	  && m_equiv_classes[c->m_rhs.m_idx]->m_vars.length () == 0)
+	{
+	  m_constraints.ordered_remove (con_idx);
+	  if (stats)
+	    stats->m_num_constraints++;
+	}
+      else
+	{
+	  con_idx++;
+	}
+    }
+
+  /* Finally, delete any ECs that purely contain constants and aren't
+     referenced by any constraints.  */
+  for (unsigned ec_idx = 0; ec_idx < m_equiv_classes.length (); )
+    {
+      equiv_class2 *ec = m_equiv_classes[ec_idx];
+      if (ec->m_vars.length () == 0)
+	{
+	  equiv_class2_id ec_id (ec_idx);
+	  bool has_constraint = false;
+	  for (unsigned con_idx = 0; con_idx < m_constraints.length ();
+	       con_idx++)
+	    {
+	      constraint2 *c = &m_constraints[con_idx];
+	      if (c->m_lhs == ec_id
+		  || c->m_rhs == ec_id)
+		{
+		  has_constraint = true;
+		  break;
+		}
+	    }
+	  if (!has_constraint)
+	    {
+	      delete ec;
+	      m_equiv_classes.ordered_remove (ec_idx);
+	      if (stats)
+		stats->m_num_equiv_classes++;
+
+	      /* Renumber constraints that refer to ECs that have
+		 had their idx changed.  */
+	      for (unsigned con_idx = 0; con_idx < m_constraints.length ();
+		   con_idx++)
+		{
+		  constraint2 *c = &m_constraints[con_idx];
+		  c->m_lhs.update_for_removal (ec_idx);
+		  c->m_rhs.update_for_removal (ec_idx);
+		}
+	      continue;
+	    }
+	}
+      ec_idx++;
+    }
+
+  validate ();
+}
+
+/* Implementation of PurgeCriteria.  */
+
+class dead_svalue_purger
+{
+public:
+  dead_svalue_purger (const hash_set<const svalue2 *> &live_svalues,
+		      const region_model2 *model)
+  : m_live_svalues (live_svalues), m_model (model)
+  {
+  }
+
+  bool should_purge_p (const svalue2 *sval) const
+  {
+    return !sval->live_p (m_live_svalues, m_model);
+  }
+
+private:
+  const hash_set<const svalue2 *> &m_live_svalues;
+  const region_model2 *m_model;
+};
+
+/* Purge dead svalues from equivalence classes and update constraints
+   accordingly.  */
+
+void
+constraint_manager2::
+on_liveness_change (const hash_set<const svalue2 *> &live_svalues,
+		    const region_model2 *model)
+{
+  dead_svalue_purger p (live_svalues, model);
+  purge (p, NULL);
+}
+
+/* Comparator for use by constraint_manager2::canonicalize.
+   Sort a pair of equiv_class2 instances, using the representative
+   svalue2_id as a sort key.  */
+
+static int
+equiv_class2_cmp (const void *p1, const void *p2)
+{
+  const equiv_class2 *ec1 = *(const equiv_class2 * const *)p1;
+  const equiv_class2 *ec2 = *(const equiv_class2 * const *)p2;
+
+  const svalue2 *rep1 = ec1->get_representative ();
+  const svalue2 *rep2 = ec2->get_representative ();
+
+  gcc_assert (rep1);
+  gcc_assert (rep2);
+
+  if (rep1 < rep2)
+    return 1;
+  if (rep1 > rep2)
+    return -1;
+  return 0;
+}
+
+/* Comparator for use by constraint_manager2::canonicalize.
+   Sort a pair of constraint instances.  */
+
+static int
+constraint_cmp (const void *p1, const void *p2)
+{
+  const constraint2 *c1 = (const constraint2 *)p1;
+  const constraint2 *c2 = (const constraint2 *)p2;
+  int lhs_cmp = c1->m_lhs.as_int () - c2->m_lhs.as_int ();
+  if (lhs_cmp)
+    return lhs_cmp;
+  int rhs_cmp = c1->m_rhs.as_int () - c2->m_rhs.as_int ();
+  if (rhs_cmp)
+    return rhs_cmp;
+  return c1->m_op - c2->m_op;
+}
+
+/* Purge redundant equivalence classes and constraints, and reorder them
+   within this constraint_manager2 into a canonical order, to increase the
+   chances of finding equality with another instance.  */
+
+void
+constraint_manager2::canonicalize ()
+{
+  /* First, sort svalue2s within the ECs.  */
+  unsigned i;
+  equiv_class2 *ec;
+  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+    ec->canonicalize ();
+
+  /* TODO: remove constraints where both sides have a constant, and are
+     thus implicit.  But does this break transitivity?  */
+
+  /* We will be purging and reordering ECs.
+     We will need to remap the equiv_class2_ids in the constraints,
+     so we need to store the original index of each EC.
+     Build a lookup table, mapping from the representative svalue2
+     to the original equiv_class2_id of that svalue2.  */
+  hash_map<const svalue2 *, equiv_class2_id> original_ec_id;
+  const unsigned orig_num_equiv_classes = m_equiv_classes.length ();
+  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+    {
+      const svalue2 *rep = ec->get_representative ();
+      gcc_assert (rep);
+      original_ec_id.put (rep, i);
+    }
+
+  /* Find ECs used by constraints.  */
+  hash_set<const equiv_class2 *> used_ecs;
+  constraint2 *c;
+  FOR_EACH_VEC_ELT (m_constraints, i, c)
+    {
+      used_ecs.add (m_equiv_classes[c->m_lhs.as_int ()]);
+      used_ecs.add (m_equiv_classes[c->m_rhs.as_int ()]);
+    }
+
+  /* Purge unused ECs: those that aren't used by constraints and
+     that effectively have only one svalue2 (either in m_constant
+     or in m_vars).  */
+  {
+    /* "unordered remove if" from a vec.  */
+    unsigned i = 0;
+    while (i < m_equiv_classes.length ())
+      {
+	equiv_class2 *ec = m_equiv_classes[i];
+	if (!used_ecs.contains (ec)
+	    && ((ec->m_vars.length () < 2 && ec->m_constant == NULL_TREE)
+		|| (ec->m_vars.length () == 0)))
+	  {
+	    m_equiv_classes.unordered_remove (i);
+	    delete ec;
+	  }
+	else
+	  i++;
+      }
+  }
+
+  /* Next, sort the surviving ECs into a canonical order.  */
+  m_equiv_classes.qsort (equiv_class2_cmp);
+
+  /* Populate ec_id_map based on the old vs new EC ids.  */
+  one_way_id_map<equiv_class2_id> ec_id_map (orig_num_equiv_classes);
+  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+    {
+      const svalue2 *rep = ec->get_representative ();
+      gcc_assert (rep);
+      ec_id_map.put (*original_ec_id.get (rep), i);
+    }
+
+  /* Use ec_id_map to update the EC ids within the constraints.  */
+  FOR_EACH_VEC_ELT (m_constraints, i, c)
+    {
+      ec_id_map.update (&c->m_lhs);
+      ec_id_map.update (&c->m_rhs);
+    }
+
+  /* Finally, sort the constraints. */
+  m_constraints.qsort (constraint_cmp);
+}
+
+/* A concrete subclass of constraint_manager2 for use when
+   merging two constraint_manager2 into a third constraint_manager2,
+   each of which has its own region_model2.
+   Calls are delegated to the constraint_manager2 for the merged model,
+   and thus affect its region_model2.  */
+#if 0
+class cleaned_constraint_manager2 : public constraint_manager2
+{
+public:
+  cleaned_constraint_manager2 (constraint_manager2 *merged)
+  : constraint_manager2 (merged->m_mgr),
+    m_merged (merged) {}
+
+#if 0
+  constraint_manager2 *clone (region_model2 *) const FINAL OVERRIDE
+  {
+    gcc_unreachable ();
+  }
+#endif
+private:
+  constraint_manager2 *m_merged;
+};
+#endif
+/* Concrete subclass of fact_visitor for use by constraint_manager2::merge.
+   For every fact in CM_A, see if it is also true in *CM_B.  Add such
+   facts to *OUT.  */
+
+class merger2_fact_visitor : public fact2_visitor
+{
+public:
+  merger2_fact_visitor (constraint_manager2 *cm_b,
+			constraint_manager2 *out,
+			const model2_merger &merger)
+  : m_cm_b (cm_b), m_out (out), m_merger (merger)
+  {}
+
+  void on_fact (const svalue2 *lhs, enum tree_code code, const svalue2 *rhs)
+    FINAL OVERRIDE
+  {
+    /* If we merged (M BINOP X) with M
+       and A has fact (M RELOP Y),
+       then we should instead have fact (M RELOP (Y BINOP X)).
+       For example, if we merged (M + 1) with M
+       and we have fact M <= 255 then we should instead have M <= 256.  */
+    // TODO: we need to fix eval_condition for this to work.
+#if ITERATED_BINOPS
+    unsigned i;
+    const binop_svalue2 *binop;
+    FOR_EACH_VEC_ELT (m_merger.m_iterated_binops, i, binop)
+      {
+	if (binop->get_arg0 () == lhs)
+	  {
+	    region_model2_manager *mgr = m_merger.get_manager ();
+	    rhs = mgr->get_or_create_binop (rhs->get_type (),
+					    binop->get_op (),
+					    rhs, binop->get_arg1 ());
+	  }
+      }
+#endif
+
+    /* Special-case for widening.  */
+    /* FIXME: should only do this if LHS isn't mentioned anywhere in
+       m_cm_b's model, rather than just in m_cb_b?  */
+    if (lhs->get_kind () == svalue2::SK_WIDENING)
+      if (!m_cm_b->get_equiv_class2_by_svalue2 (lhs, NULL))
+	{
+	  /* LHS isn't constrained within m_cm_b.  */
+	  /* FIXME: for now, treat it as "unmentioned".  */
+	  bool sat = m_out->add_constraint (lhs, code, rhs);
+	  gcc_assert (sat);
+	  return;
+	}
+
+    if (m_cm_b->eval_condition (lhs, code, rhs).is_true ())
+      {
+	bool sat = m_out->add_constraint (lhs, code, rhs);
+	gcc_assert (sat);
+      }
+  }
+
+private:
+  constraint_manager2 *m_cm_b;
+  constraint_manager2 *m_out;
+  const model2_merger &m_merger;
+};
+
+// 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.  */
+
+void
+constraint_manager2::merge (const constraint_manager2 &cm_a,
+			    const constraint_manager2 &cm_b,
+			    constraint_manager2 *out,
+			    const model2_merger &merger)
+{
+  /* FIXME: work with a copy, since eval_condition can mutate a cm.  */
+  constraint_manager2 copy_cm_a (cm_a);
+  constraint_manager2 copy_cm_b (cm_b);
+
+  /* Merge the equivalence classes and constraints.
+     The easiest way to do this seems to be to enumerate all of the facts
+     in cm_a, see which are also true in cm_b,
+     and add those to *OUT.  */
+  merger2_fact_visitor v (&copy_cm_b, out, merger);
+  copy_cm_a.for_each_fact (&v);
+
+  /* FIXME and the other way around, since things could be implicit in one
+     and explicit in the other?  */
+}
+
+/* A subroutine of constraint_manager2::merge.
+   Use MAP_SVAL_TO_M to map equivalence classes and constraints from
+   SM_IN to *OUT.  Purge any non-constant svalue2 *that don't appear
+   in the result of MAP_SVAL_TO_M, purging any ECs and their constraints
+   that become empty as a result.  Potentially create svalue2s in
+   the merged region_model2 for constants that weren't already in use there.  */
+#if 0
+void
+constraint_manager2::
+clean_merger_input (const constraint_manager2 &cm_in,
+		    const one_way_svalue2_id_map &map_sval_to_m,
+		    constraint_manager2 *out)
+{
+  one_way_id_map<equiv_class2_id> map_ec_to_m
+    (cm_in.m_equiv_classes.length ());
+  unsigned ec_idx;
+  equiv_class2 *ec;
+  FOR_EACH_VEC_ELT (cm_in.m_equiv_classes, ec_idx, ec)
+    {
+      equiv_class2 cleaned_ec;
+      if (tree cst = ec->get_any_constant ())
+	{
+	  cleaned_ec.m_constant = cst;
+	  /* Lazily create the constant in the out region_model2.  */
+	  cleaned_ec.m_cst_sval = out->get_sval_for_constant (cst);
+	}
+      unsigned var_idx;
+      const svalue2 **var_in_sval;
+      FOR_EACH_VEC_ELT (ec->m_vars, var_idx, var_in_sval)
+	{
+	  const svalue2 *var_m_sval
+	    = map_sval_to_m.get_dst_for_src (*var_in_sval);
+	  if (!var_m_sval.null_p ())
+	    cleaned_ec.m_vars.safe_push (var_m_sval);
+	}
+      if (cleaned_ec.get_any_constant () || !cleaned_ec.m_vars.is_empty ())
+	{
+	  map_ec_to_m.put (ec_idx, out->m_equiv_classes.length ());
+	  out->m_equiv_classes.safe_push (new equiv_class2 (cleaned_ec));
+	}
+    }
+
+  /* Write out to *OUT any constraints for which both svales survived
+     cleaning, using the new EC IDs.  */
+  unsigned con_idx;
+  constraint2 *c;
+  FOR_EACH_VEC_ELT (cm_in.m_constraints, con_idx, c)
+    {
+      equiv_class2_id new_lhs = map_ec_to_m.get_dst_for_src (c->m_lhs);
+      if (new_lhs.null_p ())
+	continue;
+      equiv_class2_id new_rhs = map_ec_to_m.get_dst_for_src (c->m_rhs);
+      if (new_rhs.null_p ())
+	continue;
+      out->m_constraints.safe_push (constraint (new_lhs,
+						c->m_op,
+						new_rhs));
+    }
+}
+#endif
+/* Call VISITOR's on_fact vfunc repeatedly to express the various
+   equivalence classes and constraints.
+   This is used by constraint_manager2::merge to find the common
+   facts between two input constraint_manager2s.  */
+
+void
+constraint_manager2::for_each_fact (fact2_visitor *visitor) const
+{
+  /* First, call EQ_EXPR within the various equivalence classes.  */
+  unsigned ec_idx;
+  equiv_class2 *ec;
+  FOR_EACH_VEC_ELT (m_equiv_classes, ec_idx, ec)
+    {
+      if (ec->m_cst_sval)
+	{
+	  unsigned i;
+	  const svalue2 *sval;
+	  FOR_EACH_VEC_ELT (ec->m_vars, i, sval)
+	    visitor->on_fact (ec->m_cst_sval, EQ_EXPR, sval);
+	}
+      for (unsigned i = 0; i < ec->m_vars.length (); i++)
+	for (unsigned j = i + 1; j < ec->m_vars.length (); j++)
+	  visitor->on_fact (ec->m_vars[i], EQ_EXPR, ec->m_vars[j]);
+    }
+
+  /* Now, express the various constraints.  */
+  unsigned con_idx;
+  constraint2 *c;
+  FOR_EACH_VEC_ELT (m_constraints, con_idx, c)
+    {
+      const equiv_class2 &ec_lhs = c->m_lhs.get_obj (*this);
+      const equiv_class2 &ec_rhs = c->m_rhs.get_obj (*this);
+      enum tree_code code = constraint2_tree_code (c->m_op);
+
+      if (ec_lhs.m_cst_sval)
+	{
+	  for (unsigned j = 0; j < ec_rhs.m_vars.length (); j++)
+	    {
+	      visitor->on_fact (ec_lhs.m_cst_sval, code, ec_rhs.m_vars[j]);
+	    }
+	}
+      for (unsigned i = 0; i < ec_lhs.m_vars.length (); i++)
+	{
+	  if (ec_rhs.m_cst_sval)
+	    visitor->on_fact (ec_lhs.m_vars[i], code, ec_rhs.m_cst_sval);
+	  for (unsigned j = 0; j < ec_rhs.m_vars.length (); j++)
+	    visitor->on_fact (ec_lhs.m_vars[i], code, ec_rhs.m_vars[j]);
+	}
+    }
+}
+
+/* Assert that this object is valid.  */
+
+void
+constraint_manager2::validate () const
+{
+  /* Skip this in a release build.  */
+#if !CHECKING_P
+  return;
+#endif
+
+  int i;
+  equiv_class2 *ec;
+  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+    {
+      gcc_assert (ec);
+
+      int j;
+      const svalue2 *sval;
+      FOR_EACH_VEC_ELT (ec->m_vars, j, sval)
+	gcc_assert (sval);
+      if (ec->m_constant)
+	{
+	  gcc_assert (CONSTANT_CLASS_P (ec->m_constant));
+	  gcc_assert (ec->m_cst_sval);
+	}
+#if 0
+      else
+	gcc_assert (ec->m_vars.length () > 0);
+#endif
+    }
+
+  constraint2 *c;
+  FOR_EACH_VEC_ELT (m_constraints, i, c)
+    {
+      gcc_assert (!c->m_lhs.null_p ());
+      gcc_assert (c->m_lhs.as_int () <= (int)m_equiv_classes.length ());
+      gcc_assert (!c->m_rhs.null_p ());
+      gcc_assert (c->m_rhs.as_int () <= (int)m_equiv_classes.length ());
+    }
+}
 
 #if CHECKING_P
 
 namespace selftest {
 
+/* Various constraint_manager2 selftests.
+   These have to be written in terms of a region_model2, since
+   the latter is responsible for managing svalue2 and svalue2_id
+   instances.  */
+
+/* Verify that setting and getting simple conditions within a region_model2
+   work (thus exercising the underlying constraint_manager2).  */
+
+static void
+test_constraint_conditions ()
+{
+  tree int_42 = build_int_cst (integer_type_node, 42);
+  tree int_0 = build_int_cst (integer_type_node, 0);
+
+  tree x = build_global_decl ("x", integer_type_node);
+  tree y = build_global_decl ("y", integer_type_node);
+  tree z = build_global_decl ("z", integer_type_node);
+
+  /* Self-comparisons.  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ASSERT_CONDITION_TRUE2 (model, x, EQ_EXPR, x);
+    ASSERT_CONDITION_TRUE2 (model, x, LE_EXPR, x);
+    ASSERT_CONDITION_TRUE2 (model, x, GE_EXPR, x);
+    ASSERT_CONDITION_FALSE2 (model, x, NE_EXPR, x);
+    ASSERT_CONDITION_FALSE2 (model, x, LT_EXPR, x);
+    ASSERT_CONDITION_FALSE2 (model, x, GT_EXPR, x);
+  }
+
+  /* Adding self-equality shouldn't add equiv classes.  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, x);
+    ADD_SAT_CONSTRAINT (model, int_42, EQ_EXPR, int_42);
+    /* ...even when done directly via svalues: */
+    const svalue2 *sval_int_42 = model.get_rvalue (int_42, NULL);
+    bool sat = model.get_constraints2 ()->add_constraint (sval_int_42,
+							  EQ_EXPR,
+							  sval_int_42);
+    ASSERT_TRUE (sat);
+    ASSERT_EQ (model.get_constraints2 ()->m_equiv_classes.length (), 0);
+  }
+
+  /* x == y.  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ASSERT_CONDITION_UNKNOWN2 (model, x, EQ_EXPR, y);
+
+    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, y);
+
+    ASSERT_CONDITION_TRUE2 (model, x, EQ_EXPR, y);
+    ASSERT_CONDITION_TRUE2 (model, x, LE_EXPR, y);
+    ASSERT_CONDITION_TRUE2 (model, x, GE_EXPR, y);
+    ASSERT_CONDITION_FALSE2 (model, x, NE_EXPR, y);
+    ASSERT_CONDITION_FALSE2 (model, x, LT_EXPR, y);
+    ASSERT_CONDITION_FALSE2 (model, x, GT_EXPR, y);
+
+    /* Swapped operands.  */
+    ASSERT_CONDITION_TRUE2 (model, y, EQ_EXPR, x);
+    ASSERT_CONDITION_TRUE2 (model, y, LE_EXPR, x);
+    ASSERT_CONDITION_TRUE2 (model, y, GE_EXPR, x);
+    ASSERT_CONDITION_FALSE2 (model, y, NE_EXPR, x);
+    ASSERT_CONDITION_FALSE2 (model, y, LT_EXPR, x);
+    ASSERT_CONDITION_FALSE2 (model, y, GT_EXPR, x);
+
+    /* Comparison with other var.  */
+    ASSERT_CONDITION_UNKNOWN2 (model, x, EQ_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN2 (model, x, LE_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN2 (model, x, GE_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN2 (model, x, NE_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN2 (model, x, LT_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN2 (model, x, GT_EXPR, z);
+  }
+
+  /* x == y, then y == z  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ASSERT_CONDITION_UNKNOWN2 (model, x, EQ_EXPR, y);
+
+    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, y);
+    ADD_SAT_CONSTRAINT (model, y, EQ_EXPR, z);
+
+    ASSERT_CONDITION_TRUE2 (model, x, EQ_EXPR, z);
+    ASSERT_CONDITION_TRUE2 (model, x, LE_EXPR, z);
+    ASSERT_CONDITION_TRUE2 (model, x, GE_EXPR, z);
+    ASSERT_CONDITION_FALSE2 (model, x, NE_EXPR, z);
+    ASSERT_CONDITION_FALSE2 (model, x, LT_EXPR, z);
+    ASSERT_CONDITION_FALSE2 (model, x, GT_EXPR, z);
+  }
+
+  /* x != y.  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+
+    ADD_SAT_CONSTRAINT (model, x, NE_EXPR, y);
+
+    ASSERT_CONDITION_TRUE2 (model, x, NE_EXPR, y);
+    ASSERT_CONDITION_FALSE2 (model, x, EQ_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN2 (model, x, LE_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN2 (model, x, GE_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN2 (model, x, LT_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN2 (model, x, GT_EXPR, y);
+
+    /* Swapped operands.  */
+    ASSERT_CONDITION_TRUE2 (model, y, NE_EXPR, x);
+    ASSERT_CONDITION_FALSE2 (model, y, EQ_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN2 (model, y, LE_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN2 (model, y, GE_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN2 (model, y, LT_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN2 (model, y, GT_EXPR, x);
+
+    /* Comparison with other var.  */
+    ASSERT_CONDITION_UNKNOWN2 (model, x, EQ_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN2 (model, x, LE_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN2 (model, x, GE_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN2 (model, x, NE_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN2 (model, x, LT_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN2 (model, x, GT_EXPR, z);
+  }
+
+  /* x < y.  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+
+    ADD_SAT_CONSTRAINT (model, x, LT_EXPR, y);
+
+    ASSERT_CONDITION_TRUE2 (model, x, LT_EXPR, y);
+    ASSERT_CONDITION_TRUE2 (model, x, LE_EXPR, y);
+    ASSERT_CONDITION_TRUE2 (model, x, NE_EXPR, y);
+    ASSERT_CONDITION_FALSE2 (model, x, EQ_EXPR, y);
+    ASSERT_CONDITION_FALSE2 (model, x, GT_EXPR, y);
+    ASSERT_CONDITION_FALSE2 (model, x, GE_EXPR, y);
+
+    /* Swapped operands.  */
+    ASSERT_CONDITION_FALSE2 (model, y, LT_EXPR, x);
+    ASSERT_CONDITION_FALSE2 (model, y, LE_EXPR, x);
+    ASSERT_CONDITION_TRUE2 (model, y, NE_EXPR, x);
+    ASSERT_CONDITION_FALSE2 (model, y, EQ_EXPR, x);
+    ASSERT_CONDITION_TRUE2 (model, y, GT_EXPR, x);
+    ASSERT_CONDITION_TRUE2 (model, y, GE_EXPR, x);
+  }
+
+  /* x <= y.  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+
+    ADD_SAT_CONSTRAINT (model, x, LE_EXPR, y);
+
+    ASSERT_CONDITION_UNKNOWN2 (model, x, LT_EXPR, y);
+    ASSERT_CONDITION_TRUE2 (model, x, LE_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN2 (model, x, NE_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN2 (model, x, EQ_EXPR, y);
+    ASSERT_CONDITION_FALSE2 (model, x, GT_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN2 (model, x, GE_EXPR, y);
+
+    /* Swapped operands.  */
+    ASSERT_CONDITION_FALSE2 (model, y, LT_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN2 (model, y, LE_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN2 (model, y, NE_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN2 (model, y, EQ_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN2 (model, y, GT_EXPR, x);
+    ASSERT_CONDITION_TRUE2 (model, y, GE_EXPR, x);
+  }
+
+  /* x > y.  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+
+    ADD_SAT_CONSTRAINT (model, x, GT_EXPR, y);
+
+    ASSERT_CONDITION_TRUE2 (model, x, GT_EXPR, y);
+    ASSERT_CONDITION_TRUE2 (model, x, GE_EXPR, y);
+    ASSERT_CONDITION_TRUE2 (model, x, NE_EXPR, y);
+    ASSERT_CONDITION_FALSE2 (model, x, EQ_EXPR, y);
+    ASSERT_CONDITION_FALSE2 (model, x, LT_EXPR, y);
+    ASSERT_CONDITION_FALSE2 (model, x, LE_EXPR, y);
+
+    /* Swapped operands.  */
+    ASSERT_CONDITION_FALSE2 (model, y, GT_EXPR, x);
+    ASSERT_CONDITION_FALSE2 (model, y, GE_EXPR, x);
+    ASSERT_CONDITION_TRUE2 (model, y, NE_EXPR, x);
+    ASSERT_CONDITION_FALSE2 (model, y, EQ_EXPR, x);
+    ASSERT_CONDITION_TRUE2 (model, y, LT_EXPR, x);
+    ASSERT_CONDITION_TRUE2 (model, y, LE_EXPR, x);
+  }
+
+  /* x >= y.  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+
+    ADD_SAT_CONSTRAINT (model, x, GE_EXPR, y);
+
+    ASSERT_CONDITION_UNKNOWN2 (model, x, GT_EXPR, y);
+    ASSERT_CONDITION_TRUE2 (model, x, GE_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN2 (model, x, NE_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN2 (model, x, EQ_EXPR, y);
+    ASSERT_CONDITION_FALSE2 (model, x, LT_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN2 (model, x, LE_EXPR, y);
+
+    /* Swapped operands.  */
+    ASSERT_CONDITION_FALSE2 (model, y, GT_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN2 (model, y, GE_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN2 (model, y, NE_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN2 (model, y, EQ_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN2 (model, y, LT_EXPR, x);
+    ASSERT_CONDITION_TRUE2 (model, y, LE_EXPR, x);
+  }
+
+  // TODO: implied orderings
+
+  /* Constants.  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ASSERT_CONDITION_FALSE2 (model, int_0, EQ_EXPR, int_42);
+    ASSERT_CONDITION_TRUE2 (model, int_0, NE_EXPR, int_42);
+    ASSERT_CONDITION_TRUE2 (model, int_0, LT_EXPR, int_42);
+    ASSERT_CONDITION_TRUE2 (model, int_0, LE_EXPR, int_42);
+    ASSERT_CONDITION_FALSE2 (model, int_0, GT_EXPR, int_42);
+    ASSERT_CONDITION_FALSE2 (model, int_0, GE_EXPR, int_42);
+  }
+
+  /* x == 0, y == 42.  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, int_0);
+    ADD_SAT_CONSTRAINT (model, y, EQ_EXPR, int_42);
+
+    ASSERT_CONDITION_TRUE2 (model, x, NE_EXPR, y);
+    ASSERT_CONDITION_FALSE2 (model, x, EQ_EXPR, y);
+    ASSERT_CONDITION_TRUE2 (model, x, LE_EXPR, y);
+    ASSERT_CONDITION_FALSE2 (model, x, GE_EXPR, y);
+    ASSERT_CONDITION_TRUE2 (model, x, LT_EXPR, y);
+    ASSERT_CONDITION_FALSE2 (model, x, GT_EXPR, y);
+  }
+
+  /* Unsatisfiable combinations.  */
+
+  /* x == y && x != y.  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, y);
+    ADD_UNSAT_CONSTRAINT (model, x, NE_EXPR, y);
+  }
+
+  /* x == 0 then x == 42.  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, int_0);
+    ADD_UNSAT_CONSTRAINT (model, x, EQ_EXPR, int_42);
+  }
+
+  /* x == 0 then x != 0.  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, int_0);
+    ADD_UNSAT_CONSTRAINT (model, x, NE_EXPR, int_0);
+  }
+
+  /* x == 0 then x > 0.  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, int_0);
+    ADD_UNSAT_CONSTRAINT (model, x, GT_EXPR, int_0);
+  }
+
+  /* x != y && x == y.  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ADD_SAT_CONSTRAINT (model, x, NE_EXPR, y);
+    ADD_UNSAT_CONSTRAINT (model, x, EQ_EXPR, y);
+  }
+
+  /* x <= y && x > y.  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ADD_SAT_CONSTRAINT (model, x, LE_EXPR, y);
+    ADD_UNSAT_CONSTRAINT (model, x, GT_EXPR, y);
+  }
+
+  // etc
+}
+
+/* Test transitivity of conditions.  */
+
+static void
+test_transitivity ()
+{
+  tree a = build_global_decl ("a", integer_type_node);
+  tree b = build_global_decl ("b", integer_type_node);
+  tree c = build_global_decl ("c", integer_type_node);
+  tree d = build_global_decl ("d", integer_type_node);
+
+  /* a == b, then c == d, then c == b.  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ASSERT_CONDITION_UNKNOWN2 (model, a, EQ_EXPR, b);
+    ASSERT_CONDITION_UNKNOWN2 (model, b, EQ_EXPR, c);
+    ASSERT_CONDITION_UNKNOWN2 (model, c, EQ_EXPR, d);
+    ASSERT_CONDITION_UNKNOWN2 (model, a, EQ_EXPR, d);
+
+    ADD_SAT_CONSTRAINT (model, a, EQ_EXPR, b);
+    ASSERT_CONDITION_TRUE2 (model, a, EQ_EXPR, b);
+
+    ADD_SAT_CONSTRAINT (model, c, EQ_EXPR, d);
+    ASSERT_CONDITION_TRUE2 (model, c, EQ_EXPR, d);
+    ASSERT_CONDITION_UNKNOWN2 (model, a, EQ_EXPR, d);
+
+    ADD_SAT_CONSTRAINT (model, c, EQ_EXPR, b);
+    ASSERT_CONDITION_TRUE2 (model, c, EQ_EXPR, b);
+    ASSERT_CONDITION_TRUE2 (model, a, EQ_EXPR, d);
+  }
+
+  /* Transitivity: "a < b", "b < c" should imply "a < c".  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ADD_SAT_CONSTRAINT (model, a, LT_EXPR, b);
+    ADD_SAT_CONSTRAINT (model, b, LT_EXPR, c);
+
+    ASSERT_CONDITION_TRUE2 (model, a, LT_EXPR, c);
+    ASSERT_CONDITION_FALSE2 (model, a, EQ_EXPR, c);
+  }
+
+  /* Transitivity: "a <= b", "b < c" should imply "a < c".  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ADD_SAT_CONSTRAINT (model, a, LE_EXPR, b);
+    ADD_SAT_CONSTRAINT (model, b, LT_EXPR, c);
+
+    ASSERT_CONDITION_TRUE2 (model, a, LT_EXPR, c);
+    ASSERT_CONDITION_FALSE2 (model, a, EQ_EXPR, c);
+  }
+
+  /* Transitivity: "a <= b", "b <= c" should imply "a <= c".  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ADD_SAT_CONSTRAINT (model, a, LE_EXPR, b);
+    ADD_SAT_CONSTRAINT (model, b, LE_EXPR, c);
+
+    ASSERT_CONDITION_TRUE2 (model, a, LE_EXPR, c);
+    ASSERT_CONDITION_UNKNOWN2 (model, a, EQ_EXPR, c);
+  }
+
+  /* Transitivity: "a > b", "b > c" should imply "a > c".  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ADD_SAT_CONSTRAINT (model, a, GT_EXPR, b);
+    ADD_SAT_CONSTRAINT (model, b, GT_EXPR, c);
+
+    ASSERT_CONDITION_TRUE2 (model, a, GT_EXPR, c);
+    ASSERT_CONDITION_FALSE2 (model, a, EQ_EXPR, c);
+  }
+
+  /* Transitivity: "a >= b", "b > c" should imply " a > c".  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ADD_SAT_CONSTRAINT (model, a, GE_EXPR, b);
+    ADD_SAT_CONSTRAINT (model, b, GT_EXPR, c);
+
+    ASSERT_CONDITION_TRUE2 (model, a, GT_EXPR, c);
+    ASSERT_CONDITION_FALSE2 (model, a, EQ_EXPR, c);
+  }
+
+  /* Transitivity: "a >= b", "b >= c" should imply "a >= c".  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ADD_SAT_CONSTRAINT (model, a, GE_EXPR, b);
+    ADD_SAT_CONSTRAINT (model, b, GE_EXPR, c);
+
+    ASSERT_CONDITION_TRUE2 (model, a, GE_EXPR, c);
+    ASSERT_CONDITION_UNKNOWN2 (model, a, EQ_EXPR, c);
+  }
+
+  /* Transitivity: "(a < b)", "(c < d)", "(b < c)" should
+     imply the easy cases:
+       (a < c)
+       (b < d)
+     but also that:
+       (a < d).  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ADD_SAT_CONSTRAINT (model, a, LT_EXPR, b);
+    ADD_SAT_CONSTRAINT (model, c, LT_EXPR, d);
+    ADD_SAT_CONSTRAINT (model, b, LT_EXPR, c);
+
+    ASSERT_CONDITION_TRUE2 (model, a, LT_EXPR, c);
+    ASSERT_CONDITION_TRUE2 (model, b, LT_EXPR, d);
+    ASSERT_CONDITION_TRUE2 (model, a, LT_EXPR, d);
+  }
+
+  /* Transitivity: "a >= b", "b >= a" should imply that a == b.  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ADD_SAT_CONSTRAINT (model, a, GE_EXPR, b);
+    ADD_SAT_CONSTRAINT (model, b, GE_EXPR, a);
+
+    // TODO:
+    ASSERT_CONDITION_TRUE2 (model, a, EQ_EXPR, b);
+
+    /* The ECs for a and b should have merged, and any constraints removed.  */
+    ASSERT_EQ (model.get_constraints2 ()->m_equiv_classes.length (), 1);
+    ASSERT_EQ (model.get_constraints2 ()->m_constraints.length (), 0);
+  }
+
+  /* Transitivity: "a >= b", "b > a" should be impossible.  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ADD_SAT_CONSTRAINT (model, a, GE_EXPR, b);
+    ADD_UNSAT_CONSTRAINT (model, b, GT_EXPR, a);
+  }
+
+  /* Transitivity: "a >= b", "b >= c", "c >= a" should imply
+     that a == b == c.  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ADD_SAT_CONSTRAINT (model, a, GE_EXPR, b);
+    ADD_SAT_CONSTRAINT (model, b, GE_EXPR, c);
+    ADD_SAT_CONSTRAINT (model, c, GE_EXPR, a);
+
+    ASSERT_CONDITION_TRUE2 (model, a, EQ_EXPR, c);
+  }
+
+  /* Transitivity: "a > b", "b > c", "c > a"
+     should be impossible.  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ADD_SAT_CONSTRAINT (model, a, GT_EXPR, b);
+    ADD_SAT_CONSTRAINT (model, b, GT_EXPR, c);
+    ADD_UNSAT_CONSTRAINT (model, c, GT_EXPR, a);
+  }
+
+}
+
+/* Test various conditionals involving constants where the results
+   ought to be implied based on the values of the constants.  */
+
+static void
+test_constant_comparisons ()
+{
+  tree int_3 = build_int_cst (integer_type_node, 3);
+  tree int_4 = build_int_cst (integer_type_node, 4);
+  tree int_5 = build_int_cst (integer_type_node, 5);
+
+  tree int_1023 = build_int_cst (integer_type_node, 1023);
+  tree int_1024 = build_int_cst (integer_type_node, 1024);
+
+  tree a = build_global_decl ("a", integer_type_node);
+  tree b = build_global_decl ("b", integer_type_node);
+
+  /* Given a >= 1024, then a <= 1023 should be impossible.  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ADD_SAT_CONSTRAINT (model, a, GE_EXPR, int_1024);
+    ADD_UNSAT_CONSTRAINT (model, a, LE_EXPR, int_1023);
+  }
+
+  /* a > 4.  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ADD_SAT_CONSTRAINT (model, a, GT_EXPR, int_4);
+    ASSERT_CONDITION_TRUE2 (model, a, GT_EXPR, int_4);
+    ASSERT_CONDITION_TRUE2 (model, a, NE_EXPR, int_3);
+    ASSERT_CONDITION_UNKNOWN2 (model, a, NE_EXPR, int_5);
+  }
+
+  /* a <= 4.  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ADD_SAT_CONSTRAINT (model, a, LE_EXPR, int_4);
+    ASSERT_CONDITION_FALSE2 (model, a, GT_EXPR, int_4);
+    ASSERT_CONDITION_FALSE2 (model, a, GT_EXPR, int_5);
+    ASSERT_CONDITION_UNKNOWN2 (model, a, NE_EXPR, int_3);
+  }
+
+  /* If "a > b" and "a == 3", then "b == 4" ought to be unsatisfiable.  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ADD_SAT_CONSTRAINT (model, a, GT_EXPR, b);
+    ADD_SAT_CONSTRAINT (model, a, EQ_EXPR, int_3);
+    ADD_UNSAT_CONSTRAINT (model, b, EQ_EXPR, int_4);
+  }
+
+  /* Various tests of int ranges where there is only one possible candidate.  */
+  {
+    /* If "a <= 4" && "a > 3", then "a == 4",
+       assuming a is of integral type.  */
+    {
+      region_model2_manager mgr;
+      region_model2 model (&mgr);
+      ADD_SAT_CONSTRAINT (model, a, LE_EXPR, int_4);
+      ADD_SAT_CONSTRAINT (model, a, GT_EXPR, int_3);
+      ASSERT_CONDITION_TRUE2 (model, a, EQ_EXPR, int_4);
+    }
+
+    /* If "a > 3" && "a <= 4", then "a == 4",
+       assuming a is of integral type.  */
+    {
+      region_model2_manager mgr;
+      region_model2 model (&mgr);
+      ADD_SAT_CONSTRAINT (model, a, GT_EXPR, int_3);
+      ADD_SAT_CONSTRAINT (model, a, LE_EXPR, int_4);
+      ASSERT_CONDITION_TRUE2 (model, a, EQ_EXPR, int_4);
+    }
+    /* If "a > 3" && "a < 5", then "a == 4",
+       assuming a is of integral type.  */
+    {
+      region_model2_manager mgr;
+      region_model2 model (&mgr);
+      ADD_SAT_CONSTRAINT (model, a, GT_EXPR, int_3);
+      ADD_SAT_CONSTRAINT (model, a, LT_EXPR, int_5);
+      ASSERT_CONDITION_TRUE2 (model, a, EQ_EXPR, int_4);
+    }
+    /* If "a >= 4" && "a < 5", then "a == 4",
+       assuming a is of integral type.  */
+    {
+      region_model2_manager mgr;
+      region_model2 model (&mgr);
+      ADD_SAT_CONSTRAINT (model, a, GE_EXPR, int_4);
+      ADD_SAT_CONSTRAINT (model, a, LT_EXPR, int_5);
+      ASSERT_CONDITION_TRUE2 (model, a, EQ_EXPR, int_4);
+    }
+    /* If "a >= 4" && "a <= 4", then "a == 4".  */
+    {
+      region_model2_manager mgr;
+      region_model2 model (&mgr);
+      ADD_SAT_CONSTRAINT (model, a, GE_EXPR, int_4);
+      ADD_SAT_CONSTRAINT (model, a, LE_EXPR, int_4);
+      ASSERT_CONDITION_TRUE2 (model, a, EQ_EXPR, int_4);
+    }
+  }
+
+  /* As above, but for floating-point:
+     if "f > 3" && "f <= 4" we don't know that f == 4.  */
+  {
+    tree f = build_global_decl ("f", double_type_node);
+    tree float_3 = build_real_from_int_cst (double_type_node, int_3);
+    tree float_4 = build_real_from_int_cst (double_type_node, int_4);
+
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ADD_SAT_CONSTRAINT (model, f, GT_EXPR, float_3);
+    ADD_SAT_CONSTRAINT (model, f, LE_EXPR, float_4);
+    ASSERT_CONDITION_UNKNOWN2 (model, f, EQ_EXPR, float_4);
+    ASSERT_CONDITION_UNKNOWN2 (model, f, EQ_EXPR, int_4);
+  }
+}
+
+/* Verify various lower-level implementation details about
+   constraint_manager2.  */
+
+static void
+test_constraint_impl ()
+{
+  tree int_42 = build_int_cst (integer_type_node, 42);
+  tree int_0 = build_int_cst (integer_type_node, 0);
+
+  tree x = build_global_decl ("x", integer_type_node);
+  tree y = build_global_decl ("y", integer_type_node);
+  tree z = build_global_decl ("z", integer_type_node);
+
+  /* x == y.  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+
+    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, y);
+
+    /* Assert various things about the insides of model.  */
+    constraint_manager2 *cm = model.get_constraints2 ();
+    ASSERT_EQ (cm->m_constraints.length (), 0);
+    ASSERT_EQ (cm->m_equiv_classes.length (), 1);
+  }
+
+  /* y <= z; x == y.  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ASSERT_CONDITION_UNKNOWN2 (model, x, EQ_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN2 (model, x, GE_EXPR, z);
+
+    ADD_SAT_CONSTRAINT (model, y, GE_EXPR, z);
+    ASSERT_CONDITION_TRUE2 (model, y, GE_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN2 (model, x, GE_EXPR, z);
+
+    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, y);
+
+    /* Assert various things about the insides of model.  */
+    constraint_manager2 *cm = model.get_constraints2 ();
+    ASSERT_EQ (cm->m_constraints.length (), 1);
+    ASSERT_EQ (cm->m_equiv_classes.length (), 2);
+
+    /* Ensure that we merged the constraints.  */
+    ASSERT_CONDITION_TRUE2 (model, x, GE_EXPR, z);
+  }
+
+  /* y <= z; y == x.  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+    ASSERT_CONDITION_UNKNOWN2 (model, x, EQ_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN2 (model, x, GE_EXPR, z);
+
+    ADD_SAT_CONSTRAINT (model, y, GE_EXPR, z);
+    ASSERT_CONDITION_TRUE2 (model, y, GE_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN2 (model, x, GE_EXPR, z);
+
+    ADD_SAT_CONSTRAINT (model, y, EQ_EXPR, x);
+
+    /* Assert various things about the insides of model.  */
+    constraint_manager2 *cm = model.get_constraints2 ();
+    ASSERT_EQ (cm->m_constraints.length (), 1);
+    ASSERT_EQ (cm->m_equiv_classes.length (), 2);
+
+    /* Ensure that we merged the constraints.  */
+    ASSERT_CONDITION_TRUE2 (model, x, GE_EXPR, z);
+  }
+
+  /* x == 0, then x != 42.  */
+  {
+    region_model2_manager mgr;
+    region_model2 model (&mgr);
+
+    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, int_0);
+    ADD_SAT_CONSTRAINT (model, x, NE_EXPR, int_42);
+
+    /* Assert various things about the insides of model.  */
+    constraint_manager2 *cm = model.get_constraints2 ();
+    ASSERT_EQ (cm->m_constraints.length (), 0);
+    ASSERT_EQ (cm->m_equiv_classes.length (), 1);
+#if 0
+    ASSERT_EQ (cm->m_constraints[0].m_lhs,
+	       cm->get_or_add_equiv_class2 (model.get_rvalue (int_0, NULL)));
+    ASSERT_EQ (cm->m_constraints[0].m_rhs,
+	       cm->get_or_add_equiv_class2 (model.get_rvalue (int_42, NULL)));
+    ASSERT_EQ (cm->m_constraints[0].m_op, CONSTRAINT2_LT);
+#endif
+  }
+
+  // TODO: selftest for merging ecs "in the middle"
+  // where a non-final one gets overwritten
+
+  // TODO: selftest where there are pre-existing constraints
+}
+
+/* Check that operator== and hashing works as expected for the
+   various types.  */
+
+static void
+test_equality ()
+{
+  tree x = build_global_decl ("x", integer_type_node);
+  tree y = build_global_decl ("y", integer_type_node);
+
+  {
+    region_model2_manager mgr;
+    region_model2 model0 (&mgr);
+    region_model2 model1 (&mgr);
+
+    constraint_manager2 *cm0 = model0.get_constraints2 ();
+    constraint_manager2 *cm1 = model1.get_constraints2 ();
+
+    ASSERT_EQ (cm0->hash (), cm1->hash ());
+    ASSERT_EQ (*cm0, *cm1);
+
+#if 0
+    ASSERT_EQ (model0.hash (), model1.hash ());
+#endif
+    ASSERT_EQ (model0, model1);
+
+    ADD_SAT_CONSTRAINT (model1, x, EQ_EXPR, y);
+    ASSERT_NE (cm0->hash (), cm1->hash ());
+    ASSERT_NE (*cm0, *cm1);
+
+#if 0
+    ASSERT_NE (model0.hash (), model1.hash ());
+#endif
+    ASSERT_NE (model0, model1);
+
+    region_model2 model2 (&mgr);
+    constraint_manager2 *cm2 = model2.get_constraints2 ();
+    /* Make the same change to cm2.  */
+    ADD_SAT_CONSTRAINT (model2, x, EQ_EXPR, y);
+    ASSERT_EQ (cm1->hash (), cm2->hash ());
+    ASSERT_EQ (*cm1, *cm2);
+
+#if 0
+    ASSERT_EQ (model1.hash (), model2.hash ());
+#endif
+    ASSERT_EQ (model1, model2);
+  }
+}
+
+/* Verify tracking inequality of a variable against many constants.  */
+
+static void
+test_many_constants ()
+{
+  tree a = build_global_decl ("a", integer_type_node);
+
+  region_model2_manager mgr;
+  region_model2 model (&mgr);
+  auto_vec<tree> constants;
+  for (int i = 0; i < 20; i++)
+    {
+      tree constant = build_int_cst (integer_type_node, i);
+      constants.safe_push (constant);
+      ADD_SAT_CONSTRAINT (model, a, NE_EXPR, constant);
+
+      /* Merge, and check the result.  */
+      region_model2 other (model);
+
+#if 0
+      region_model2 merged (&mgr);
+      ASSERT_TRUE (model.can_merge_with_p (other, &merged));
+      model.canonicalize (NULL);
+      merged.canonicalize (NULL);
+      ASSERT_EQ (model, merged);
+#endif
+
+      for (int j = 0; j <= i; j++)
+	ASSERT_CONDITION_TRUE2 (model, a, NE_EXPR, constants[j]);
+    }
+}
+
+/* FIXME.  */
+
+static void
+test_redundant_constraints ()
+{
+  tree int_257 = build_int_cst (integer_type_node, 257);
+  tree int_256 = build_int_cst (integer_type_node, 256);
+  tree i = build_global_decl ("i", integer_type_node);
+
+  {
+    /* i < 257 then i < 256.  */
+    region_model2_manager mgr;
+    region_model2 model0 (&mgr);
+    ADD_SAT_CONSTRAINT (model0, i, LT_EXPR, int_257);
+    ADD_SAT_CONSTRAINT (model0, i, LT_EXPR, int_256);
+    ASSERT_CONDITION_TRUE2 (model0, i, LT_EXPR, int_256);
+    ASSERT_CONDITION_TRUE2 (model0, i, LT_EXPR, int_257);
+    /* The older constraint was made redundant by the newer;
+       it should have been removed.  */
+    //ASSERT_EQ (model0.get_constraints2 ()->m_constraints.length (), 1);
+    //ASSERT_EQ (model1.get_constraints2 ()->m_equiv_classes.length (), 2);
+
+    /* i < 256 then i < 257.  */
+    region_model2 model1 (&mgr);
+    ADD_SAT_CONSTRAINT (model1, i, LT_EXPR, int_256);
+    ADD_SAT_CONSTRAINT (model1, i, LT_EXPR, int_257);
+    ASSERT_CONDITION_TRUE2 (model1, i, LT_EXPR, int_256);
+    ASSERT_CONDITION_TRUE2 (model1, i, LT_EXPR, int_257);
+    ASSERT_EQ (model1.get_constraints2 ()->m_equiv_classes.length (), 2);
+    ASSERT_EQ (model1.get_constraints2 ()->m_constraints.length (), 1);
+
+    // TODO:
+    //ASSERT_EQ (model1, model0);
+  }
+
+  {
+    /* i <= 257 then i <= 256.  */
+    region_model2_manager mgr;
+    region_model2 model0 (&mgr);
+    ADD_SAT_CONSTRAINT (model0, i, LE_EXPR, int_257);
+    ADD_SAT_CONSTRAINT (model0, i, LE_EXPR, int_256);
+    ASSERT_CONDITION_TRUE2 (model0, i, LE_EXPR, int_256);
+    ASSERT_CONDITION_TRUE2 (model0, i, LE_EXPR, int_257);
+    /* The older constraint was made redundant by the newer;
+       it should have been removed.  */
+    //ASSERT_EQ (model0.get_constraints2 ()->m_constraints.length (), 1);
+    //ASSERT_EQ (model1.get_constraints2 ()->m_equiv_classes.length (), 2);
+
+    /* i <= 256 then i <= 257.  */
+    region_model2 model1 (&mgr);
+    ADD_SAT_CONSTRAINT (model1, i, LE_EXPR, int_256);
+    ADD_SAT_CONSTRAINT (model1, i, LE_EXPR, int_257);
+    ASSERT_CONDITION_TRUE2 (model1, i, LE_EXPR, int_256);
+    ASSERT_CONDITION_TRUE2 (model1, i, LE_EXPR, int_257);
+    ASSERT_EQ (model1.get_constraints2 ()->m_equiv_classes.length (), 2);
+    ASSERT_EQ (model1.get_constraints2 ()->m_constraints.length (), 1);
+
+    // TODO:
+    //ASSERT_EQ (model1, model0);
+  }
+
+}
+
+/* Run the selftests in this file, temporarily overriding
+   flag_analyzer_transitivity with TRANSITIVITY.  */
+
+static void
+run_constraint_manager_tests (bool transitivity)
+{
+  int saved_flag_analyzer_transitivity = flag_analyzer_transitivity;
+  flag_analyzer_transitivity = transitivity;
+
+  test_constraint_conditions ();
+  if (flag_analyzer_transitivity)
+    {
+      /* These selftests assume transitivity.  */
+      test_transitivity ();
+    }
+  test_constant_comparisons ();
+  test_constraint_impl ();
+  test_equality ();
+  test_many_constants ();
+  test_redundant_constraints ();
+
+  flag_analyzer_transitivity = saved_flag_analyzer_transitivity;
+}
+
 /* Run all of the selftests within this file.  */
 
 void
 analyzer_constraint_manager_cc_tests ()
 {
+  /* Run the tests twice: with and without transitivity.  */
+  run_constraint_manager_tests (true);
+  run_constraint_manager_tests (false);
 }
 
 } // namespace selftest
diff --git a/gcc/analyzer/constraint-manager.h b/gcc/analyzer/constraint-manager.h
index e65ad827ce2..8fae2b488b1 100644
--- a/gcc/analyzer/constraint-manager.h
+++ b/gcc/analyzer/constraint-manager.h
@@ -18,11 +18,280 @@ You should have received a copy of the GNU General Public License
 along with GCC; see the file COPYING3.  If not see
 <http://www.gnu.org/licenses/>.  */
 
-#ifndef GCC_ANALYZER_CONSTRAINT_MANAGER_H
-#define GCC_ANALYZER_CONSTRAINT_MANAGER_H
+#ifndef GCC_ANALYZER_CONSTRAINT_MANAGER2_H
+#define GCC_ANALYZER_CONSTRAINT_MANAGER2_H
 
 namespace ana {
 
+class constraint_manager2;
+
+/* One of the end-points of a range2.  */
+
+struct bound2
+{
+  bound2 () : m_constant (NULL_TREE), m_closed (false) {}
+  bound2 (tree constant, bool closed)
+  : m_constant (constant), m_closed (closed) {}
+
+  void ensure_closed (bool is_upper);
+
+  const char * get_relation_as_str () const;
+
+  tree m_constant;
+  bool m_closed;
+};
+
+/* A range of values, used for determining if a value has been
+   constrained to just one possible constant value.  */
+
+struct range2
+{
+  range2 () : m_lower_bound (), m_upper_bound () {}
+  range2 (const bound2 &lower, const bound2 &upper)
+  : m_lower_bound (lower), m_upper_bound (upper) {}
+
+  void dump_to_pp (pretty_printer *pp) const;
+  void dump () const;
+
+  tree constrained_to_single_element ();
+
+  tristate eval_condition (enum tree_code op,
+			   tree rhs_const) const;
+  bool below_lower_bound (tree rhs_const) const;
+  bool above_upper_bound (tree rhs_const) const;
+
+  bound2 m_lower_bound;
+  bound2 m_upper_bound;
+};
+
+/* An equivalence class within a constraint manager: a set of
+   svalue_ids that are known to all be equal to each other,
+   together with an optional tree constant that they are equal to.  */
+
+class equiv_class2
+{
+public:
+  equiv_class2 ();
+  equiv_class2 (const equiv_class2 &other);
+
+  hashval_t hash () const;
+  bool operator== (const equiv_class2 &other);
+
+  void add (const svalue2 *sval);
+  bool del (const svalue2 *sval);
+
+  tree get_any_constant () const { return m_constant; }
+
+  const svalue2 *get_representative () const;
+
+  void canonicalize ();
+
+  void print (pretty_printer *pp) const;
+
+  /* An equivalence class can contain multiple constants (e.g. multiple
+     different zeroes, for different types); these are just for the last
+     constant added.  */
+  tree m_constant;
+  const svalue2 *m_cst_sval;
+
+  // TODO: should this be a set rather than a vec?
+  auto_vec<const svalue2 *> m_vars;
+};
+
+/* The various kinds of constraint.  */
+
+enum constraint2_op
+{
+  CONSTRAINT2_NE,
+  CONSTRAINT2_LT,
+  CONSTRAINT2_LE
+};
+
+const char *constraint_op_code (enum constraint2_op c_op);
+
+/* An ID for an equiv_class2 within a constraint_manager2.  Internally, this
+   is an index into a vector of equiv_class2 * within the constraint_manager2.  */
+
+class equiv_class2_id
+{
+public:
+  static equiv_class2_id null () { return equiv_class2_id (-1); }
+
+  equiv_class2_id (unsigned idx) : m_idx (idx) {}
+  const equiv_class2 &get_obj (const constraint_manager2 &cm) const;
+  equiv_class2 &get_obj (constraint_manager2 &cm) const;
+
+  bool operator== (const equiv_class2_id &other) const
+  {
+    return m_idx == other.m_idx;
+  }
+  bool operator!= (const equiv_class2_id &other) const
+  {
+    return m_idx != other.m_idx;
+  }
+
+  bool null_p () const { return m_idx == -1; }
+
+  static equiv_class2_id from_int (int idx) { return equiv_class2_id (idx); }
+  int as_int () const { return m_idx; }
+
+  void print (pretty_printer *pp) const;
+
+  void update_for_removal (equiv_class2_id other)
+  {
+    if (m_idx > other.m_idx)
+      m_idx--;
+  }
+
+  int m_idx;
+};
+
+/* A relationship between two equivalence classes in a constraint_manager2.  */
+
+class constraint2
+{
+ public:
+  constraint2 (equiv_class2_id lhs, enum constraint2_op c_op, equiv_class2_id rhs)
+  : m_lhs (lhs), m_op (c_op), m_rhs (rhs)
+  {
+    gcc_assert (!lhs.null_p ());
+    gcc_assert (!rhs.null_p ());
+  }
+
+  void print (pretty_printer *pp, const constraint_manager2 &cm) const;
+
+  hashval_t hash () const;
+  bool operator== (const constraint2 &other) const;
+
+  /* Is this an ordering, rather than a "!=".  */
+  bool is_ordering_p () const
+  {
+    return m_op != CONSTRAINT2_NE;
+  }
+
+  bool implied_by (const constraint2 &other,
+		   const constraint_manager2 &cm) const;
+
+  equiv_class2_id m_lhs;
+  enum constraint2_op m_op;
+  equiv_class2_id m_rhs;
+};
+
+/* An abstract base class for use with constraint_manager2::for_each_fact.  */
+
+class fact2_visitor
+{
+ public:
+  virtual ~fact2_visitor () {}
+  virtual void on_fact (const svalue2 *lhs,
+			enum tree_code,
+			const svalue2 *rhs) = 0;
+};
+
+/* A collection of equivalence classes and constraints on them.
+
+   Given N svalue2s, this can be thought of as representing a subset of
+   N-dimensional space.  When we call add_constraint,
+   we are effectively taking an intersection with that constraint.  */
+
+class constraint_manager2
+{
+public:
+  constraint_manager2 (region_model2_manager *mgr) : m_mgr (mgr) {}
+  constraint_manager2 (const constraint_manager2 &other);
+  virtual ~constraint_manager2 () {}
+
+#if 0
+  virtual constraint_manager2 *clone (region_model2 *) const = 0;
+#endif
+  constraint_manager2& operator= (const constraint_manager2 &other);
+
+  hashval_t hash () const;
+  bool operator== (const constraint_manager2 &other) const;
+  bool operator!= (const constraint_manager2 &other) const
+  {
+    return !(*this == other);
+  }
+
+  void print (pretty_printer *pp) const;
+  void dump_to_pp (pretty_printer *pp, bool multiline) const;
+  void dump (FILE *fp) const;
+  void dump () const;
+
+  const equiv_class2 &get_equiv_class2_by_index (unsigned idx) const
+  {
+    return *m_equiv_classes[idx];
+  }
+  equiv_class2 &get_equiv_class2_by_index (unsigned idx)
+  {
+    return *m_equiv_classes[idx];
+  }
+
+  equiv_class2 &get_equiv_class2 (const svalue2 *sval)
+  {
+    equiv_class2_id ec_id = get_or_add_equiv_class2 (sval);
+    return ec_id.get_obj (*this);
+  }
+
+  bool add_constraint (const svalue2 *lhs,
+		       enum tree_code op,
+		       const svalue2 *rhs);
+
+  bool add_constraint (equiv_class2_id lhs_ec_id,
+		       enum tree_code op,
+		       equiv_class2_id rhs_ec_id);
+
+  void add_unknown_constraint (equiv_class2_id lhs_ec_id,
+			       enum tree_code op,
+			       equiv_class2_id rhs_ec_id);
+
+  bool get_equiv_class2_by_svalue2 (const svalue2 *sval,
+				    equiv_class2_id *out) const;
+  equiv_class2_id get_or_add_equiv_class2 (const svalue2 *sval);
+  tristate eval_condition (equiv_class2_id lhs,
+			   enum tree_code op,
+			   equiv_class2_id rhs) const;
+  tristate eval_condition (equiv_class2_id lhs_ec,
+			   enum tree_code op,
+			   tree rhs_const) const;
+  tristate eval_condition (const svalue2 *lhs,
+			   enum tree_code op,
+			   const svalue2 *rhs) const;
+  range2 get_ec_bounds (equiv_class2_id ec_id) const;
+
+  /* PurgeCriteria should have:
+     bool should_purge_p (const svalue2 *sval) const.  */
+  template <typename PurgeCriteria>
+  void purge (const PurgeCriteria &p, purge_stats2 *stats);
+
+  void on_liveness_change (const hash_set<const svalue2 *> &live_svalues,
+			   const region_model2 *model);
+
+  void canonicalize ();
+
+  static void merge (const constraint_manager2 &cm_a,
+		     const constraint_manager2 &cm_b,
+		     constraint_manager2 *out,
+		     const model2_merger &merger);
+
+  void for_each_fact (fact2_visitor *) const;
+
+  void validate () const;
+
+  auto_delete_vec<equiv_class2> m_equiv_classes;
+  auto_vec<constraint2> m_constraints;
+
+ private:
+  static void clean_merger_input (const constraint_manager2 &cm_in,
+				  constraint_manager2 *out);
+
+  void add_constraint_internal (equiv_class2_id lhs_id,
+				enum constraint2_op c_op,
+				equiv_class2_id rhs_id);
+
+  region_model2_manager *m_mgr;
+};
+
 } // namespace ana
 
-#endif /* GCC_ANALYZER_CONSTRAINT_MANAGER_H */
+#endif /* GCC_ANALYZER_CONSTRAINT_MANAGER2_H */
diff --git a/gcc/analyzer/constraint-manager2.cc b/gcc/analyzer/constraint-manager2.cc
deleted file mode 100644
index 4ef85eff629..00000000000
--- a/gcc/analyzer/constraint-manager2.cc
+++ /dev/null
@@ -1,2897 +0,0 @@
-/* Tracking equivalence classes and constraints at a point on an execution path.
-   Copyright (C) 2019-2020 Free Software Foundation, Inc.
-   Contributed by David Malcolm <dmalcolm@redhat.com>.
-
-This file is part of GCC.
-
-GCC is free software; you can redistribute it and/or modify it
-under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 3, or (at your option)
-any later version.
-
-GCC is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING3.  If not see
-<http://www.gnu.org/licenses/>.  */
-
-#include "config.h"
-#include "system.h"
-#include "coretypes.h"
-#include "tree.h"
-#include "function.h"
-#include "basic-block.h"
-#include "gimple.h"
-#include "gimple-iterator.h"
-#include "fold-const.h"
-#include "selftest.h"
-#include "diagnostic-core.h"
-#include "graphviz.h"
-#include "function.h"
-#include "analyzer/analyzer.h"
-#include "ordered-hash-map.h"
-#include "options.h"
-#include "cgraph.h"
-#include "cfg.h"
-#include "digraph.h"
-#include "analyzer/supergraph.h"
-#include "sbitmap.h"
-#include "bitmap.h"
-#include "tristate.h"
-#include "analyzer/region-model.h" // for one_way_id_map
-#include "analyzer/region-model2.h"
-#include "analyzer/constraint-manager2.h"
-#include "analyzer/analyzer-selftests.h"
-
-/* TODO:
-   presumably best to drop equiv_class_id in favor of just equiv_class *,
-   so that there's no ordering.
-   But maybe a deeper reworking is needed?  */
-
-#if ENABLE_ANALYZER
-
-namespace ana {
-
-static tristate
-compare_constants (tree lhs_const, enum tree_code op, tree rhs_const)
-{
-  tree comparison
-    = fold_binary (op, boolean_type_node, lhs_const, rhs_const);
-  if (comparison == boolean_true_node)
-    return tristate (tristate::TS_TRUE);
-  if (comparison == boolean_false_node)
-    return tristate (tristate::TS_FALSE);
-  return tristate (tristate::TS_UNKNOWN);
-}
-
-/* struct bound2.  */
-
-/* Ensure that this bound2 is closed by converting an open bound2 to a
-   closed one.  */
-
-void
-bound2::ensure_closed (bool is_upper)
-{
-  if (!m_closed)
-    {
-      /* Offset by 1 in the appropriate direction.
-	 For example, convert 3 < x into 4 <= x,
-	 and convert x < 5 into x <= 4.  */
-      gcc_assert (CONSTANT_CLASS_P (m_constant));
-      m_constant = fold_build2 (is_upper ? MINUS_EXPR : PLUS_EXPR,
-				TREE_TYPE (m_constant),
-				m_constant, integer_one_node);
-      gcc_assert (CONSTANT_CLASS_P (m_constant));
-      m_closed = true;
-    }
-}
-
-/* Get "<=" vs "<" for this bound2.  */
-
-const char *
-bound2::get_relation_as_str () const
-{
-  if (m_closed)
-    return "<=";
-  else
-    return "<";
-}
-
-/* struct range2.  */
-
-/* Dump this range2 to PP, which must support %E for tree.  */
-
-void
-range2::dump_to_pp (pretty_printer *pp) const
-{
-  if (m_lower_bound.m_constant)
-    {
-      if (m_upper_bound.m_constant)
-	pp_printf (pp, "%qE %s x %s %qE",
-		   m_lower_bound.m_constant,
-		   m_lower_bound.get_relation_as_str (),
-		   m_upper_bound.get_relation_as_str (),
-		   m_upper_bound.m_constant);
-      else
-	pp_printf (pp, "%qE %s x",
-		   m_lower_bound.m_constant,
-		   m_lower_bound.get_relation_as_str ());
-    }
-  else
-    {
-      if (m_upper_bound.m_constant)
-	pp_printf (pp, "x %s %qE",
-		   m_upper_bound.get_relation_as_str (),
-		   m_upper_bound.m_constant);
-      else
-	pp_string (pp, "x");
-    }
-}
-
-/* Dump this range2 to stderr.  */
-
-DEBUG_FUNCTION void
-range2::dump () 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);
-  pp_newline (&pp);
-  pp_flush (&pp);
-}
-
-/* Determine if there is only one possible value for this range2.
-   If so, return the constant; otherwise, return NULL_TREE.  */
-
-tree
-range2::constrained_to_single_element ()
-{
-  if (m_lower_bound.m_constant == NULL_TREE
-      || m_upper_bound.m_constant == NULL_TREE)
-    return NULL_TREE;
-
-  if (!INTEGRAL_TYPE_P (TREE_TYPE (m_lower_bound.m_constant)))
-    return NULL_TREE;
-  if (!INTEGRAL_TYPE_P (TREE_TYPE (m_upper_bound.m_constant)))
-    return NULL_TREE;
-
-  /* Convert any open bounds to closed bounds.  */
-  m_lower_bound.ensure_closed (false);
-  m_upper_bound.ensure_closed (true);
-
-  // Are they equal?
-  tree comparison = fold_binary (EQ_EXPR, boolean_type_node,
-				 m_lower_bound.m_constant,
-				 m_upper_bound.m_constant);
-  if (comparison == boolean_true_node)
-    return m_lower_bound.m_constant;
-  else
-    return NULL_TREE;
-}
-
-/* Eval the condition "X OP RHS_CONST" for X within the range.  */
-
-tristate
-range2::eval_condition (enum tree_code op,
-			tree rhs_const) const
-{
-  range2 copy (*this);
-  if (tree single_element = copy.constrained_to_single_element ())
-    return compare_constants (single_element, op, rhs_const);
-
-  switch (op)
-    {
-    case EQ_EXPR:
-      if (below_lower_bound (rhs_const))
-	return tristate (tristate::TS_FALSE);
-      if (above_upper_bound (rhs_const))
-	return tristate (tristate::TS_FALSE);
-      break;
-
-    case LT_EXPR:
-      // TODO: distinction between LT and GE
-    case LE_EXPR:
-      /* Qn: "X < RHS_CONST".  */
-      /* If RHS_CONST > upper bound, then it's true.
-	 If RHS_CONST < lower bound, then it's false.
-	 Otherwise unknown.  */
-      if (above_upper_bound (rhs_const))
-	return tristate (tristate::TS_TRUE);
-      if (below_lower_bound (rhs_const))
-	return tristate (tristate::TS_FALSE);
-      break;
-
-    case NE_EXPR:
-      /* Qn: "X != RHS_CONST".  */
-      /* If RHS_CONST < lower bound, then it's true.
-	 If RHS_CONST > upper bound, then it's false.
-	 Otherwise unknown.  */
-      if (below_lower_bound (rhs_const))
-	return tristate (tristate::TS_TRUE);
-      if (above_upper_bound (rhs_const))
-	return tristate (tristate::TS_TRUE);
-      break;
-
-    case GE_EXPR:
-      // TODO: distinction between GE and GT
-    case GT_EXPR:
-      /* Qn: "X > RHS_CONST".  */
-      if (above_upper_bound (rhs_const))
-	return tristate (tristate::TS_FALSE);
-      if (below_lower_bound (rhs_const))
-	return tristate (tristate::TS_TRUE);
-      break;
-
-    default:
-      gcc_unreachable (); // TODO
-      break;
-    }
-  return tristate (tristate::TS_UNKNOWN);
-}
-
-/* Return true if RHS_CONST is below the lower bound of this range.  */
-
-bool
-range2::below_lower_bound (tree rhs_const) const
-{
-  if (!m_lower_bound.m_constant)
-    return false;
-
-  return compare_constants (rhs_const,
-			    m_lower_bound.m_closed ? LT_EXPR : LE_EXPR,
-			    m_lower_bound.m_constant).is_true ();
-}
-
-/* Return true if RHS_CONST is above the upper bound of this range.  */
-
-bool
-range2::above_upper_bound (tree rhs_const) const
-{
-  if (!m_upper_bound.m_constant)
-    return false;
-
-  return compare_constants (rhs_const,
-			    m_upper_bound.m_closed ? GT_EXPR : GE_EXPR,
-			    m_upper_bound.m_constant).is_true ();
-}
-
-/* class equiv_class2.  */
-
-/* equiv_class2's default ctor.  */
-
-equiv_class2::equiv_class2 ()
-: m_constant (NULL_TREE), m_cst_sval (NULL), m_vars ()
-{
-}
-
-/* equiv_class2's copy ctor.  */
-
-equiv_class2::equiv_class2 (const equiv_class2 &other)
-: m_constant (other.m_constant), m_cst_sval (other.m_cst_sval),
-  m_vars (other.m_vars.length ())
-{
-  int i;
-  const svalue2 *sval;
-  FOR_EACH_VEC_ELT (other.m_vars, i, sval)
-    m_vars.quick_push (sval);
-}
-
-/* Print an all-on-one-line representation of this equiv_class2 to PP,
-   which must support %E for trees.  */
-
-void
-equiv_class2::print (pretty_printer *pp) const
-{
-  pp_character (pp, '{');
-  int i;
-  const svalue2 *sval;
-  FOR_EACH_VEC_ELT (m_vars, i, sval)
-    {
-      if (i > 0)
-	pp_string (pp, " == ");
-      sval->dump_to_pp (pp, true);
-    }
-  if (m_constant)
-    {
-      if (i > 0)
-	pp_string (pp, " == ");
-      pp_printf (pp, "[m_constant]%qE", m_constant);
-    }
-  pp_character (pp, '}');
-}
-
-/* Generate a hash value for this equiv_class2.  */
-
-hashval_t
-equiv_class2::hash () const
-{
-  inchash::hash hstate;
-  int i;
-  const svalue2 *sval;
-
-  inchash::add_expr (m_constant, hstate);
-  // TODO:
-#if 0
-  FOR_EACH_VEC_ELT (m_vars, i, sval)
-    inchash::add (sval, hstate);
-#endif
-  return hstate.end ();
-}
-
-/* Equality operator for equiv_class2.  */
-
-bool
-equiv_class2::operator== (const equiv_class2 &other)
-{
-  if (m_constant != other.m_constant)
-    return false; // TODO: use tree equality here?
-
-  /* FIXME: should we compare m_cst_sval?  */
-
-  if (m_vars.length () != other.m_vars.length ())
-    return false;
-
-  int i;
-  const svalue2 *sval;
-  FOR_EACH_VEC_ELT (m_vars, i, sval)
-    if (sval != other.m_vars[i])
-      return false;
-  // FIXME: ^^^ doesn't this rely on ordering
-
-  return true;
-}
-
-/* Add SID to this equiv_class2, using CM to check if it's a constant.  */
-
-void
-equiv_class2::add (const svalue2 *sval)
-{
-  gcc_assert (sval);
-  if (tree cst = sval->maybe_get_constant ())
-    {
-      gcc_assert (CONSTANT_CLASS_P (cst));
-      /* FIXME: should we canonicalize which svalue2 is the constant
-	 when there are multiple equal constants?  */
-      m_constant = cst;
-      m_cst_sval = sval;
-    }
-  m_vars.safe_push (sval);
-}
-
-/* Remove SID from this equivalence class.
-   Return true if SID was the last var in the equivalence class (suggesting
-   a possible leak).  */
-
-bool
-equiv_class2::del (const svalue2 *sval)
-{
-  gcc_assert (sval);
-  gcc_assert (sval != m_cst_sval);
-
-  int i;
-  const svalue2 *iv;
-  FOR_EACH_VEC_ELT (m_vars, i, iv)
-    {
-      if (iv == sval)
-	{
-	  m_vars[i] = m_vars[m_vars.length () - 1];
-	  m_vars.pop ();
-	  return m_vars.length () == 0;
-	}
-    }
-
-  /* SVAL must be in the class.  */
-  gcc_unreachable ();
-  return false;
-}
-
-/* Get a representative member of this class, for handling cases
-   where the IDs can change mid-traversal.  */
-
-const svalue2 *
-equiv_class2::get_representative () const
-{
-  gcc_assert (m_vars.length () > 0);
-  return m_vars[0];
-}
-
-/* Comparator for use by equiv_class2::canonicalize.  */
-
-static int
-svalue2_cmp_by_ptr (const void *p1, const void *p2)
-{
-  const svalue2 *sval1 = *(const svalue2 * const *)p1;
-  const svalue2 *sval2 = *(const svalue2 * const *)p2;
-  if (sval1 < sval2)
-    return 1;
-  if (sval1 > sval2)
-    return -1;
-  return 0;
-}
-
-/* Sort the svalue2s within this equiv_class2.  */
-
-void
-equiv_class2::canonicalize ()
-{
-  m_vars.qsort (svalue2_cmp_by_ptr);
-}
-
-/* Get a debug string for C_OP.  */
-
-const char *
-constraint2_op_code (enum constraint2_op c_op)
-{
-  switch (c_op)
-    {
-    default:
-      gcc_unreachable ();
-    case CONSTRAINT2_NE: return "!=";
-    case CONSTRAINT2_LT: return "<";
-    case CONSTRAINT2_LE: return "<=";
-    }
-}
-
-/* Convert C_OP to an enum tree_code.  */
-
-enum tree_code
-constraint2_tree_code (enum constraint2_op c_op)
-{
-  switch (c_op)
-    {
-    default:
-      gcc_unreachable ();
-    case CONSTRAINT2_NE: return NE_EXPR;
-    case CONSTRAINT2_LT: return LT_EXPR;
-    case CONSTRAINT2_LE: return LE_EXPR;
-    }
-}
-
-/* Given "lhs C_OP rhs", determine "lhs T_OP rhs".
-
-   For example, given "x < y", then "x > y" is false.  */
-
-static tristate
-eval_constraint_op_for_op (enum constraint2_op c_op, enum tree_code t_op)
-{
-  switch (c_op)
-    {
-    default:
-      gcc_unreachable ();
-    case CONSTRAINT2_NE:
-      if (t_op == EQ_EXPR)
-	return tristate (tristate::TS_FALSE);
-      if (t_op == NE_EXPR)
-	return tristate (tristate::TS_TRUE);
-      break;
-    case CONSTRAINT2_LT:
-      if (t_op == LT_EXPR || t_op == LE_EXPR || t_op == NE_EXPR)
-	return tristate (tristate::TS_TRUE);
-      if (t_op == EQ_EXPR || t_op == GT_EXPR || t_op == GE_EXPR)
-	return tristate (tristate::TS_FALSE);
-      break;
-    case CONSTRAINT2_LE:
-      if (t_op == LE_EXPR)
-	return tristate (tristate::TS_TRUE);
-      if (t_op == GT_EXPR)
-	return tristate (tristate::TS_FALSE);
-      break;
-    }
-  return tristate (tristate::TS_UNKNOWN);
-}
-
-/* class constraint2.  */
-
-/* Print this constraint2 to PP (which must support %E for trees),
-   using CM to look up equiv_class2 instances from ids.  */
-
-void
-constraint2::print (pretty_printer *pp, const constraint_manager2 &cm) const
-{
-  m_lhs.print (pp);
-  pp_string (pp, ": ");
-  m_lhs.get_obj (cm).print (pp);
-  pp_string (pp, " ");
-  pp_string (pp, constraint2_op_code (m_op));
-  pp_string (pp, " ");
-  m_rhs.print (pp);
-  pp_string (pp, ": ");
-  m_rhs.get_obj (cm).print (pp);
-}
-
-/* Generate a hash value for this constraint2.  */
-
-hashval_t
-constraint2::hash () const
-{
-  inchash::hash hstate;
-  hstate.add_int (m_lhs.m_idx);
-  hstate.add_int (m_op);
-  hstate.add_int (m_rhs.m_idx);
-  return hstate.end ();
-}
-
-/* Equality operator for constraint2s.  */
-
-bool
-constraint2::operator== (const constraint2 &other) const
-{
-  if (m_lhs != other.m_lhs)
-    return false;
-  if (m_op != other.m_op)
-    return false;
-  if (m_rhs != other.m_rhs)
-    return false;
-  return true;
-}
-
-/* Return true if this constraint is implied by OTHER.  */
-
-bool
-constraint2::implied_by (const constraint2 &other,
-			 const constraint_manager2 &cm) const
-{
-  if (m_lhs == other.m_lhs)
-    if (tree rhs_const = m_rhs.get_obj (cm).get_any_constant ())
-      if (tree other_rhs_const = other.m_rhs.get_obj (cm).get_any_constant ())
-	if (m_lhs.get_obj (cm).get_any_constant () == NULL_TREE)
-	  if (m_op == other.m_op)
-	    switch (m_op)
-	      {
-	      default:
-		break;
-	      case CONSTRAINT2_LE:
-	      case CONSTRAINT2_LT:
-		if (compare_constants (rhs_const,
-				       GE_EXPR,
-				       other_rhs_const).is_true ())
-		  return true;
-		break;
-	      }
-  return false;
-}
-
-/* class equiv_class2_id.  */
-
-/* Get the underlying equiv_class2 for this ID from CM.  */
-
-const equiv_class2 &
-equiv_class2_id::get_obj (const constraint_manager2 &cm) const
-{
-  return cm.get_equiv_class2_by_index (m_idx);
-}
-
-/* Access the underlying equiv_class2 for this ID from CM.  */
-
-equiv_class2 &
-equiv_class2_id::get_obj (constraint_manager2 &cm) const
-{
-  return cm.get_equiv_class2_by_index (m_idx);
-}
-
-/* Print this equiv_class2_id to PP.  */
-
-void
-equiv_class2_id::print (pretty_printer *pp) const
-{
-  if (null_p ())
-    pp_printf (pp, "null");
-  else
-    pp_printf (pp, "ec%i", m_idx);
-}
-
-/* class constraint_manager2.  */
-
-/* constraint_manager2's copy ctor.  */
-
-constraint_manager2::constraint_manager2 (const constraint_manager2 &other)
-: m_equiv_classes (other.m_equiv_classes.length ()),
-  m_constraints (other.m_constraints.length ()),
-  m_mgr (other.m_mgr)
-{
-  int i;
-  equiv_class2 *ec;
-  FOR_EACH_VEC_ELT (other.m_equiv_classes, i, ec)
-    m_equiv_classes.quick_push (new equiv_class2 (*ec));
-  constraint2 *c;
-  FOR_EACH_VEC_ELT (other.m_constraints, i, c)
-    m_constraints.quick_push (*c);
-}
-
-/* constraint_manager2's assignment operator.  */
-
-constraint_manager2&
-constraint_manager2::operator= (const constraint_manager2 &other)
-{
-  gcc_assert (m_equiv_classes.length () == 0);
-  gcc_assert (m_constraints.length () == 0);
-
-  int i;
-  equiv_class2 *ec;
-  m_equiv_classes.reserve (other.m_equiv_classes.length ());
-  FOR_EACH_VEC_ELT (other.m_equiv_classes, i, ec)
-    m_equiv_classes.quick_push (new equiv_class2 (*ec));
-  constraint2 *c;
-  m_constraints.reserve (other.m_constraints.length ());
-  FOR_EACH_VEC_ELT (other.m_constraints, i, c)
-    m_constraints.quick_push (*c);
-
-  return *this;
-}
-
-/* Generate a hash value for this constraint_manager2.  */
-
-hashval_t
-constraint_manager2::hash () const
-{
-  inchash::hash hstate;
-  int i;
-  equiv_class2 *ec;
-  constraint2 *c;
-
-  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
-    hstate.merge_hash (ec->hash ());
-  FOR_EACH_VEC_ELT (m_constraints, i, c)
-    hstate.merge_hash (c->hash ());
-  return hstate.end ();
-}
-
-/* Equality operator for constraint_manager2.  */
-
-bool
-constraint_manager2::operator== (const constraint_manager2 &other) const
-{
-  if (m_equiv_classes.length () != other.m_equiv_classes.length ())
-    return false;
-  if (m_constraints.length () != other.m_constraints.length ())
-    return false;
-
-  int i;
-  equiv_class2 *ec;
-
-  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
-    if (!(*ec == *other.m_equiv_classes[i]))
-      return false;
-
-  constraint2 *c;
-
-  FOR_EACH_VEC_ELT (m_constraints, i, c)
-    if (!(*c == other.m_constraints[i]))
-      return false;
-
-  return true;
-}
-
-/* Print this constraint_manager2 to PP (which must support %E for trees).  */
-
-void
-constraint_manager2::print (pretty_printer *pp) const
-{
-  pp_string (pp, "{");
-  int i;
-  equiv_class2 *ec;
-  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
-    {
-      if (i > 0)
-	pp_string (pp, ", ");
-      equiv_class2_id (i).print (pp);
-      pp_string (pp, ": ");
-      ec->print (pp);
-    }
-  pp_string (pp, "  |  ");
-  constraint2 *c;
-  FOR_EACH_VEC_ELT (m_constraints, i, c)
-    {
-      if (i > 0)
-	pp_string (pp, " && ");
-      c->print (pp, *this);
-    }
-  pp_printf (pp, "}");
-}
-
-/* Dump a representation of this constraint_manager2 to PP
-   (which must support %E for trees).  */
-
-void
-constraint_manager2::dump_to_pp (pretty_printer *pp, bool multiline) const
-{
-  // TODO
-  if (multiline)
-    pp_string (pp, "  ");
-  pp_string (pp, "equiv classes:");
-  if (multiline)
-    pp_newline (pp);
-  else
-    pp_string (pp, " {");
-  int i;
-  equiv_class2 *ec;
-  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
-    {
-      if (multiline)
-	pp_string (pp, "    ");
-      else if (i > 0)
-	pp_string (pp, ", ");
-      equiv_class2_id (i).print (pp);
-      pp_string (pp, ": ");
-      ec->print (pp);
-      if (multiline)
-	pp_newline (pp);
-    }
-  if (multiline)
-    pp_string (pp, "  ");
-  else
-    pp_string (pp, "}");
-  pp_string (pp, "constraints:");
-  if (multiline)
-    pp_newline (pp);
-  else
-    pp_string (pp, "{");
-  constraint2 *c;
-  FOR_EACH_VEC_ELT (m_constraints, i, c)
-    {
-      if (multiline)
-	pp_string (pp, "    ");
-      pp_printf (pp, "%i: ", i);
-      c->print (pp, *this);
-      if (multiline)
-	pp_newline (pp);
-    }
-  if (!multiline)
-    pp_string (pp, "}");
-}
-
-/* Dump a multiline representation of this constraint_manager2 to FP.  */
-
-void
-constraint_manager2::dump (FILE *fp) const
-{
-  pretty_printer pp;
-  pp_format_decoder (&pp) = default_tree_printer;
-  pp_show_color (&pp) = pp_show_color (global_dc->printer);
-  pp.buffer->stream = fp;
-  dump_to_pp (&pp, true);
-  pp_flush (&pp);
-}
-
-/* Dump a multiline representation of this constraint_manager2 to stderr.  */
-
-DEBUG_FUNCTION void
-constraint_manager2::dump () const
-{
-  dump (stderr);
-}
-
-/* Dump a multiline representation of CM to stderr.  */
-
-DEBUG_FUNCTION void
-debug (const constraint_manager2 &cm)
-{
-  cm.dump ();
-}
-
-/* Attempt to add the constraint LHS OP RHS to this constraint_manager2.
-   Return true if the constraint could be added (or is already true).
-   Return false if the constraint contradicts existing knowledge.  */
-
-bool
-constraint_manager2::add_constraint (const svalue2 *lhs,
-				     enum tree_code op,
-				     const svalue2 *rhs)
-{
-  lhs = lhs->unwrap_any_unmergeable ();
-  rhs = rhs->unwrap_any_unmergeable ();
-
-  /* Nothing can be known about unknown values.  */
-  if (lhs->get_kind () == svalue2::SK_UNKNOWN
-      || rhs->get_kind () == svalue2::SK_UNKNOWN)
-    /* Not a contradiction.  */
-    return true;
-
-  /* Check the conditions on svalues.  */
-  {
-    tristate t_cond = eval_condition (lhs, op, rhs);
-
-    /* If we already have the condition, do nothing.  */
-    if (t_cond.is_true ())
-      return true;
-
-    /* Reject a constraint that would contradict existing knowledge, as
-       unsatisfiable.  */
-    if (t_cond.is_false ())
-      return false;
-  }
-
-  equiv_class2_id lhs_ec_id = get_or_add_equiv_class2 (lhs);
-  equiv_class2_id rhs_ec_id = get_or_add_equiv_class2 (rhs);
-
-  /* Check the stronger conditions on ECs.  */
-  {
-    tristate t = eval_condition (lhs_ec_id, op, rhs_ec_id);
-
-    /* Discard constraints that are already known.  */
-    if (t.is_true ())
-      return true;
-
-    /* Reject unsatisfiable constraints.  */
-    if (t.is_false ())
-      return false;
-  }
-
-  add_unknown_constraint (lhs_ec_id, op, rhs_ec_id);
-  return true;
-}
-
-/* Attempt to add the constraint LHS_EC_ID OP RHS_EC_ID to this
-   constraint_manager2.
-   Return true if the constraint could be added (or is already true).
-   Return false if the constraint contradicts existing knowledge.  */
-
-bool
-constraint_manager2::add_constraint (equiv_class2_id lhs_ec_id,
-				     enum tree_code op,
-				     equiv_class2_id rhs_ec_id)
-{
-  tristate t = eval_condition (lhs_ec_id, op, rhs_ec_id);
-
-  /* Discard constraints that are already known.  */
-  if (t.is_true ())
-    return true;
-
-  /* Reject unsatisfiable constraints.  */
-  if (t.is_false ())
-    return false;
-
-  add_unknown_constraint (lhs_ec_id, op, rhs_ec_id);
-  return true;
-}
-
-/* Add the constraint LHS_EC_ID OP RHS_EC_ID to this constraint_manager2,
-   where the constraint has already been checked for being "unknown".  */
-
-void
-constraint_manager2::add_unknown_constraint (equiv_class2_id lhs_ec_id,
-					     enum tree_code op,
-					     equiv_class2_id rhs_ec_id)
-{
-  gcc_assert (lhs_ec_id != rhs_ec_id);
-
-  /* For now, simply accumulate constraints, without attempting any further
-     optimization.  */
-  switch (op)
-    {
-    case EQ_EXPR:
-      {
-	/* Merge rhs_ec into lhs_ec.  */
-	equiv_class2 &lhs_ec_obj = lhs_ec_id.get_obj (*this);
-	const equiv_class2 &rhs_ec_obj = rhs_ec_id.get_obj (*this);
-
-	int i;
-	const svalue2 *sval;
-	FOR_EACH_VEC_ELT (rhs_ec_obj.m_vars, i, sval)
-	  lhs_ec_obj.add (sval);
-
-	if (rhs_ec_obj.m_constant)
-	  {
-	    lhs_ec_obj.m_constant = rhs_ec_obj.m_constant;
-	    lhs_ec_obj.m_cst_sval = rhs_ec_obj.m_cst_sval;
-	  }
-
-	/* Drop rhs equivalence class, overwriting it with the
-	   final ec (which might be the same one).  */
-	equiv_class2_id final_ec_id = m_equiv_classes.length () - 1;
-	equiv_class2 *old_ec = m_equiv_classes[rhs_ec_id.m_idx];
-	equiv_class2 *final_ec = m_equiv_classes.pop ();
-	if (final_ec != old_ec)
-	  m_equiv_classes[rhs_ec_id.m_idx] = final_ec;
-	delete old_ec;
-
-	/* Update the constraints.  */
-	constraint2 *c;
-	FOR_EACH_VEC_ELT (m_constraints, i, c)
-	  {
-	    /* Update references to the rhs_ec so that
-	       they refer to the lhs_ec.  */
-	    if (c->m_lhs == rhs_ec_id)
-	      c->m_lhs = lhs_ec_id;
-	    if (c->m_rhs == rhs_ec_id)
-	      c->m_rhs = lhs_ec_id;
-
-	    /* Renumber all constraints that refer to the final rhs_ec
-	       to the old rhs_ec, where the old final_ec now lives.  */
-	    if (c->m_lhs == final_ec_id)
-	      c->m_lhs = rhs_ec_id;
-	    if (c->m_rhs == final_ec_id)
-	      c->m_rhs = rhs_ec_id;
-	  }
-
-	/* We may now have self-comparisons due to the merger; these
-	   constraints should be removed.  */
-	unsigned read_index, write_index;
-	VEC_ORDERED_REMOVE_IF (m_constraints, read_index, write_index, c,
-			       (c->m_lhs == c->m_rhs));
-      }
-      break;
-    case GE_EXPR:
-      add_constraint_internal (rhs_ec_id, CONSTRAINT2_LE, lhs_ec_id);
-      break;
-    case LE_EXPR:
-      add_constraint_internal (lhs_ec_id, CONSTRAINT2_LE, rhs_ec_id);
-      break;
-    case NE_EXPR:
-      add_constraint_internal (lhs_ec_id, CONSTRAINT2_NE, rhs_ec_id);
-      break;
-    case GT_EXPR:
-      add_constraint_internal (rhs_ec_id, CONSTRAINT2_LT, lhs_ec_id);
-      break;
-    case LT_EXPR:
-      add_constraint_internal (lhs_ec_id, CONSTRAINT2_LT, rhs_ec_id);
-      break;
-    default:
-      /* do nothing.  */
-      break;
-    }
-  validate ();
-}
-
-/* Subroutine of constraint_manager2::add_constraint, for handling all
-   operations other than equality (for which equiv classes are merged).  */
-
-void
-constraint_manager2::add_constraint_internal (equiv_class2_id lhs_id,
-					      enum constraint2_op c_op,
-					      equiv_class2_id rhs_id)
-{
-  constraint2 new_c (lhs_id, c_op, rhs_id);
-
-  /* Remove existing constraints that would be implied by the
-     new constraint.  */
-  unsigned read_index, write_index;
-  constraint2 *c;
-  VEC_ORDERED_REMOVE_IF (m_constraints, read_index, write_index, c,
-			 (c->implied_by (new_c, *this)));
-
-  /* Add the constraint.  */
-  m_constraints.safe_push (new_c);
-
-  if (!flag_analyzer_transitivity)
-    return;
-
-  if (c_op != CONSTRAINT2_NE)
-    {
-      /* The following can potentially add EQ_EXPR facts, which could lead
-	 to ECs being merged, which would change the meaning of the EC IDs.
-	 Hence we need to do this via representatives.  */
-      const svalue2 *lhs = lhs_id.get_obj (*this).get_representative ();
-      const svalue2 *rhs = rhs_id.get_obj (*this).get_representative ();
-
-      /* We have LHS </<= RHS */
-
-      /* Handle transitivity of ordering by adding additional constraints
-	 based on what we already knew.
-
-	 So if we have already have:
-	   (a < b)
-	   (c < d)
-	 Then adding:
-	   (b < c)
-	 will also add:
-	   (a < c)
-	   (b < d)
-	 We need to recurse to ensure we also add:
-	   (a < d).
-	 We call the checked add_constraint to avoid adding constraints
-	 that are already present.  Doing so also ensures termination
-	 in the case of cycles.
-
-	 We also check for single-element ranges, adding EQ_EXPR facts
-	 where we discover them.  For example 3 < x < 5 implies
-	 that x == 4 (if x is an integer).  */
-      for (unsigned i = 0; i < m_constraints.length (); i++)
-	{
-	  const constraint2 *other = &m_constraints[i];
-	  if (other->is_ordering_p ())
-	    {
-	      /* Refresh the EC IDs, in case any mergers have happened.  */
-	      lhs_id = get_or_add_equiv_class2 (lhs);
-	      rhs_id = get_or_add_equiv_class2 (rhs);
-
-	      tree lhs_const = lhs_id.get_obj (*this).m_constant;
-	      tree rhs_const = rhs_id.get_obj (*this).m_constant;
-	      tree other_lhs_const
-		= other->m_lhs.get_obj (*this).m_constant;
-	      tree other_rhs_const
-		= other->m_rhs.get_obj (*this).m_constant;
-
-	      /* We have "LHS </<= RHS" and "other.lhs </<= other.rhs".  */
-
-	      /* If we have LHS </<= RHS and RHS </<= LHS, then we have a
-		 cycle.  */
-	      if (rhs_id == other->m_lhs
-		  && other->m_rhs == lhs_id)
-		{
-		  /* We must have equality for this to be possible.  */
-		  gcc_assert (c_op == CONSTRAINT2_LE
-			      && other->m_op == CONSTRAINT2_LE);
-		  add_constraint (lhs_id, EQ_EXPR, rhs_id);
-		  /* Adding an equality will merge the two ECs and potentially
-		     reorganize the constraints.  Stop iterating.  */
-		  return;
-		}
-	      /* Otherwise, check for transitivity.  */
-	      if (rhs_id == other->m_lhs)
-		{
-		  /* With RHS == other.lhs, we have:
-		     "LHS </<= (RHS, other.lhs) </<= other.rhs"
-		     and thus this implies "LHS </<= other.rhs".  */
-
-		  /* Do we have a tightly-constrained range?  */
-		  if (lhs_const
-		      && !rhs_const
-		      && other_rhs_const)
-		    {
-		      range2 r (bound2 (lhs_const, c_op == CONSTRAINT2_LE),
-			       bound2 (other_rhs_const,
-				      other->m_op == CONSTRAINT2_LE));
-		      if (tree constant = r.constrained_to_single_element ())
-			{
-			  const svalue2 *cst_sval
-			    = m_mgr->get_or_create_constant_svalue2 (constant);
-			  add_constraint
-			    (rhs_id, EQ_EXPR,
-			     get_or_add_equiv_class2 (cst_sval));
-			  return;
-			}
-		    }
-
-		  /* Otherwise, add the constraint implied by transitivity.  */
-		  enum tree_code new_op
-		    = ((c_op == CONSTRAINT2_LE && other->m_op == CONSTRAINT2_LE)
-		       ? LE_EXPR : LT_EXPR);
-		  add_constraint (lhs_id, new_op, other->m_rhs);
-		}
-	      else if (other->m_rhs == lhs_id)
-		{
-		  /* With other.rhs == LHS, we have:
-		     "other.lhs </<= (other.rhs, LHS) </<= RHS"
-		     and thus this implies "other.lhs </<= RHS".  */
-
-		  /* Do we have a tightly-constrained range?  */
-		  if (other_lhs_const
-		      && !lhs_const
-		      && rhs_const)
-		    {
-		      range2 r (bound2 (other_lhs_const,
-				      other->m_op == CONSTRAINT2_LE),
-			       bound2 (rhs_const,
-				      c_op == CONSTRAINT2_LE));
-		      if (tree constant = r.constrained_to_single_element ())
-			{
-			  const svalue2 *cst_sval
-			    = m_mgr->get_or_create_constant_svalue2 (constant);
-			  add_constraint
-			    (lhs_id, EQ_EXPR,
-			     get_or_add_equiv_class2 (cst_sval));
-			  return;
-			}
-		    }
-
-		  /* Otherwise, add the constraint implied by transitivity.  */
-		  enum tree_code new_op
-		    = ((c_op == CONSTRAINT2_LE && other->m_op == CONSTRAINT2_LE)
-		       ? LE_EXPR : LT_EXPR);
-		  add_constraint (other->m_lhs, new_op, rhs_id);
-		}
-	    }
-	}
-    }
-}
-
-/* Look for SVAL within the equivalence classes of this constraint_manager2;
-   if found, return true, writing the id to *OUT if OUT is non-NULL,
-   otherwise return false.  */
-
-bool
-constraint_manager2::get_equiv_class2_by_svalue2 (const svalue2 *sval,
-						  equiv_class2_id *out) const
-{
-  /* TODO: should we have a map, rather than these searches?  */
-  int i;
-  equiv_class2 *ec;
-  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
-    {
-      int j;
-      const svalue2 *iv;
-      FOR_EACH_VEC_ELT (ec->m_vars, j, iv)
-	if (iv == sval)
-	  {
-	    if (out)
-	      *out = equiv_class2_id (i);
-	    return true;
-	  }
-    }
-  return false;
-}
-
-/* Ensure that SVAL has an equivalence class within this constraint_manager2;
-   return the ID of the class.  */
-
-equiv_class2_id
-constraint_manager2::get_or_add_equiv_class2 (const svalue2 *sval)
-{
-  equiv_class2_id result (-1);
-
-  gcc_assert (sval->get_kind () != svalue2::SK_UNKNOWN);
-
-  /* Convert all NULL pointers to (void *) to avoid state explosions
-     involving all of the various (foo *)NULL vs (bar *)NULL.  */
-#if NULL_POINTER_TYPE_CONSOLIDATION
-  if (POINTER_TYPE_P (sval->get_type ()))
-    if (tree cst = sval->maybe_get_constant ())
-      if (zerop (cst))
-	sval = m_mgr->get_or_create_constant_svalue2 (null_pointer_node);
-#endif
-
-  /* Try svalue2 match.  */
-  if (get_equiv_class2_by_svalue2 (sval, &result))
-    return result;
-
-  /* Try equality of constants.  */
-  if (tree cst = sval->maybe_get_constant ())
-    {
-      int i;
-      equiv_class2 *ec;
-      FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
-	if (ec->m_constant
-	    && types_compatible_p (TREE_TYPE (cst),
-				   TREE_TYPE (ec->m_constant)))
-	  {
-	    tree eq = fold_binary (EQ_EXPR, boolean_type_node,
-				   cst, ec->m_constant);
-	    if (eq == boolean_true_node)
-	      {
-		ec->add (sval);
-		return equiv_class2_id (i);
-	      }
-	  }
-    }
-
-
-  /* Not found.  */
-  equiv_class2 *new_ec = new equiv_class2 ();
-  new_ec->add (sval);
-  m_equiv_classes.safe_push (new_ec);
-
-  equiv_class2_id new_id (m_equiv_classes.length () - 1);
-
-  if (sval->maybe_get_constant ())
-    {
-      /* If we have a new EC for a constant, add constraints comparing this
-	 to other constants we may have (so that we accumulate the transitive
-	 closure of all constraints on constants as the constants are
-	 added).  */
-      for (equiv_class2_id other_id (0); other_id.m_idx < new_id.m_idx;
-	   other_id.m_idx++)
-	{
-	  const equiv_class2 &other_ec = other_id.get_obj (*this);
-	  if (other_ec.m_constant
-	      && types_compatible_p (TREE_TYPE (new_ec->m_constant),
-				     TREE_TYPE (other_ec.m_constant)))
-	    {
-	      /* If we have two ECs, both with constants, the constants must be
-		 non-equal (or they would be in the same EC).
-		 Determine the direction of the inequality, and record that
-		 fact.  */
-	      tree lt
-		= fold_binary (LT_EXPR, boolean_type_node,
-			       new_ec->m_constant, other_ec.m_constant);
-	      if (lt == boolean_true_node)
-		add_constraint_internal (new_id, CONSTRAINT2_LT, other_id);
-	      else if (lt == boolean_false_node)
-		add_constraint_internal (other_id, CONSTRAINT2_LT, new_id);
-	      /* Refresh new_id, in case ECs were merged.  SVAL should always
-		 be present by now, so this should never lead to a
-		 recursion.  */
-	      new_id = get_or_add_equiv_class2 (sval);
-	    }
-	}
-    }
-
-  return new_id;
-}
-
-/* Evaluate the condition LHS_EC OP RHS_EC.  */
-
-tristate
-constraint_manager2::eval_condition (equiv_class2_id lhs_ec,
-				    enum tree_code op,
-				    equiv_class2_id rhs_ec) const
-{
-  if (lhs_ec == rhs_ec)
-    {
-      switch (op)
-	{
-	case EQ_EXPR:
-	case GE_EXPR:
-	case LE_EXPR:
-	  return tristate (tristate::TS_TRUE);
-
-	case NE_EXPR:
-	case GT_EXPR:
-	case LT_EXPR:
-	  return tristate (tristate::TS_FALSE);
-	default:
-	  break;
-	}
-    }
-
-  tree lhs_const = lhs_ec.get_obj (*this).get_any_constant ();
-  tree rhs_const = rhs_ec.get_obj (*this).get_any_constant ();
-  if (lhs_const && rhs_const)
-    {
-      tristate result_for_constants
-	= compare_constants (lhs_const, op, rhs_const);
-      if (result_for_constants.is_known ())
-	return result_for_constants;
-    }
-
-  enum tree_code swapped_op = swap_tree_comparison (op);
-
-  int i;
-  constraint2 *c;
-  FOR_EACH_VEC_ELT (m_constraints, i, c)
-    {
-      if (c->m_lhs == lhs_ec
-	  && c->m_rhs == rhs_ec)
-	{
-	  tristate result_for_constraint
-	    = eval_constraint_op_for_op (c->m_op, op);
-	  if (result_for_constraint.is_known ())
-	    return result_for_constraint;
-	}
-      /* Swapped operands.  */
-      if (c->m_lhs == rhs_ec
-	  && c->m_rhs == lhs_ec)
-	{
-	  tristate result_for_constraint
-	    = eval_constraint_op_for_op (c->m_op, swapped_op);
-	  if (result_for_constraint.is_known ())
-	    return result_for_constraint;
-	}
-    }
-
-  return tristate (tristate::TS_UNKNOWN);
-}
-
-range2
-constraint_manager2::get_ec_bounds (equiv_class2_id ec_id) const
-{
-  range2 result;
-
-  int i;
-  constraint2 *c;
-  FOR_EACH_VEC_ELT (m_constraints, i, c)
-    {
-      if (c->m_lhs == ec_id)
-	{
-	  if (tree other_cst = c->m_rhs.get_obj (*this).get_any_constant ())
-	    switch (c->m_op)
-	      {
-	      default:
-		gcc_unreachable ();
-	      case CONSTRAINT2_NE:
-		continue;
-
-	      case CONSTRAINT2_LT:
-		/* We have "EC_ID < OTHER_CST".  */
-		result.m_upper_bound = bound2 (other_cst, false);
-		break;
-
-	      case CONSTRAINT2_LE:
-		/* We have "EC_ID <= OTHER_CST".  */
-		result.m_upper_bound = bound2 (other_cst, true);
-		break;
-	      }
-	}
-      if (c->m_rhs == ec_id)
-	{
-	  if (tree other_cst = c->m_lhs.get_obj (*this).get_any_constant ())
-	    switch (c->m_op)
-	      {
-	      default:
-		gcc_unreachable ();
-	      case CONSTRAINT2_NE:
-		continue;
-
-	      case CONSTRAINT2_LT:
-		/* We have "OTHER_CST < EC_ID"
-		   i.e. "EC_ID > OTHER_CST".  */
-		result.m_lower_bound = bound2 (other_cst, false);
-		break;
-
-	      case CONSTRAINT2_LE:
-		/* We have "OTHER_CST <= EC_ID"
-		   i.e. "EC_ID >= OTHER_CST".  */
-		result.m_lower_bound = bound2 (other_cst, true);
-		break;
-	      }
-	}
-    }
-
-  return result;
-}
-
-
-/* Evaluate the condition LHS_EC OP RHS_CONST, avoiding the creation
-   of equiv_class2 instances.  */
-
-tristate
-constraint_manager2::eval_condition (equiv_class2_id lhs_ec,
-				     enum tree_code op,
-				     tree rhs_const) const
-{
-  gcc_assert (!lhs_ec.null_p ());
-  gcc_assert (CONSTANT_CLASS_P (rhs_const));
-
-  if (tree lhs_const = lhs_ec.get_obj (*this).get_any_constant ())
-    return compare_constants (lhs_const, op, rhs_const);
-
-  /* Check for known inequalities of the form
-       (LHS_EC != OTHER_CST) or (OTHER_CST != LHS_EC).
-     If RHS_CONST == OTHER_CST, then we also know that LHS_EC != OTHER_CST.
-     For example, we might have the constraint
-       ptr != (void *)0
-     so we want the condition
-       ptr == (foo *)0
-     to be false.  */
-  int i;
-  constraint2 *c;
-  FOR_EACH_VEC_ELT (m_constraints, i, c)
-    {
-      if (c->m_op == CONSTRAINT2_NE)
-	{
-	  if (c->m_lhs == lhs_ec)
-	    {
-	      if (tree other_cst = c->m_rhs.get_obj (*this).get_any_constant ())
-		if (compare_constants
-		      (rhs_const, EQ_EXPR, other_cst).is_true ())
-		  {
-		    switch (op)
-		      {
-		      case EQ_EXPR:
-			return tristate (tristate::TS_FALSE);
-		      case NE_EXPR:
-			return tristate (tristate::TS_TRUE);
-		      default:
-			break;
-		      }
-		  }
-	    }
-	  if (c->m_rhs == lhs_ec)
-	    {
-	      if (tree other_cst = c->m_lhs.get_obj (*this).get_any_constant ())
-		if (compare_constants
-		      (rhs_const, EQ_EXPR, other_cst).is_true ())
-		  {
-		    switch (op)
-		      {
-		      case EQ_EXPR:
-			return tristate (tristate::TS_FALSE);
-		      case NE_EXPR:
-			return tristate (tristate::TS_TRUE);
-		      default:
-			break;
-		      }
-		  }
-	    }
-	}
-    }
-  /* Look at existing bounds on LHS_EC.  */
-  range2 lhs_bounds = get_ec_bounds (lhs_ec);
-  return lhs_bounds.eval_condition (op, rhs_const);
-}
-
-// FIXME:
-/* Evaluate the condition LHS OP RHS, avoiding the creation
-   of equiv_class2 instances.  */
-
-tristate
-constraint_manager2::eval_condition (const svalue2 *lhs,
-				     enum tree_code op,
-				     const svalue2 *rhs) const
-{
-  lhs = lhs->unwrap_any_unmergeable ();
-  rhs = rhs->unwrap_any_unmergeable ();
-
-  /* Nothing can be known about unknown or poisoned values.  */
-  if (lhs->get_kind () == svalue2::SK_UNKNOWN
-      || lhs->get_kind () == svalue2::SK_POISONED
-      || rhs->get_kind () == svalue2::SK_UNKNOWN
-      || rhs->get_kind () == svalue2::SK_POISONED)
-    return tristate (tristate::TS_UNKNOWN);
-
-  if (lhs == rhs
-      && !(FLOAT_TYPE_P (lhs->get_type ())
-	   || FLOAT_TYPE_P (rhs->get_type ())))
-    {
-      switch (op)
-	{
-	case EQ_EXPR:
-	case GE_EXPR:
-	case LE_EXPR:
-	  return tristate (tristate::TS_TRUE);
-
-	case NE_EXPR:
-	case GT_EXPR:
-	case LT_EXPR:
-	  return tristate (tristate::TS_FALSE);
-	default:
-	  break;
-	}
-    }
-
-  equiv_class2_id lhs_ec (-1);
-  equiv_class2_id rhs_ec (-1);
-  get_equiv_class2_by_svalue2 (lhs, &lhs_ec);
-  get_equiv_class2_by_svalue2 (rhs, &rhs_ec);
-  if (!lhs_ec.null_p () && !rhs_ec.null_p ())
-    {
-      tristate result_for_ecs
-	= eval_condition (lhs_ec, op, rhs_ec);
-      if (result_for_ecs.is_known ())
-	return result_for_ecs;
-    }
-
-  /* If at least one is not in an EC, we have no constraints
-     comparing LHS and RHS yet.
-     They might still be comparable if one (or both) is a constant.
-
-     Alternatively, we can also get here if we had ECs but they weren't
-     comparable.  Again, constant comparisons might give an answer.  */
-  tree lhs_const = lhs->maybe_get_constant ();
-  tree rhs_const = rhs->maybe_get_constant ();
-  // TODO: enable this:
-#if 0
-  if (lhs_const && rhs_const)
-    {
-      tristate result_for_constants
-	= compare_constants (lhs_const, op, rhs_const);
-      if (result_for_constants.is_known ())
-	return result_for_constants;
-    }
-#endif
-
-  if (!lhs_ec.null_p ())
-    {
-      if (rhs_const)
-	return eval_condition (lhs_ec, op, rhs_const);
-    }
-  if (!rhs_ec.null_p ())
-    {
-      if (lhs_const)
-	{
-	  enum tree_code swapped_op = swap_tree_comparison (op);
-	  return eval_condition (rhs_ec, swapped_op, lhs_const);
-	}
-    }
-
-  return tristate (tristate::TS_UNKNOWN);
-}
-
-/* Delete any information about svalue2_id instances identified by P.
-   Such instances are removed from equivalence classes, and any
-   redundant ECs and constraints are also removed.
-   Accumulate stats into STATS.  */
-
-template <typename PurgeCriteria>
-void
-constraint_manager2::purge (const PurgeCriteria &p, purge_stats2 *stats)
-{
-  /* Delete any svalue2_ids identified by P within the various equivalence
-     classes.  */
-  for (unsigned ec_idx = 0; ec_idx < m_equiv_classes.length (); )
-    {
-      equiv_class2 *ec = m_equiv_classes[ec_idx];
-
-      int i;
-      const svalue2 *sval;
-      bool delete_ec = false;
-      FOR_EACH_VEC_ELT (ec->m_vars, i, sval)
-	{
-	  if (sval == ec->m_cst_sval)
-	    continue;
-	  if (p.should_purge_p (sval))
-	    {
-	      if (ec->del (sval))
-		if (!ec->m_constant)
-		  delete_ec = true;
-	    }
-	}
-
-      if (delete_ec)
-	{
-	  delete ec;
-	  m_equiv_classes.ordered_remove (ec_idx);
-	  if (stats)
-	    stats->m_num_equiv_classes++;
-
-	  /* Update the constraints, potentially removing some.  */
-	  for (unsigned con_idx = 0; con_idx < m_constraints.length (); )
-	    {
-	      constraint2 *c = &m_constraints[con_idx];
-
-	      /* Remove constraints that refer to the deleted EC.  */
-	      if (c->m_lhs == ec_idx
-		  || c->m_rhs == ec_idx)
-		{
-		  m_constraints.ordered_remove (con_idx);
-		  if (stats)
-		    stats->m_num_constraints++;
-		}
-	      else
-		{
-		  /* Renumber constraints that refer to ECs that have
-		     had their idx changed.  */
-		  c->m_lhs.update_for_removal (ec_idx);
-		  c->m_rhs.update_for_removal (ec_idx);
-
-		  con_idx++;
-		}
-	    }
-	}
-      else
-	ec_idx++;
-    }
-
-  /* Now delete any constraints that are purely between constants.  */
-  for (unsigned con_idx = 0; con_idx < m_constraints.length (); )
-    {
-      constraint2 *c = &m_constraints[con_idx];
-      if (m_equiv_classes[c->m_lhs.m_idx]->m_vars.length () == 0
-	  && m_equiv_classes[c->m_rhs.m_idx]->m_vars.length () == 0)
-	{
-	  m_constraints.ordered_remove (con_idx);
-	  if (stats)
-	    stats->m_num_constraints++;
-	}
-      else
-	{
-	  con_idx++;
-	}
-    }
-
-  /* Finally, delete any ECs that purely contain constants and aren't
-     referenced by any constraints.  */
-  for (unsigned ec_idx = 0; ec_idx < m_equiv_classes.length (); )
-    {
-      equiv_class2 *ec = m_equiv_classes[ec_idx];
-      if (ec->m_vars.length () == 0)
-	{
-	  equiv_class2_id ec_id (ec_idx);
-	  bool has_constraint = false;
-	  for (unsigned con_idx = 0; con_idx < m_constraints.length ();
-	       con_idx++)
-	    {
-	      constraint2 *c = &m_constraints[con_idx];
-	      if (c->m_lhs == ec_id
-		  || c->m_rhs == ec_id)
-		{
-		  has_constraint = true;
-		  break;
-		}
-	    }
-	  if (!has_constraint)
-	    {
-	      delete ec;
-	      m_equiv_classes.ordered_remove (ec_idx);
-	      if (stats)
-		stats->m_num_equiv_classes++;
-
-	      /* Renumber constraints that refer to ECs that have
-		 had their idx changed.  */
-	      for (unsigned con_idx = 0; con_idx < m_constraints.length ();
-		   con_idx++)
-		{
-		  constraint2 *c = &m_constraints[con_idx];
-		  c->m_lhs.update_for_removal (ec_idx);
-		  c->m_rhs.update_for_removal (ec_idx);
-		}
-	      continue;
-	    }
-	}
-      ec_idx++;
-    }
-
-  validate ();
-}
-
-/* Implementation of PurgeCriteria.  */
-
-class dead_svalue_purger
-{
-public:
-  dead_svalue_purger (const hash_set<const svalue2 *> &live_svalues,
-		      const region_model2 *model)
-  : m_live_svalues (live_svalues), m_model (model)
-  {
-  }
-
-  bool should_purge_p (const svalue2 *sval) const
-  {
-    return !sval->live_p (m_live_svalues, m_model);
-  }
-
-private:
-  const hash_set<const svalue2 *> &m_live_svalues;
-  const region_model2 *m_model;
-};
-
-/* Purge dead svalues from equivalence classes and update constraints
-   accordingly.  */
-
-void
-constraint_manager2::
-on_liveness_change (const hash_set<const svalue2 *> &live_svalues,
-		    const region_model2 *model)
-{
-  dead_svalue_purger p (live_svalues, model);
-  purge (p, NULL);
-}
-
-/* Comparator for use by constraint_manager2::canonicalize.
-   Sort a pair of equiv_class2 instances, using the representative
-   svalue2_id as a sort key.  */
-
-static int
-equiv_class2_cmp (const void *p1, const void *p2)
-{
-  const equiv_class2 *ec1 = *(const equiv_class2 * const *)p1;
-  const equiv_class2 *ec2 = *(const equiv_class2 * const *)p2;
-
-  const svalue2 *rep1 = ec1->get_representative ();
-  const svalue2 *rep2 = ec2->get_representative ();
-
-  gcc_assert (rep1);
-  gcc_assert (rep2);
-
-  if (rep1 < rep2)
-    return 1;
-  if (rep1 > rep2)
-    return -1;
-  return 0;
-}
-
-/* Comparator for use by constraint_manager2::canonicalize.
-   Sort a pair of constraint instances.  */
-
-static int
-constraint_cmp (const void *p1, const void *p2)
-{
-  const constraint2 *c1 = (const constraint2 *)p1;
-  const constraint2 *c2 = (const constraint2 *)p2;
-  int lhs_cmp = c1->m_lhs.as_int () - c2->m_lhs.as_int ();
-  if (lhs_cmp)
-    return lhs_cmp;
-  int rhs_cmp = c1->m_rhs.as_int () - c2->m_rhs.as_int ();
-  if (rhs_cmp)
-    return rhs_cmp;
-  return c1->m_op - c2->m_op;
-}
-
-/* Purge redundant equivalence classes and constraints, and reorder them
-   within this constraint_manager2 into a canonical order, to increase the
-   chances of finding equality with another instance.  */
-
-void
-constraint_manager2::canonicalize ()
-{
-  /* First, sort svalue2s within the ECs.  */
-  unsigned i;
-  equiv_class2 *ec;
-  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
-    ec->canonicalize ();
-
-  /* TODO: remove constraints where both sides have a constant, and are
-     thus implicit.  But does this break transitivity?  */
-
-  /* We will be purging and reordering ECs.
-     We will need to remap the equiv_class2_ids in the constraints,
-     so we need to store the original index of each EC.
-     Build a lookup table, mapping from the representative svalue2
-     to the original equiv_class2_id of that svalue2.  */
-  hash_map<const svalue2 *, equiv_class2_id> original_ec_id;
-  const unsigned orig_num_equiv_classes = m_equiv_classes.length ();
-  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
-    {
-      const svalue2 *rep = ec->get_representative ();
-      gcc_assert (rep);
-      original_ec_id.put (rep, i);
-    }
-
-  /* Find ECs used by constraints.  */
-  hash_set<const equiv_class2 *> used_ecs;
-  constraint2 *c;
-  FOR_EACH_VEC_ELT (m_constraints, i, c)
-    {
-      used_ecs.add (m_equiv_classes[c->m_lhs.as_int ()]);
-      used_ecs.add (m_equiv_classes[c->m_rhs.as_int ()]);
-    }
-
-  /* Purge unused ECs: those that aren't used by constraints and
-     that effectively have only one svalue2 (either in m_constant
-     or in m_vars).  */
-  {
-    /* "unordered remove if" from a vec.  */
-    unsigned i = 0;
-    while (i < m_equiv_classes.length ())
-      {
-	equiv_class2 *ec = m_equiv_classes[i];
-	if (!used_ecs.contains (ec)
-	    && ((ec->m_vars.length () < 2 && ec->m_constant == NULL_TREE)
-		|| (ec->m_vars.length () == 0)))
-	  {
-	    m_equiv_classes.unordered_remove (i);
-	    delete ec;
-	  }
-	else
-	  i++;
-      }
-  }
-
-  /* Next, sort the surviving ECs into a canonical order.  */
-  m_equiv_classes.qsort (equiv_class2_cmp);
-
-  /* Populate ec_id_map based on the old vs new EC ids.  */
-  one_way_id_map<equiv_class2_id> ec_id_map (orig_num_equiv_classes);
-  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
-    {
-      const svalue2 *rep = ec->get_representative ();
-      gcc_assert (rep);
-      ec_id_map.put (*original_ec_id.get (rep), i);
-    }
-
-  /* Use ec_id_map to update the EC ids within the constraints.  */
-  FOR_EACH_VEC_ELT (m_constraints, i, c)
-    {
-      ec_id_map.update (&c->m_lhs);
-      ec_id_map.update (&c->m_rhs);
-    }
-
-  /* Finally, sort the constraints. */
-  m_constraints.qsort (constraint_cmp);
-}
-
-/* A concrete subclass of constraint_manager2 for use when
-   merging two constraint_manager2 into a third constraint_manager2,
-   each of which has its own region_model2.
-   Calls are delegated to the constraint_manager2 for the merged model,
-   and thus affect its region_model2.  */
-#if 0
-class cleaned_constraint_manager2 : public constraint_manager2
-{
-public:
-  cleaned_constraint_manager2 (constraint_manager2 *merged)
-  : constraint_manager2 (merged->m_mgr),
-    m_merged (merged) {}
-
-#if 0
-  constraint_manager2 *clone (region_model2 *) const FINAL OVERRIDE
-  {
-    gcc_unreachable ();
-  }
-#endif
-private:
-  constraint_manager2 *m_merged;
-};
-#endif
-/* Concrete subclass of fact_visitor for use by constraint_manager2::merge.
-   For every fact in CM_A, see if it is also true in *CM_B.  Add such
-   facts to *OUT.  */
-
-class merger2_fact_visitor : public fact2_visitor
-{
-public:
-  merger2_fact_visitor (constraint_manager2 *cm_b,
-			constraint_manager2 *out,
-			const model2_merger &merger)
-  : m_cm_b (cm_b), m_out (out), m_merger (merger)
-  {}
-
-  void on_fact (const svalue2 *lhs, enum tree_code code, const svalue2 *rhs)
-    FINAL OVERRIDE
-  {
-    /* If we merged (M BINOP X) with M
-       and A has fact (M RELOP Y),
-       then we should instead have fact (M RELOP (Y BINOP X)).
-       For example, if we merged (M + 1) with M
-       and we have fact M <= 255 then we should instead have M <= 256.  */
-    // TODO: we need to fix eval_condition for this to work.
-#if ITERATED_BINOPS
-    unsigned i;
-    const binop_svalue2 *binop;
-    FOR_EACH_VEC_ELT (m_merger.m_iterated_binops, i, binop)
-      {
-	if (binop->get_arg0 () == lhs)
-	  {
-	    region_model2_manager *mgr = m_merger.get_manager ();
-	    rhs = mgr->get_or_create_binop (rhs->get_type (),
-					    binop->get_op (),
-					    rhs, binop->get_arg1 ());
-	  }
-      }
-#endif
-
-    /* Special-case for widening.  */
-    /* FIXME: should only do this if LHS isn't mentioned anywhere in
-       m_cm_b's model, rather than just in m_cb_b?  */
-    if (lhs->get_kind () == svalue2::SK_WIDENING)
-      if (!m_cm_b->get_equiv_class2_by_svalue2 (lhs, NULL))
-	{
-	  /* LHS isn't constrained within m_cm_b.  */
-	  /* FIXME: for now, treat it as "unmentioned".  */
-	  bool sat = m_out->add_constraint (lhs, code, rhs);
-	  gcc_assert (sat);
-	  return;
-	}
-
-    if (m_cm_b->eval_condition (lhs, code, rhs).is_true ())
-      {
-	bool sat = m_out->add_constraint (lhs, code, rhs);
-	gcc_assert (sat);
-      }
-  }
-
-private:
-  constraint_manager2 *m_cm_b;
-  constraint_manager2 *m_out;
-  const model2_merger &m_merger;
-};
-
-// 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.  */
-
-void
-constraint_manager2::merge (const constraint_manager2 &cm_a,
-			    const constraint_manager2 &cm_b,
-			    constraint_manager2 *out,
-			    const model2_merger &merger)
-{
-  /* FIXME: work with a copy, since eval_condition can mutate a cm.  */
-  constraint_manager2 copy_cm_a (cm_a);
-  constraint_manager2 copy_cm_b (cm_b);
-
-  /* Merge the equivalence classes and constraints.
-     The easiest way to do this seems to be to enumerate all of the facts
-     in cm_a, see which are also true in cm_b,
-     and add those to *OUT.  */
-  merger2_fact_visitor v (&copy_cm_b, out, merger);
-  copy_cm_a.for_each_fact (&v);
-
-  /* FIXME and the other way around, since things could be implicit in one
-     and explicit in the other?  */
-}
-
-/* A subroutine of constraint_manager2::merge.
-   Use MAP_SVAL_TO_M to map equivalence classes and constraints from
-   SM_IN to *OUT.  Purge any non-constant svalue2 *that don't appear
-   in the result of MAP_SVAL_TO_M, purging any ECs and their constraints
-   that become empty as a result.  Potentially create svalue2s in
-   the merged region_model2 for constants that weren't already in use there.  */
-#if 0
-void
-constraint_manager2::
-clean_merger_input (const constraint_manager2 &cm_in,
-		    const one_way_svalue2_id_map &map_sval_to_m,
-		    constraint_manager2 *out)
-{
-  one_way_id_map<equiv_class2_id> map_ec_to_m
-    (cm_in.m_equiv_classes.length ());
-  unsigned ec_idx;
-  equiv_class2 *ec;
-  FOR_EACH_VEC_ELT (cm_in.m_equiv_classes, ec_idx, ec)
-    {
-      equiv_class2 cleaned_ec;
-      if (tree cst = ec->get_any_constant ())
-	{
-	  cleaned_ec.m_constant = cst;
-	  /* Lazily create the constant in the out region_model2.  */
-	  cleaned_ec.m_cst_sval = out->get_sval_for_constant (cst);
-	}
-      unsigned var_idx;
-      const svalue2 **var_in_sval;
-      FOR_EACH_VEC_ELT (ec->m_vars, var_idx, var_in_sval)
-	{
-	  const svalue2 *var_m_sval
-	    = map_sval_to_m.get_dst_for_src (*var_in_sval);
-	  if (!var_m_sval.null_p ())
-	    cleaned_ec.m_vars.safe_push (var_m_sval);
-	}
-      if (cleaned_ec.get_any_constant () || !cleaned_ec.m_vars.is_empty ())
-	{
-	  map_ec_to_m.put (ec_idx, out->m_equiv_classes.length ());
-	  out->m_equiv_classes.safe_push (new equiv_class2 (cleaned_ec));
-	}
-    }
-
-  /* Write out to *OUT any constraints for which both svales survived
-     cleaning, using the new EC IDs.  */
-  unsigned con_idx;
-  constraint2 *c;
-  FOR_EACH_VEC_ELT (cm_in.m_constraints, con_idx, c)
-    {
-      equiv_class2_id new_lhs = map_ec_to_m.get_dst_for_src (c->m_lhs);
-      if (new_lhs.null_p ())
-	continue;
-      equiv_class2_id new_rhs = map_ec_to_m.get_dst_for_src (c->m_rhs);
-      if (new_rhs.null_p ())
-	continue;
-      out->m_constraints.safe_push (constraint (new_lhs,
-						c->m_op,
-						new_rhs));
-    }
-}
-#endif
-/* Call VISITOR's on_fact vfunc repeatedly to express the various
-   equivalence classes and constraints.
-   This is used by constraint_manager2::merge to find the common
-   facts between two input constraint_manager2s.  */
-
-void
-constraint_manager2::for_each_fact (fact2_visitor *visitor) const
-{
-  /* First, call EQ_EXPR within the various equivalence classes.  */
-  unsigned ec_idx;
-  equiv_class2 *ec;
-  FOR_EACH_VEC_ELT (m_equiv_classes, ec_idx, ec)
-    {
-      if (ec->m_cst_sval)
-	{
-	  unsigned i;
-	  const svalue2 *sval;
-	  FOR_EACH_VEC_ELT (ec->m_vars, i, sval)
-	    visitor->on_fact (ec->m_cst_sval, EQ_EXPR, sval);
-	}
-      for (unsigned i = 0; i < ec->m_vars.length (); i++)
-	for (unsigned j = i + 1; j < ec->m_vars.length (); j++)
-	  visitor->on_fact (ec->m_vars[i], EQ_EXPR, ec->m_vars[j]);
-    }
-
-  /* Now, express the various constraints.  */
-  unsigned con_idx;
-  constraint2 *c;
-  FOR_EACH_VEC_ELT (m_constraints, con_idx, c)
-    {
-      const equiv_class2 &ec_lhs = c->m_lhs.get_obj (*this);
-      const equiv_class2 &ec_rhs = c->m_rhs.get_obj (*this);
-      enum tree_code code = constraint2_tree_code (c->m_op);
-
-      if (ec_lhs.m_cst_sval)
-	{
-	  for (unsigned j = 0; j < ec_rhs.m_vars.length (); j++)
-	    {
-	      visitor->on_fact (ec_lhs.m_cst_sval, code, ec_rhs.m_vars[j]);
-	    }
-	}
-      for (unsigned i = 0; i < ec_lhs.m_vars.length (); i++)
-	{
-	  if (ec_rhs.m_cst_sval)
-	    visitor->on_fact (ec_lhs.m_vars[i], code, ec_rhs.m_cst_sval);
-	  for (unsigned j = 0; j < ec_rhs.m_vars.length (); j++)
-	    visitor->on_fact (ec_lhs.m_vars[i], code, ec_rhs.m_vars[j]);
-	}
-    }
-}
-
-/* Assert that this object is valid.  */
-
-void
-constraint_manager2::validate () const
-{
-  /* Skip this in a release build.  */
-#if !CHECKING_P
-  return;
-#endif
-
-  int i;
-  equiv_class2 *ec;
-  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
-    {
-      gcc_assert (ec);
-
-      int j;
-      const svalue2 *sval;
-      FOR_EACH_VEC_ELT (ec->m_vars, j, sval)
-	gcc_assert (sval);
-      if (ec->m_constant)
-	{
-	  gcc_assert (CONSTANT_CLASS_P (ec->m_constant));
-	  gcc_assert (ec->m_cst_sval);
-	}
-#if 0
-      else
-	gcc_assert (ec->m_vars.length () > 0);
-#endif
-    }
-
-  constraint2 *c;
-  FOR_EACH_VEC_ELT (m_constraints, i, c)
-    {
-      gcc_assert (!c->m_lhs.null_p ());
-      gcc_assert (c->m_lhs.as_int () <= (int)m_equiv_classes.length ());
-      gcc_assert (!c->m_rhs.null_p ());
-      gcc_assert (c->m_rhs.as_int () <= (int)m_equiv_classes.length ());
-    }
-}
-
-#if CHECKING_P
-
-namespace selftest {
-
-/* Various constraint_manager2 selftests.
-   These have to be written in terms of a region_model2, since
-   the latter is responsible for managing svalue2 and svalue2_id
-   instances.  */
-
-/* Verify that setting and getting simple conditions within a region_model2
-   work (thus exercising the underlying constraint_manager2).  */
-
-static void
-test_constraint_conditions ()
-{
-  tree int_42 = build_int_cst (integer_type_node, 42);
-  tree int_0 = build_int_cst (integer_type_node, 0);
-
-  tree x = build_global_decl ("x", integer_type_node);
-  tree y = build_global_decl ("y", integer_type_node);
-  tree z = build_global_decl ("z", integer_type_node);
-
-  /* Self-comparisons.  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ASSERT_CONDITION_TRUE2 (model, x, EQ_EXPR, x);
-    ASSERT_CONDITION_TRUE2 (model, x, LE_EXPR, x);
-    ASSERT_CONDITION_TRUE2 (model, x, GE_EXPR, x);
-    ASSERT_CONDITION_FALSE2 (model, x, NE_EXPR, x);
-    ASSERT_CONDITION_FALSE2 (model, x, LT_EXPR, x);
-    ASSERT_CONDITION_FALSE2 (model, x, GT_EXPR, x);
-  }
-
-  /* Adding self-equality shouldn't add equiv classes.  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, x);
-    ADD_SAT_CONSTRAINT (model, int_42, EQ_EXPR, int_42);
-    /* ...even when done directly via svalues: */
-    const svalue2 *sval_int_42 = model.get_rvalue (int_42, NULL);
-    bool sat = model.get_constraints2 ()->add_constraint (sval_int_42,
-							  EQ_EXPR,
-							  sval_int_42);
-    ASSERT_TRUE (sat);
-    ASSERT_EQ (model.get_constraints2 ()->m_equiv_classes.length (), 0);
-  }
-
-  /* x == y.  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ASSERT_CONDITION_UNKNOWN2 (model, x, EQ_EXPR, y);
-
-    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, y);
-
-    ASSERT_CONDITION_TRUE2 (model, x, EQ_EXPR, y);
-    ASSERT_CONDITION_TRUE2 (model, x, LE_EXPR, y);
-    ASSERT_CONDITION_TRUE2 (model, x, GE_EXPR, y);
-    ASSERT_CONDITION_FALSE2 (model, x, NE_EXPR, y);
-    ASSERT_CONDITION_FALSE2 (model, x, LT_EXPR, y);
-    ASSERT_CONDITION_FALSE2 (model, x, GT_EXPR, y);
-
-    /* Swapped operands.  */
-    ASSERT_CONDITION_TRUE2 (model, y, EQ_EXPR, x);
-    ASSERT_CONDITION_TRUE2 (model, y, LE_EXPR, x);
-    ASSERT_CONDITION_TRUE2 (model, y, GE_EXPR, x);
-    ASSERT_CONDITION_FALSE2 (model, y, NE_EXPR, x);
-    ASSERT_CONDITION_FALSE2 (model, y, LT_EXPR, x);
-    ASSERT_CONDITION_FALSE2 (model, y, GT_EXPR, x);
-
-    /* Comparison with other var.  */
-    ASSERT_CONDITION_UNKNOWN2 (model, x, EQ_EXPR, z);
-    ASSERT_CONDITION_UNKNOWN2 (model, x, LE_EXPR, z);
-    ASSERT_CONDITION_UNKNOWN2 (model, x, GE_EXPR, z);
-    ASSERT_CONDITION_UNKNOWN2 (model, x, NE_EXPR, z);
-    ASSERT_CONDITION_UNKNOWN2 (model, x, LT_EXPR, z);
-    ASSERT_CONDITION_UNKNOWN2 (model, x, GT_EXPR, z);
-  }
-
-  /* x == y, then y == z  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ASSERT_CONDITION_UNKNOWN2 (model, x, EQ_EXPR, y);
-
-    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, y);
-    ADD_SAT_CONSTRAINT (model, y, EQ_EXPR, z);
-
-    ASSERT_CONDITION_TRUE2 (model, x, EQ_EXPR, z);
-    ASSERT_CONDITION_TRUE2 (model, x, LE_EXPR, z);
-    ASSERT_CONDITION_TRUE2 (model, x, GE_EXPR, z);
-    ASSERT_CONDITION_FALSE2 (model, x, NE_EXPR, z);
-    ASSERT_CONDITION_FALSE2 (model, x, LT_EXPR, z);
-    ASSERT_CONDITION_FALSE2 (model, x, GT_EXPR, z);
-  }
-
-  /* x != y.  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-
-    ADD_SAT_CONSTRAINT (model, x, NE_EXPR, y);
-
-    ASSERT_CONDITION_TRUE2 (model, x, NE_EXPR, y);
-    ASSERT_CONDITION_FALSE2 (model, x, EQ_EXPR, y);
-    ASSERT_CONDITION_UNKNOWN2 (model, x, LE_EXPR, y);
-    ASSERT_CONDITION_UNKNOWN2 (model, x, GE_EXPR, y);
-    ASSERT_CONDITION_UNKNOWN2 (model, x, LT_EXPR, y);
-    ASSERT_CONDITION_UNKNOWN2 (model, x, GT_EXPR, y);
-
-    /* Swapped operands.  */
-    ASSERT_CONDITION_TRUE2 (model, y, NE_EXPR, x);
-    ASSERT_CONDITION_FALSE2 (model, y, EQ_EXPR, x);
-    ASSERT_CONDITION_UNKNOWN2 (model, y, LE_EXPR, x);
-    ASSERT_CONDITION_UNKNOWN2 (model, y, GE_EXPR, x);
-    ASSERT_CONDITION_UNKNOWN2 (model, y, LT_EXPR, x);
-    ASSERT_CONDITION_UNKNOWN2 (model, y, GT_EXPR, x);
-
-    /* Comparison with other var.  */
-    ASSERT_CONDITION_UNKNOWN2 (model, x, EQ_EXPR, z);
-    ASSERT_CONDITION_UNKNOWN2 (model, x, LE_EXPR, z);
-    ASSERT_CONDITION_UNKNOWN2 (model, x, GE_EXPR, z);
-    ASSERT_CONDITION_UNKNOWN2 (model, x, NE_EXPR, z);
-    ASSERT_CONDITION_UNKNOWN2 (model, x, LT_EXPR, z);
-    ASSERT_CONDITION_UNKNOWN2 (model, x, GT_EXPR, z);
-  }
-
-  /* x < y.  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-
-    ADD_SAT_CONSTRAINT (model, x, LT_EXPR, y);
-
-    ASSERT_CONDITION_TRUE2 (model, x, LT_EXPR, y);
-    ASSERT_CONDITION_TRUE2 (model, x, LE_EXPR, y);
-    ASSERT_CONDITION_TRUE2 (model, x, NE_EXPR, y);
-    ASSERT_CONDITION_FALSE2 (model, x, EQ_EXPR, y);
-    ASSERT_CONDITION_FALSE2 (model, x, GT_EXPR, y);
-    ASSERT_CONDITION_FALSE2 (model, x, GE_EXPR, y);
-
-    /* Swapped operands.  */
-    ASSERT_CONDITION_FALSE2 (model, y, LT_EXPR, x);
-    ASSERT_CONDITION_FALSE2 (model, y, LE_EXPR, x);
-    ASSERT_CONDITION_TRUE2 (model, y, NE_EXPR, x);
-    ASSERT_CONDITION_FALSE2 (model, y, EQ_EXPR, x);
-    ASSERT_CONDITION_TRUE2 (model, y, GT_EXPR, x);
-    ASSERT_CONDITION_TRUE2 (model, y, GE_EXPR, x);
-  }
-
-  /* x <= y.  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-
-    ADD_SAT_CONSTRAINT (model, x, LE_EXPR, y);
-
-    ASSERT_CONDITION_UNKNOWN2 (model, x, LT_EXPR, y);
-    ASSERT_CONDITION_TRUE2 (model, x, LE_EXPR, y);
-    ASSERT_CONDITION_UNKNOWN2 (model, x, NE_EXPR, y);
-    ASSERT_CONDITION_UNKNOWN2 (model, x, EQ_EXPR, y);
-    ASSERT_CONDITION_FALSE2 (model, x, GT_EXPR, y);
-    ASSERT_CONDITION_UNKNOWN2 (model, x, GE_EXPR, y);
-
-    /* Swapped operands.  */
-    ASSERT_CONDITION_FALSE2 (model, y, LT_EXPR, x);
-    ASSERT_CONDITION_UNKNOWN2 (model, y, LE_EXPR, x);
-    ASSERT_CONDITION_UNKNOWN2 (model, y, NE_EXPR, x);
-    ASSERT_CONDITION_UNKNOWN2 (model, y, EQ_EXPR, x);
-    ASSERT_CONDITION_UNKNOWN2 (model, y, GT_EXPR, x);
-    ASSERT_CONDITION_TRUE2 (model, y, GE_EXPR, x);
-  }
-
-  /* x > y.  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-
-    ADD_SAT_CONSTRAINT (model, x, GT_EXPR, y);
-
-    ASSERT_CONDITION_TRUE2 (model, x, GT_EXPR, y);
-    ASSERT_CONDITION_TRUE2 (model, x, GE_EXPR, y);
-    ASSERT_CONDITION_TRUE2 (model, x, NE_EXPR, y);
-    ASSERT_CONDITION_FALSE2 (model, x, EQ_EXPR, y);
-    ASSERT_CONDITION_FALSE2 (model, x, LT_EXPR, y);
-    ASSERT_CONDITION_FALSE2 (model, x, LE_EXPR, y);
-
-    /* Swapped operands.  */
-    ASSERT_CONDITION_FALSE2 (model, y, GT_EXPR, x);
-    ASSERT_CONDITION_FALSE2 (model, y, GE_EXPR, x);
-    ASSERT_CONDITION_TRUE2 (model, y, NE_EXPR, x);
-    ASSERT_CONDITION_FALSE2 (model, y, EQ_EXPR, x);
-    ASSERT_CONDITION_TRUE2 (model, y, LT_EXPR, x);
-    ASSERT_CONDITION_TRUE2 (model, y, LE_EXPR, x);
-  }
-
-  /* x >= y.  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-
-    ADD_SAT_CONSTRAINT (model, x, GE_EXPR, y);
-
-    ASSERT_CONDITION_UNKNOWN2 (model, x, GT_EXPR, y);
-    ASSERT_CONDITION_TRUE2 (model, x, GE_EXPR, y);
-    ASSERT_CONDITION_UNKNOWN2 (model, x, NE_EXPR, y);
-    ASSERT_CONDITION_UNKNOWN2 (model, x, EQ_EXPR, y);
-    ASSERT_CONDITION_FALSE2 (model, x, LT_EXPR, y);
-    ASSERT_CONDITION_UNKNOWN2 (model, x, LE_EXPR, y);
-
-    /* Swapped operands.  */
-    ASSERT_CONDITION_FALSE2 (model, y, GT_EXPR, x);
-    ASSERT_CONDITION_UNKNOWN2 (model, y, GE_EXPR, x);
-    ASSERT_CONDITION_UNKNOWN2 (model, y, NE_EXPR, x);
-    ASSERT_CONDITION_UNKNOWN2 (model, y, EQ_EXPR, x);
-    ASSERT_CONDITION_UNKNOWN2 (model, y, LT_EXPR, x);
-    ASSERT_CONDITION_TRUE2 (model, y, LE_EXPR, x);
-  }
-
-  // TODO: implied orderings
-
-  /* Constants.  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ASSERT_CONDITION_FALSE2 (model, int_0, EQ_EXPR, int_42);
-    ASSERT_CONDITION_TRUE2 (model, int_0, NE_EXPR, int_42);
-    ASSERT_CONDITION_TRUE2 (model, int_0, LT_EXPR, int_42);
-    ASSERT_CONDITION_TRUE2 (model, int_0, LE_EXPR, int_42);
-    ASSERT_CONDITION_FALSE2 (model, int_0, GT_EXPR, int_42);
-    ASSERT_CONDITION_FALSE2 (model, int_0, GE_EXPR, int_42);
-  }
-
-  /* x == 0, y == 42.  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, int_0);
-    ADD_SAT_CONSTRAINT (model, y, EQ_EXPR, int_42);
-
-    ASSERT_CONDITION_TRUE2 (model, x, NE_EXPR, y);
-    ASSERT_CONDITION_FALSE2 (model, x, EQ_EXPR, y);
-    ASSERT_CONDITION_TRUE2 (model, x, LE_EXPR, y);
-    ASSERT_CONDITION_FALSE2 (model, x, GE_EXPR, y);
-    ASSERT_CONDITION_TRUE2 (model, x, LT_EXPR, y);
-    ASSERT_CONDITION_FALSE2 (model, x, GT_EXPR, y);
-  }
-
-  /* Unsatisfiable combinations.  */
-
-  /* x == y && x != y.  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, y);
-    ADD_UNSAT_CONSTRAINT (model, x, NE_EXPR, y);
-  }
-
-  /* x == 0 then x == 42.  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, int_0);
-    ADD_UNSAT_CONSTRAINT (model, x, EQ_EXPR, int_42);
-  }
-
-  /* x == 0 then x != 0.  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, int_0);
-    ADD_UNSAT_CONSTRAINT (model, x, NE_EXPR, int_0);
-  }
-
-  /* x == 0 then x > 0.  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, int_0);
-    ADD_UNSAT_CONSTRAINT (model, x, GT_EXPR, int_0);
-  }
-
-  /* x != y && x == y.  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ADD_SAT_CONSTRAINT (model, x, NE_EXPR, y);
-    ADD_UNSAT_CONSTRAINT (model, x, EQ_EXPR, y);
-  }
-
-  /* x <= y && x > y.  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ADD_SAT_CONSTRAINT (model, x, LE_EXPR, y);
-    ADD_UNSAT_CONSTRAINT (model, x, GT_EXPR, y);
-  }
-
-  // etc
-}
-
-/* Test transitivity of conditions.  */
-
-static void
-test_transitivity ()
-{
-  tree a = build_global_decl ("a", integer_type_node);
-  tree b = build_global_decl ("b", integer_type_node);
-  tree c = build_global_decl ("c", integer_type_node);
-  tree d = build_global_decl ("d", integer_type_node);
-
-  /* a == b, then c == d, then c == b.  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ASSERT_CONDITION_UNKNOWN2 (model, a, EQ_EXPR, b);
-    ASSERT_CONDITION_UNKNOWN2 (model, b, EQ_EXPR, c);
-    ASSERT_CONDITION_UNKNOWN2 (model, c, EQ_EXPR, d);
-    ASSERT_CONDITION_UNKNOWN2 (model, a, EQ_EXPR, d);
-
-    ADD_SAT_CONSTRAINT (model, a, EQ_EXPR, b);
-    ASSERT_CONDITION_TRUE2 (model, a, EQ_EXPR, b);
-
-    ADD_SAT_CONSTRAINT (model, c, EQ_EXPR, d);
-    ASSERT_CONDITION_TRUE2 (model, c, EQ_EXPR, d);
-    ASSERT_CONDITION_UNKNOWN2 (model, a, EQ_EXPR, d);
-
-    ADD_SAT_CONSTRAINT (model, c, EQ_EXPR, b);
-    ASSERT_CONDITION_TRUE2 (model, c, EQ_EXPR, b);
-    ASSERT_CONDITION_TRUE2 (model, a, EQ_EXPR, d);
-  }
-
-  /* Transitivity: "a < b", "b < c" should imply "a < c".  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ADD_SAT_CONSTRAINT (model, a, LT_EXPR, b);
-    ADD_SAT_CONSTRAINT (model, b, LT_EXPR, c);
-
-    ASSERT_CONDITION_TRUE2 (model, a, LT_EXPR, c);
-    ASSERT_CONDITION_FALSE2 (model, a, EQ_EXPR, c);
-  }
-
-  /* Transitivity: "a <= b", "b < c" should imply "a < c".  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ADD_SAT_CONSTRAINT (model, a, LE_EXPR, b);
-    ADD_SAT_CONSTRAINT (model, b, LT_EXPR, c);
-
-    ASSERT_CONDITION_TRUE2 (model, a, LT_EXPR, c);
-    ASSERT_CONDITION_FALSE2 (model, a, EQ_EXPR, c);
-  }
-
-  /* Transitivity: "a <= b", "b <= c" should imply "a <= c".  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ADD_SAT_CONSTRAINT (model, a, LE_EXPR, b);
-    ADD_SAT_CONSTRAINT (model, b, LE_EXPR, c);
-
-    ASSERT_CONDITION_TRUE2 (model, a, LE_EXPR, c);
-    ASSERT_CONDITION_UNKNOWN2 (model, a, EQ_EXPR, c);
-  }
-
-  /* Transitivity: "a > b", "b > c" should imply "a > c".  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ADD_SAT_CONSTRAINT (model, a, GT_EXPR, b);
-    ADD_SAT_CONSTRAINT (model, b, GT_EXPR, c);
-
-    ASSERT_CONDITION_TRUE2 (model, a, GT_EXPR, c);
-    ASSERT_CONDITION_FALSE2 (model, a, EQ_EXPR, c);
-  }
-
-  /* Transitivity: "a >= b", "b > c" should imply " a > c".  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ADD_SAT_CONSTRAINT (model, a, GE_EXPR, b);
-    ADD_SAT_CONSTRAINT (model, b, GT_EXPR, c);
-
-    ASSERT_CONDITION_TRUE2 (model, a, GT_EXPR, c);
-    ASSERT_CONDITION_FALSE2 (model, a, EQ_EXPR, c);
-  }
-
-  /* Transitivity: "a >= b", "b >= c" should imply "a >= c".  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ADD_SAT_CONSTRAINT (model, a, GE_EXPR, b);
-    ADD_SAT_CONSTRAINT (model, b, GE_EXPR, c);
-
-    ASSERT_CONDITION_TRUE2 (model, a, GE_EXPR, c);
-    ASSERT_CONDITION_UNKNOWN2 (model, a, EQ_EXPR, c);
-  }
-
-  /* Transitivity: "(a < b)", "(c < d)", "(b < c)" should
-     imply the easy cases:
-       (a < c)
-       (b < d)
-     but also that:
-       (a < d).  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ADD_SAT_CONSTRAINT (model, a, LT_EXPR, b);
-    ADD_SAT_CONSTRAINT (model, c, LT_EXPR, d);
-    ADD_SAT_CONSTRAINT (model, b, LT_EXPR, c);
-
-    ASSERT_CONDITION_TRUE2 (model, a, LT_EXPR, c);
-    ASSERT_CONDITION_TRUE2 (model, b, LT_EXPR, d);
-    ASSERT_CONDITION_TRUE2 (model, a, LT_EXPR, d);
-  }
-
-  /* Transitivity: "a >= b", "b >= a" should imply that a == b.  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ADD_SAT_CONSTRAINT (model, a, GE_EXPR, b);
-    ADD_SAT_CONSTRAINT (model, b, GE_EXPR, a);
-
-    // TODO:
-    ASSERT_CONDITION_TRUE2 (model, a, EQ_EXPR, b);
-
-    /* The ECs for a and b should have merged, and any constraints removed.  */
-    ASSERT_EQ (model.get_constraints2 ()->m_equiv_classes.length (), 1);
-    ASSERT_EQ (model.get_constraints2 ()->m_constraints.length (), 0);
-  }
-
-  /* Transitivity: "a >= b", "b > a" should be impossible.  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ADD_SAT_CONSTRAINT (model, a, GE_EXPR, b);
-    ADD_UNSAT_CONSTRAINT (model, b, GT_EXPR, a);
-  }
-
-  /* Transitivity: "a >= b", "b >= c", "c >= a" should imply
-     that a == b == c.  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ADD_SAT_CONSTRAINT (model, a, GE_EXPR, b);
-    ADD_SAT_CONSTRAINT (model, b, GE_EXPR, c);
-    ADD_SAT_CONSTRAINT (model, c, GE_EXPR, a);
-
-    ASSERT_CONDITION_TRUE2 (model, a, EQ_EXPR, c);
-  }
-
-  /* Transitivity: "a > b", "b > c", "c > a"
-     should be impossible.  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ADD_SAT_CONSTRAINT (model, a, GT_EXPR, b);
-    ADD_SAT_CONSTRAINT (model, b, GT_EXPR, c);
-    ADD_UNSAT_CONSTRAINT (model, c, GT_EXPR, a);
-  }
-
-}
-
-/* Test various conditionals involving constants where the results
-   ought to be implied based on the values of the constants.  */
-
-static void
-test_constant_comparisons ()
-{
-  tree int_3 = build_int_cst (integer_type_node, 3);
-  tree int_4 = build_int_cst (integer_type_node, 4);
-  tree int_5 = build_int_cst (integer_type_node, 5);
-
-  tree int_1023 = build_int_cst (integer_type_node, 1023);
-  tree int_1024 = build_int_cst (integer_type_node, 1024);
-
-  tree a = build_global_decl ("a", integer_type_node);
-  tree b = build_global_decl ("b", integer_type_node);
-
-  /* Given a >= 1024, then a <= 1023 should be impossible.  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ADD_SAT_CONSTRAINT (model, a, GE_EXPR, int_1024);
-    ADD_UNSAT_CONSTRAINT (model, a, LE_EXPR, int_1023);
-  }
-
-  /* a > 4.  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ADD_SAT_CONSTRAINT (model, a, GT_EXPR, int_4);
-    ASSERT_CONDITION_TRUE2 (model, a, GT_EXPR, int_4);
-    ASSERT_CONDITION_TRUE2 (model, a, NE_EXPR, int_3);
-    ASSERT_CONDITION_UNKNOWN2 (model, a, NE_EXPR, int_5);
-  }
-
-  /* a <= 4.  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ADD_SAT_CONSTRAINT (model, a, LE_EXPR, int_4);
-    ASSERT_CONDITION_FALSE2 (model, a, GT_EXPR, int_4);
-    ASSERT_CONDITION_FALSE2 (model, a, GT_EXPR, int_5);
-    ASSERT_CONDITION_UNKNOWN2 (model, a, NE_EXPR, int_3);
-  }
-
-  /* If "a > b" and "a == 3", then "b == 4" ought to be unsatisfiable.  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ADD_SAT_CONSTRAINT (model, a, GT_EXPR, b);
-    ADD_SAT_CONSTRAINT (model, a, EQ_EXPR, int_3);
-    ADD_UNSAT_CONSTRAINT (model, b, EQ_EXPR, int_4);
-  }
-
-  /* Various tests of int ranges where there is only one possible candidate.  */
-  {
-    /* If "a <= 4" && "a > 3", then "a == 4",
-       assuming a is of integral type.  */
-    {
-      region_model2_manager mgr;
-      region_model2 model (&mgr);
-      ADD_SAT_CONSTRAINT (model, a, LE_EXPR, int_4);
-      ADD_SAT_CONSTRAINT (model, a, GT_EXPR, int_3);
-      ASSERT_CONDITION_TRUE2 (model, a, EQ_EXPR, int_4);
-    }
-
-    /* If "a > 3" && "a <= 4", then "a == 4",
-       assuming a is of integral type.  */
-    {
-      region_model2_manager mgr;
-      region_model2 model (&mgr);
-      ADD_SAT_CONSTRAINT (model, a, GT_EXPR, int_3);
-      ADD_SAT_CONSTRAINT (model, a, LE_EXPR, int_4);
-      ASSERT_CONDITION_TRUE2 (model, a, EQ_EXPR, int_4);
-    }
-    /* If "a > 3" && "a < 5", then "a == 4",
-       assuming a is of integral type.  */
-    {
-      region_model2_manager mgr;
-      region_model2 model (&mgr);
-      ADD_SAT_CONSTRAINT (model, a, GT_EXPR, int_3);
-      ADD_SAT_CONSTRAINT (model, a, LT_EXPR, int_5);
-      ASSERT_CONDITION_TRUE2 (model, a, EQ_EXPR, int_4);
-    }
-    /* If "a >= 4" && "a < 5", then "a == 4",
-       assuming a is of integral type.  */
-    {
-      region_model2_manager mgr;
-      region_model2 model (&mgr);
-      ADD_SAT_CONSTRAINT (model, a, GE_EXPR, int_4);
-      ADD_SAT_CONSTRAINT (model, a, LT_EXPR, int_5);
-      ASSERT_CONDITION_TRUE2 (model, a, EQ_EXPR, int_4);
-    }
-    /* If "a >= 4" && "a <= 4", then "a == 4".  */
-    {
-      region_model2_manager mgr;
-      region_model2 model (&mgr);
-      ADD_SAT_CONSTRAINT (model, a, GE_EXPR, int_4);
-      ADD_SAT_CONSTRAINT (model, a, LE_EXPR, int_4);
-      ASSERT_CONDITION_TRUE2 (model, a, EQ_EXPR, int_4);
-    }
-  }
-
-  /* As above, but for floating-point:
-     if "f > 3" && "f <= 4" we don't know that f == 4.  */
-  {
-    tree f = build_global_decl ("f", double_type_node);
-    tree float_3 = build_real_from_int_cst (double_type_node, int_3);
-    tree float_4 = build_real_from_int_cst (double_type_node, int_4);
-
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ADD_SAT_CONSTRAINT (model, f, GT_EXPR, float_3);
-    ADD_SAT_CONSTRAINT (model, f, LE_EXPR, float_4);
-    ASSERT_CONDITION_UNKNOWN2 (model, f, EQ_EXPR, float_4);
-    ASSERT_CONDITION_UNKNOWN2 (model, f, EQ_EXPR, int_4);
-  }
-}
-
-/* Verify various lower-level implementation details about
-   constraint_manager2.  */
-
-static void
-test_constraint_impl ()
-{
-  tree int_42 = build_int_cst (integer_type_node, 42);
-  tree int_0 = build_int_cst (integer_type_node, 0);
-
-  tree x = build_global_decl ("x", integer_type_node);
-  tree y = build_global_decl ("y", integer_type_node);
-  tree z = build_global_decl ("z", integer_type_node);
-
-  /* x == y.  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-
-    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, y);
-
-    /* Assert various things about the insides of model.  */
-    constraint_manager2 *cm = model.get_constraints2 ();
-    ASSERT_EQ (cm->m_constraints.length (), 0);
-    ASSERT_EQ (cm->m_equiv_classes.length (), 1);
-  }
-
-  /* y <= z; x == y.  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ASSERT_CONDITION_UNKNOWN2 (model, x, EQ_EXPR, y);
-    ASSERT_CONDITION_UNKNOWN2 (model, x, GE_EXPR, z);
-
-    ADD_SAT_CONSTRAINT (model, y, GE_EXPR, z);
-    ASSERT_CONDITION_TRUE2 (model, y, GE_EXPR, z);
-    ASSERT_CONDITION_UNKNOWN2 (model, x, GE_EXPR, z);
-
-    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, y);
-
-    /* Assert various things about the insides of model.  */
-    constraint_manager2 *cm = model.get_constraints2 ();
-    ASSERT_EQ (cm->m_constraints.length (), 1);
-    ASSERT_EQ (cm->m_equiv_classes.length (), 2);
-
-    /* Ensure that we merged the constraints.  */
-    ASSERT_CONDITION_TRUE2 (model, x, GE_EXPR, z);
-  }
-
-  /* y <= z; y == x.  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-    ASSERT_CONDITION_UNKNOWN2 (model, x, EQ_EXPR, y);
-    ASSERT_CONDITION_UNKNOWN2 (model, x, GE_EXPR, z);
-
-    ADD_SAT_CONSTRAINT (model, y, GE_EXPR, z);
-    ASSERT_CONDITION_TRUE2 (model, y, GE_EXPR, z);
-    ASSERT_CONDITION_UNKNOWN2 (model, x, GE_EXPR, z);
-
-    ADD_SAT_CONSTRAINT (model, y, EQ_EXPR, x);
-
-    /* Assert various things about the insides of model.  */
-    constraint_manager2 *cm = model.get_constraints2 ();
-    ASSERT_EQ (cm->m_constraints.length (), 1);
-    ASSERT_EQ (cm->m_equiv_classes.length (), 2);
-
-    /* Ensure that we merged the constraints.  */
-    ASSERT_CONDITION_TRUE2 (model, x, GE_EXPR, z);
-  }
-
-  /* x == 0, then x != 42.  */
-  {
-    region_model2_manager mgr;
-    region_model2 model (&mgr);
-
-    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, int_0);
-    ADD_SAT_CONSTRAINT (model, x, NE_EXPR, int_42);
-
-    /* Assert various things about the insides of model.  */
-    constraint_manager2 *cm = model.get_constraints2 ();
-    ASSERT_EQ (cm->m_constraints.length (), 0);
-    ASSERT_EQ (cm->m_equiv_classes.length (), 1);
-#if 0
-    ASSERT_EQ (cm->m_constraints[0].m_lhs,
-	       cm->get_or_add_equiv_class2 (model.get_rvalue (int_0, NULL)));
-    ASSERT_EQ (cm->m_constraints[0].m_rhs,
-	       cm->get_or_add_equiv_class2 (model.get_rvalue (int_42, NULL)));
-    ASSERT_EQ (cm->m_constraints[0].m_op, CONSTRAINT2_LT);
-#endif
-  }
-
-  // TODO: selftest for merging ecs "in the middle"
-  // where a non-final one gets overwritten
-
-  // TODO: selftest where there are pre-existing constraints
-}
-
-/* Check that operator== and hashing works as expected for the
-   various types.  */
-
-static void
-test_equality ()
-{
-  tree x = build_global_decl ("x", integer_type_node);
-  tree y = build_global_decl ("y", integer_type_node);
-
-  {
-    region_model2_manager mgr;
-    region_model2 model0 (&mgr);
-    region_model2 model1 (&mgr);
-
-    constraint_manager2 *cm0 = model0.get_constraints2 ();
-    constraint_manager2 *cm1 = model1.get_constraints2 ();
-
-    ASSERT_EQ (cm0->hash (), cm1->hash ());
-    ASSERT_EQ (*cm0, *cm1);
-
-#if 0
-    ASSERT_EQ (model0.hash (), model1.hash ());
-#endif
-    ASSERT_EQ (model0, model1);
-
-    ADD_SAT_CONSTRAINT (model1, x, EQ_EXPR, y);
-    ASSERT_NE (cm0->hash (), cm1->hash ());
-    ASSERT_NE (*cm0, *cm1);
-
-#if 0
-    ASSERT_NE (model0.hash (), model1.hash ());
-#endif
-    ASSERT_NE (model0, model1);
-
-    region_model2 model2 (&mgr);
-    constraint_manager2 *cm2 = model2.get_constraints2 ();
-    /* Make the same change to cm2.  */
-    ADD_SAT_CONSTRAINT (model2, x, EQ_EXPR, y);
-    ASSERT_EQ (cm1->hash (), cm2->hash ());
-    ASSERT_EQ (*cm1, *cm2);
-
-#if 0
-    ASSERT_EQ (model1.hash (), model2.hash ());
-#endif
-    ASSERT_EQ (model1, model2);
-  }
-}
-
-/* Verify tracking inequality of a variable against many constants.  */
-
-static void
-test_many_constants ()
-{
-  tree a = build_global_decl ("a", integer_type_node);
-
-  region_model2_manager mgr;
-  region_model2 model (&mgr);
-  auto_vec<tree> constants;
-  for (int i = 0; i < 20; i++)
-    {
-      tree constant = build_int_cst (integer_type_node, i);
-      constants.safe_push (constant);
-      ADD_SAT_CONSTRAINT (model, a, NE_EXPR, constant);
-
-      /* Merge, and check the result.  */
-      region_model2 other (model);
-
-#if 0
-      region_model2 merged (&mgr);
-      ASSERT_TRUE (model.can_merge_with_p (other, &merged));
-      model.canonicalize (NULL);
-      merged.canonicalize (NULL);
-      ASSERT_EQ (model, merged);
-#endif
-
-      for (int j = 0; j <= i; j++)
-	ASSERT_CONDITION_TRUE2 (model, a, NE_EXPR, constants[j]);
-    }
-}
-
-/* FIXME.  */
-
-static void
-test_redundant_constraints ()
-{
-  tree int_257 = build_int_cst (integer_type_node, 257);
-  tree int_256 = build_int_cst (integer_type_node, 256);
-  tree i = build_global_decl ("i", integer_type_node);
-
-  {
-    /* i < 257 then i < 256.  */
-    region_model2_manager mgr;
-    region_model2 model0 (&mgr);
-    ADD_SAT_CONSTRAINT (model0, i, LT_EXPR, int_257);
-    ADD_SAT_CONSTRAINT (model0, i, LT_EXPR, int_256);
-    ASSERT_CONDITION_TRUE2 (model0, i, LT_EXPR, int_256);
-    ASSERT_CONDITION_TRUE2 (model0, i, LT_EXPR, int_257);
-    /* The older constraint was made redundant by the newer;
-       it should have been removed.  */
-    //ASSERT_EQ (model0.get_constraints2 ()->m_constraints.length (), 1);
-    //ASSERT_EQ (model1.get_constraints2 ()->m_equiv_classes.length (), 2);
-
-    /* i < 256 then i < 257.  */
-    region_model2 model1 (&mgr);
-    ADD_SAT_CONSTRAINT (model1, i, LT_EXPR, int_256);
-    ADD_SAT_CONSTRAINT (model1, i, LT_EXPR, int_257);
-    ASSERT_CONDITION_TRUE2 (model1, i, LT_EXPR, int_256);
-    ASSERT_CONDITION_TRUE2 (model1, i, LT_EXPR, int_257);
-    ASSERT_EQ (model1.get_constraints2 ()->m_equiv_classes.length (), 2);
-    ASSERT_EQ (model1.get_constraints2 ()->m_constraints.length (), 1);
-
-    // TODO:
-    //ASSERT_EQ (model1, model0);
-  }
-
-  {
-    /* i <= 257 then i <= 256.  */
-    region_model2_manager mgr;
-    region_model2 model0 (&mgr);
-    ADD_SAT_CONSTRAINT (model0, i, LE_EXPR, int_257);
-    ADD_SAT_CONSTRAINT (model0, i, LE_EXPR, int_256);
-    ASSERT_CONDITION_TRUE2 (model0, i, LE_EXPR, int_256);
-    ASSERT_CONDITION_TRUE2 (model0, i, LE_EXPR, int_257);
-    /* The older constraint was made redundant by the newer;
-       it should have been removed.  */
-    //ASSERT_EQ (model0.get_constraints2 ()->m_constraints.length (), 1);
-    //ASSERT_EQ (model1.get_constraints2 ()->m_equiv_classes.length (), 2);
-
-    /* i <= 256 then i <= 257.  */
-    region_model2 model1 (&mgr);
-    ADD_SAT_CONSTRAINT (model1, i, LE_EXPR, int_256);
-    ADD_SAT_CONSTRAINT (model1, i, LE_EXPR, int_257);
-    ASSERT_CONDITION_TRUE2 (model1, i, LE_EXPR, int_256);
-    ASSERT_CONDITION_TRUE2 (model1, i, LE_EXPR, int_257);
-    ASSERT_EQ (model1.get_constraints2 ()->m_equiv_classes.length (), 2);
-    ASSERT_EQ (model1.get_constraints2 ()->m_constraints.length (), 1);
-
-    // TODO:
-    //ASSERT_EQ (model1, model0);
-  }
-
-}
-
-/* Run the selftests in this file, temporarily overriding
-   flag_analyzer_transitivity with TRANSITIVITY.  */
-
-static void
-run_constraint_manager2_tests (bool transitivity)
-{
-  int saved_flag_analyzer_transitivity = flag_analyzer_transitivity;
-  flag_analyzer_transitivity = transitivity;
-
-  test_constraint_conditions ();
-  if (flag_analyzer_transitivity)
-    {
-      /* These selftests assume transitivity.  */
-      test_transitivity ();
-    }
-  test_constant_comparisons ();
-  test_constraint_impl ();
-  test_equality ();
-  test_many_constants ();
-  test_redundant_constraints ();
-
-  flag_analyzer_transitivity = saved_flag_analyzer_transitivity;
-}
-
-/* Run all of the selftests within this file.  */
-
-void
-analyzer_constraint_manager2_cc_tests ()
-{
-  /* Run the tests twice: with and without transitivity.  */
-  run_constraint_manager2_tests (true);
-  run_constraint_manager2_tests (false);
-}
-
-} // namespace selftest
-
-#endif /* CHECKING_P */
-
-} // namespace ana
-
-#endif /* #if ENABLE_ANALYZER */
diff --git a/gcc/analyzer/constraint-manager2.h b/gcc/analyzer/constraint-manager2.h
deleted file mode 100644
index 8fae2b488b1..00000000000
--- a/gcc/analyzer/constraint-manager2.h
+++ /dev/null
@@ -1,297 +0,0 @@
-/* Tracking equivalence classes and constraints at a point on an execution path.
-   Copyright (C) 2019-2020 Free Software Foundation, Inc.
-   Contributed by David Malcolm <dmalcolm@redhat.com>.
-
-This file is part of GCC.
-
-GCC is free software; you can redistribute it and/or modify it
-under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 3, or (at your option)
-any later version.
-
-GCC is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING3.  If not see
-<http://www.gnu.org/licenses/>.  */
-
-#ifndef GCC_ANALYZER_CONSTRAINT_MANAGER2_H
-#define GCC_ANALYZER_CONSTRAINT_MANAGER2_H
-
-namespace ana {
-
-class constraint_manager2;
-
-/* One of the end-points of a range2.  */
-
-struct bound2
-{
-  bound2 () : m_constant (NULL_TREE), m_closed (false) {}
-  bound2 (tree constant, bool closed)
-  : m_constant (constant), m_closed (closed) {}
-
-  void ensure_closed (bool is_upper);
-
-  const char * get_relation_as_str () const;
-
-  tree m_constant;
-  bool m_closed;
-};
-
-/* A range of values, used for determining if a value has been
-   constrained to just one possible constant value.  */
-
-struct range2
-{
-  range2 () : m_lower_bound (), m_upper_bound () {}
-  range2 (const bound2 &lower, const bound2 &upper)
-  : m_lower_bound (lower), m_upper_bound (upper) {}
-
-  void dump_to_pp (pretty_printer *pp) const;
-  void dump () const;
-
-  tree constrained_to_single_element ();
-
-  tristate eval_condition (enum tree_code op,
-			   tree rhs_const) const;
-  bool below_lower_bound (tree rhs_const) const;
-  bool above_upper_bound (tree rhs_const) const;
-
-  bound2 m_lower_bound;
-  bound2 m_upper_bound;
-};
-
-/* An equivalence class within a constraint manager: a set of
-   svalue_ids that are known to all be equal to each other,
-   together with an optional tree constant that they are equal to.  */
-
-class equiv_class2
-{
-public:
-  equiv_class2 ();
-  equiv_class2 (const equiv_class2 &other);
-
-  hashval_t hash () const;
-  bool operator== (const equiv_class2 &other);
-
-  void add (const svalue2 *sval);
-  bool del (const svalue2 *sval);
-
-  tree get_any_constant () const { return m_constant; }
-
-  const svalue2 *get_representative () const;
-
-  void canonicalize ();
-
-  void print (pretty_printer *pp) const;
-
-  /* An equivalence class can contain multiple constants (e.g. multiple
-     different zeroes, for different types); these are just for the last
-     constant added.  */
-  tree m_constant;
-  const svalue2 *m_cst_sval;
-
-  // TODO: should this be a set rather than a vec?
-  auto_vec<const svalue2 *> m_vars;
-};
-
-/* The various kinds of constraint.  */
-
-enum constraint2_op
-{
-  CONSTRAINT2_NE,
-  CONSTRAINT2_LT,
-  CONSTRAINT2_LE
-};
-
-const char *constraint_op_code (enum constraint2_op c_op);
-
-/* An ID for an equiv_class2 within a constraint_manager2.  Internally, this
-   is an index into a vector of equiv_class2 * within the constraint_manager2.  */
-
-class equiv_class2_id
-{
-public:
-  static equiv_class2_id null () { return equiv_class2_id (-1); }
-
-  equiv_class2_id (unsigned idx) : m_idx (idx) {}
-  const equiv_class2 &get_obj (const constraint_manager2 &cm) const;
-  equiv_class2 &get_obj (constraint_manager2 &cm) const;
-
-  bool operator== (const equiv_class2_id &other) const
-  {
-    return m_idx == other.m_idx;
-  }
-  bool operator!= (const equiv_class2_id &other) const
-  {
-    return m_idx != other.m_idx;
-  }
-
-  bool null_p () const { return m_idx == -1; }
-
-  static equiv_class2_id from_int (int idx) { return equiv_class2_id (idx); }
-  int as_int () const { return m_idx; }
-
-  void print (pretty_printer *pp) const;
-
-  void update_for_removal (equiv_class2_id other)
-  {
-    if (m_idx > other.m_idx)
-      m_idx--;
-  }
-
-  int m_idx;
-};
-
-/* A relationship between two equivalence classes in a constraint_manager2.  */
-
-class constraint2
-{
- public:
-  constraint2 (equiv_class2_id lhs, enum constraint2_op c_op, equiv_class2_id rhs)
-  : m_lhs (lhs), m_op (c_op), m_rhs (rhs)
-  {
-    gcc_assert (!lhs.null_p ());
-    gcc_assert (!rhs.null_p ());
-  }
-
-  void print (pretty_printer *pp, const constraint_manager2 &cm) const;
-
-  hashval_t hash () const;
-  bool operator== (const constraint2 &other) const;
-
-  /* Is this an ordering, rather than a "!=".  */
-  bool is_ordering_p () const
-  {
-    return m_op != CONSTRAINT2_NE;
-  }
-
-  bool implied_by (const constraint2 &other,
-		   const constraint_manager2 &cm) const;
-
-  equiv_class2_id m_lhs;
-  enum constraint2_op m_op;
-  equiv_class2_id m_rhs;
-};
-
-/* An abstract base class for use with constraint_manager2::for_each_fact.  */
-
-class fact2_visitor
-{
- public:
-  virtual ~fact2_visitor () {}
-  virtual void on_fact (const svalue2 *lhs,
-			enum tree_code,
-			const svalue2 *rhs) = 0;
-};
-
-/* A collection of equivalence classes and constraints on them.
-
-   Given N svalue2s, this can be thought of as representing a subset of
-   N-dimensional space.  When we call add_constraint,
-   we are effectively taking an intersection with that constraint.  */
-
-class constraint_manager2
-{
-public:
-  constraint_manager2 (region_model2_manager *mgr) : m_mgr (mgr) {}
-  constraint_manager2 (const constraint_manager2 &other);
-  virtual ~constraint_manager2 () {}
-
-#if 0
-  virtual constraint_manager2 *clone (region_model2 *) const = 0;
-#endif
-  constraint_manager2& operator= (const constraint_manager2 &other);
-
-  hashval_t hash () const;
-  bool operator== (const constraint_manager2 &other) const;
-  bool operator!= (const constraint_manager2 &other) const
-  {
-    return !(*this == other);
-  }
-
-  void print (pretty_printer *pp) const;
-  void dump_to_pp (pretty_printer *pp, bool multiline) const;
-  void dump (FILE *fp) const;
-  void dump () const;
-
-  const equiv_class2 &get_equiv_class2_by_index (unsigned idx) const
-  {
-    return *m_equiv_classes[idx];
-  }
-  equiv_class2 &get_equiv_class2_by_index (unsigned idx)
-  {
-    return *m_equiv_classes[idx];
-  }
-
-  equiv_class2 &get_equiv_class2 (const svalue2 *sval)
-  {
-    equiv_class2_id ec_id = get_or_add_equiv_class2 (sval);
-    return ec_id.get_obj (*this);
-  }
-
-  bool add_constraint (const svalue2 *lhs,
-		       enum tree_code op,
-		       const svalue2 *rhs);
-
-  bool add_constraint (equiv_class2_id lhs_ec_id,
-		       enum tree_code op,
-		       equiv_class2_id rhs_ec_id);
-
-  void add_unknown_constraint (equiv_class2_id lhs_ec_id,
-			       enum tree_code op,
-			       equiv_class2_id rhs_ec_id);
-
-  bool get_equiv_class2_by_svalue2 (const svalue2 *sval,
-				    equiv_class2_id *out) const;
-  equiv_class2_id get_or_add_equiv_class2 (const svalue2 *sval);
-  tristate eval_condition (equiv_class2_id lhs,
-			   enum tree_code op,
-			   equiv_class2_id rhs) const;
-  tristate eval_condition (equiv_class2_id lhs_ec,
-			   enum tree_code op,
-			   tree rhs_const) const;
-  tristate eval_condition (const svalue2 *lhs,
-			   enum tree_code op,
-			   const svalue2 *rhs) const;
-  range2 get_ec_bounds (equiv_class2_id ec_id) const;
-
-  /* PurgeCriteria should have:
-     bool should_purge_p (const svalue2 *sval) const.  */
-  template <typename PurgeCriteria>
-  void purge (const PurgeCriteria &p, purge_stats2 *stats);
-
-  void on_liveness_change (const hash_set<const svalue2 *> &live_svalues,
-			   const region_model2 *model);
-
-  void canonicalize ();
-
-  static void merge (const constraint_manager2 &cm_a,
-		     const constraint_manager2 &cm_b,
-		     constraint_manager2 *out,
-		     const model2_merger &merger);
-
-  void for_each_fact (fact2_visitor *) const;
-
-  void validate () const;
-
-  auto_delete_vec<equiv_class2> m_equiv_classes;
-  auto_vec<constraint2> m_constraints;
-
- private:
-  static void clean_merger_input (const constraint_manager2 &cm_in,
-				  constraint_manager2 *out);
-
-  void add_constraint_internal (equiv_class2_id lhs_id,
-				enum constraint2_op c_op,
-				equiv_class2_id rhs_id);
-
-  region_model2_manager *m_mgr;
-};
-
-} // namespace ana
-
-#endif /* GCC_ANALYZER_CONSTRAINT_MANAGER2_H */
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 0808cadeeef..65c8172eeb9 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -40,7 +40,6 @@ along with GCC; see the file COPYING3.  If not see
 #include "analyzer/analyzer.h"
 #include "analyzer/analyzer-logging.h"
 #include "analyzer/region-model.h"
-#include "analyzer/region-model2.h"
 #include "analyzer/constraint-manager.h"
 #include "analyzer/sm.h"
 #include "analyzer/pending-diagnostic.h"
diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h
index 003bded829d..1655a4ad878 100644
--- a/gcc/analyzer/exploded-graph.h
+++ b/gcc/analyzer/exploded-graph.h
@@ -21,7 +21,7 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_ANALYZER_EXPLODED_GRAPH_H
 #define GCC_ANALYZER_EXPLODED_GRAPH_H
 
-#include "region-model2.h" // FIXME
+#include "region-model.h" // FIXME
 
 namespace ana {
 
diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc
index ae444ada7f3..f80f10dca84 100644
--- a/gcc/analyzer/program-state.cc
+++ b/gcc/analyzer/program-state.cc
@@ -34,14 +34,11 @@ along with GCC; see the file COPYING3.  If not see
 #include "ordered-hash-map.h"
 #include "selftest.h"
 #include "analyzer/region-model.h"
-#include "analyzer/region-model2.h"
 #include "analyzer/program-state.h"
 #include "analyzer/constraint-manager.h"
-#include "analyzer/constraint-manager2.h"
 #include "alloc-pool.h"
 #include "fibonacci_heap.h"
 #include "shortest-paths.h"
-#include "analyzer/constraint-manager.h"
 #include "diagnostic-event-id.h"
 #include "analyzer/pending-diagnostic.h"
 #include "analyzer/diagnostic-manager.h"
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 48eb491b6d6..1bbafdee7a5 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -22,6 +22,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "tree.h"
+#include "gimple-pretty-print.h"
 #include "function.h"
 #include "basic-block.h"
 #include "gimple.h"
@@ -58,8 +59,12 @@ along with GCC; see the file COPYING3.  If not see
 #include "diagnostic-event-id.h"
 #include "analyzer/sm.h"
 #include "analyzer/pending-diagnostic.h"
+#include "analyzer/region-model2-reachability.h"
 #include "analyzer/analyzer-selftests.h"
 #include "stor-layout.h"
+#include "value-range.h" // for tree-ssanames.h
+#include "gimple-ssa.h" // for struct gimple_df in FOR_EACH_SSA_NAME
+#include "tree-ssanames.h" // for FOR_EACH_SSA_NAME
 
 #if ENABLE_ANALYZER
 
@@ -104,37 +109,711 @@ print_quoted_type (pretty_printer *pp, tree t)
   pp_end_quote (pp, pp_show_color (pp));
 }
 
-/* Dump this path_var to PP (which must support %E for trees).
+/* For use in printing a comma-separated list.  */
 
-   Express the stack depth using an "@DEPTH" suffix, so e.g. given
-     void foo (int j);
-     void bar (int i)
-     {
-       foo (i);
-     }
-   then:
-   - the "i" in "bar" would be "(i @ 0)"
-   - the "j" in "foo" would be "(j @ 1)".  */
+static void
+dump_separator (pretty_printer *pp, bool *is_first)
+{
+  if (!*is_first)
+    pp_string (pp, ", ");
+  *is_first = false;
+}
+
+#if 0
+/* Concrete subclass of constraint_manager that wires it up to a region_model2
+   (whilst allowing the constraint_manager and region_model2 to be somewhat
+   at arms length).
+   TODO: revisit this; maybe put the region_model2 * into the constraint_manager
+   base class.  */
+
+class impl_constraint_manager : public constraint_manager
+{
+ public:
+  impl_constraint_manager (region_model2 *model)
+  : constraint_manager (),
+    m_model (model)
+  {}
+
+  impl_constraint_manager (const impl_constraint_manager &other,
+			   region_model2 *model)
+  : constraint_manager (other),
+    m_model (model)
+  {}
+
+  constraint_manager *clone (region_model2 *model) const
+  {
+    return new impl_constraint_manager (*this, model);
+  }
+
+  tree maybe_get_constant (const svalue2 *sval) const FINAL OVERRIDE
+  {
+    const svalue2 *svalue2 = m_model->get_svalue2 (sid);
+    return svalue2->maybe_get_constant ();
+  }
+
+  svalue2_id get_sid_for_constant (tree cst) const FINAL OVERRIDE
+  {
+    gcc_assert (CONSTANT_CLASS_P (cst));
+    return m_model->get_rvalue (cst, NULL);
+  }
+
+  int get_num_svalue2s () const FINAL OVERRIDE
+  {
+    return m_model->get_num_svalue2s ();
+  }
+
+ private:
+  region_model2 *m_model;
+};
+#endif
+
+/* struct complexity.  */
+
+/* Get complexity for a new node that references REG
+   (the complexity of REG, plus one for the new node).  */
+
+complexity::complexity (const region2 *reg)
+: m_num_nodes (reg->get_complexity ().m_num_nodes + 1),
+  m_max_depth (reg->get_complexity ().m_max_depth + 1)
+{
+}
+
+/* Get complexity for a new node that references SVAL.
+   (the complexity of SVAL, plus one for the new node).  */
+
+complexity::complexity (const svalue2 *sval)
+: m_num_nodes (sval->get_complexity ().m_num_nodes + 1),
+  m_max_depth (sval->get_complexity ().m_max_depth + 1)
+{
+}
+
+/* Get complexity for a new node that references nodes with complexity
+   C1 and C2.  */
+
+complexity
+complexity::from_pair (const complexity &c1, const complexity &c2)
+{
+  return complexity (c1.m_num_nodes + c2.m_num_nodes + 1,
+		     MAX (c1.m_max_depth, c2.m_max_depth) + 1);
+}
 
+/* class svalue2 and its various subclasses.  */
+
+/* class svalue2.  */
+
+/* Dump this svalue2 in the form of a .dot record to PP.  */
+
+#if 0
 void
-path_var::dump (pretty_printer *pp) const
+svalue2::dump_dot_to_pp (const region_model2 &model,
+			pretty_printer *pp) const
+{
+  this_sval.dump_node_name_to_pp (pp);
+  pp_printf (pp, " [label=\"");
+  pp_write_text_to_stream (pp);
+  this_sval.print (pp);
+  pp_string (pp, ": {");
+  print (model, this_sval, pp);
+  pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/false);
+  pp_string (pp, "}\"];");
+  pp_newline (pp);
+}
+#endif
+
+/* FIXME.  */
+
+DEBUG_FUNCTION void
+svalue2::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);
+  pp_newline (&pp);
+  pp_flush (&pp);
+}
+
+/* FIXME.  */
+
+label_text
+svalue2::get_desc (bool simple) const
+{
+  pretty_printer pp;
+  pp_format_decoder (&pp) = default_tree_printer;
+  dump_to_pp (&pp, simple);
+  return label_text::take (xstrdup (pp_formatted_text (&pp)));
+}
+
+/* If this svalue2 is a constant_svalue2, return the underlying tree constant.
+   Otherwise return NULL_TREE.  */
+
+tree
+svalue2::maybe_get_constant () const
 {
-  if (m_tree == NULL_TREE)
-    pp_string (pp, "NULL");
-  if (CONSTANT_CLASS_P (m_tree))
-    pp_printf (pp, "%qE", m_tree);
+  if (const constant_svalue2 *cst_sval = dyn_cast_constant_svalue2 ())
+    return cst_sval->get_constant ();
   else
-    pp_printf (pp, "(%qE @ %i)", m_tree, m_stack_depth);
+    return NULL_TREE;
 }
 
-/* For use in printing a comma-separated list.  */
+/* If this svalue2 is a cast (i.e a unaryop NOP_EXPR), return the underlying
+   svalue2.
+   Otherwise return NULL.  */
 
-static void
-dump_separator (pretty_printer *pp, bool *is_first)
+const svalue2 *
+svalue2::maybe_undo_cast () const
 {
-  if (!*is_first)
-    pp_string (pp, ", ");
-  *is_first = false;
+  if (const unaryop_svalue2 *unaryop_sval = dyn_cast_unaryop_svalue2 ())
+    if (unaryop_sval->get_op () == NOP_EXPR)
+      return unaryop_sval->get_arg ();
+  return NULL;
+}
+
+const svalue2 *
+svalue2::unwrap_any_unmergeable () const
+{
+  if (const unmergeable_svalue2 *unmergeable = dyn_cast_unmergeable_svalue2 ())
+    return unmergeable->get_arg ();
+  return this;
+}
+
+/* Attempt to merge THIS with OTHER, returning the merged svalue2.
+   Return NULL if not mergeable.  */
+
+const svalue2 *
+svalue2::can_merge_p (const svalue2 *other,
+		      region_model2_manager *mgr,
+		      model2_merger *merger) const
+{
+  if (!(get_type () && other->get_type ()))
+    return NULL;
+
+  // FIXME: should we assert this instead?
+  if (!types_compatible_p (get_type (), other->get_type ()))
+    return NULL;
+
+  /* Reject attempts to merge unmergeable svalues.  */
+  if ((get_kind () == svalue2::SK_UNMERGEABLE)
+      || (other->get_kind () == svalue2::SK_UNMERGEABLE))
+    return NULL;
+
+  /* Reject attempts to merge NULL pointers with not-NULL-pointers.  */
+  if (POINTER_TYPE_P (get_type ()))
+    {
+      bool null0 = false;
+      bool null1 = false;
+      if (tree cst0 = maybe_get_constant ())
+	if (zerop (cst0))
+	  null0 = true;
+      if (tree cst1 = other->maybe_get_constant ())
+	if (zerop (cst1))
+	  null1 = true;
+      if (null0 != null1)
+	return NULL;
+    }
+
+#if WIDENING
+  /* Widening.  */
+  /* Merge: (new_cst, existing_cst) -> widen (existing, new).  */
+  if (tree cst0 = maybe_get_constant ())
+    if (tree cst1 = other->maybe_get_constant ())
+      {
+	return mgr->get_or_create_widening_svalue2 (other->get_type (),
+						    merger->m_point,
+						    other, this);
+      }
+  /* TODO: also detect widening symbolically via binary ops
+     e.g. for (i = start; i < end; i+= step).  */
+
+  /* Merge: (Widen(existing_val, V), existing_val) -> Widen (existing_val, V)
+     and thus get a fixed point.  */
+  if (const widening_svalue2 *widen_sval = dyn_cast_widening_svalue2 ())
+    {
+      /* FIXME: which direction??? */
+      if (other == widen_sval->get_base_svalue2 ())
+	return this;
+      if (other == widen_sval->get_iter_svalue2 ())
+	return this;
+    }
+
+  if (const binop_svalue2 *binop_sval = dyn_cast_binop_svalue2 ())
+    if (const widening_svalue2 *widen_arg0
+	= binop_sval->get_arg0 ()->dyn_cast_widening_svalue2 ())
+      {
+	if (other == binop_sval->get_arg1 ())
+	  {
+	    /* Merger of: (Widen(..., OTHER) BINOP X)
+	       and      : OTHER
+	       to       : (Widen(..., OTHER) BINOP X)
+	       e.g. merge of Widen(0, 1) + 1 with 1 to the Widen(0, 1) + 1.  */
+	    return this;
+	  }
+
+	/* Merger of : (Widen() BINOP X)
+	   and       : Widen()
+	   to        : Widen()
+	   e.g. merge of Widen(0, 1) + 1 and Widen(0, 1) to Widen(0, 1).
+	   However, we want to update constraints for this case, since we're
+	   considering another iteration.
+	   Presumably we also want to ensure that it converges; we don't want
+	   a descending chain of constraints.  */
+	if (other == widen_arg0)
+	  {
+#if ITERATED_BINOPS
+	    merger->add_iterating_widening_binop_thing (binop_sval);
+#endif
+	    return widen_arg0;
+	  }
+      }
+#endif
+
+  return mgr->get_or_create_unknown_svalue2 (get_type ());
+}
+
+/* FIXME.  */
+
+bool
+svalue2::live_p (const hash_set<const svalue2 *> &live_svalues,
+		 const region_model2 *model) const
+{
+  /* Determine if SVAL is explicitly live.  */
+  if (const_cast<hash_set<const svalue2 *> &> (live_svalues).contains (this))
+    return true;
+
+  /* Otherwise, determine if SVAL is implicitly live due to being made of
+     other live svalues.  */
+  return implicitly_live_p (live_svalues, model);
+}
+
+/* Base implementation of svalue2::implicitly_live_p.  */
+
+bool
+svalue2::implicitly_live_p (const svalue2_set &, const region_model2 *) const
+{
+  return false;
+}
+
+/* FIXME: group these here, or move them to their subclass groupings.  */
+
+/* Constants are implicitly live.  */
+
+bool
+constant_svalue2::implicitly_live_p (const svalue2_set &,
+				     const region_model2 *) const
+{
+  return true;
+}
+
+bool
+initial_svalue2::implicitly_live_p (const svalue2_set &live_svalues,
+				    const region_model2 *model) const
+{
+  /* This svalue2 may be implicitly live if the region still implicitly
+     has its initial value and is reachable.  */
+
+  /* It must be a region that exists; we don't want to consider
+     INIT_VAL(R) as still being implicitly reachable if R is in
+     a popped stack frame.  */
+  if (model->region_exists_p (m_reg))
+    {
+      const svalue2 *reg_sval = model->get_store_value (m_reg);
+      if (reg_sval == this)
+	return true;
+    }
+
+  return false;
+}
+
+bool
+unaryop_svalue2::implicitly_live_p (const svalue2_set &live_svalues,
+				    const region_model2 *model) const
+{
+  return get_arg ()->live_p (live_svalues, model);
+}
+
+bool
+binop_svalue2::implicitly_live_p (const svalue2_set &live_svalues,
+				  const region_model2 *model) const
+{
+  return (get_arg0 ()->live_p (live_svalues, model)
+	  && get_arg1 ()->live_p (live_svalues, model));
+}
+
+bool
+sub_svalue2::implicitly_live_p (const svalue2_set &live_svalues,
+				const region_model2 *model) const
+{
+  return get_parent ()->live_p (live_svalues, model);
+}
+
+bool
+unmergeable_svalue2::implicitly_live_p (const svalue2_set &live_svalues,
+					const region_model2 *model) const
+{
+  return get_arg ()->live_p (live_svalues, model);
+}
+
+/* class region_svalue2 : public svalue2.  */
+
+/* FIXME.  */
+
+void
+region_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    {
+      pp_string (pp, "&");
+      m_reg->dump_to_pp (pp, simple);
+    }
+  else
+    {
+      pp_string (pp, "region_svalue2(");
+      print_quoted_type (pp, get_type ());
+      pp_string (pp, ", ");
+      m_reg->dump_to_pp (pp, simple);
+      pp_string (pp, ")");
+    }
+}
+
+/* Compare the fields of this region_svalue2 with OTHER, returning true
+   if they are equal.
+   For use by svalue2::operator==.  */
+#if 0
+bool
+region_svalue2::compare_fields (const region_svalue2 &other) const
+{
+  return m_reg == other.m_reg;
+}
+#endif
+
+/* FIXME.  */
+
+void
+region_svalue2::accept (visitor *v) const
+{
+  v->visit_region_svalue2 (this);
+  m_reg->accept (v);
+}
+
+/* Implementation of svalue2::dump_dot_to_pp for region_svalue2.  */
+
+#if 0
+void
+region_svalue2::dump_dot_to_pp (const region_model2 &model,
+			       svalue2_id this_sval,
+			       pretty_printer *pp) const
+{
+  svalue2::dump_dot_to_pp (model, this_sval, pp);
+
+  /* If non-NULL, add an edge to the pointed-to region2.  */
+  if (!m_reg.null_p ())
+    {
+      this_sval.dump_node_name_to_pp (pp);
+      pp_string (pp, " -> ");
+      m_reg.dump_node_name_to_pp (pp);
+      pp_string (pp, ";");
+      pp_newline (pp);
+  }
+}
+#endif
+
+#if 0
+/* Merge REGION2_SVAL_A and REGION2_SVAL_B using MERGER, writing the result
+   into *MERGED_SVAL.  */
+
+void
+region_svalue2::merge_values (const region_svalue2 &region2_sval_a,
+			     const region_svalue2 &region2_sval_b,
+			     svalue2_id *merged_sval,
+			     tree type,
+			     model_merger *merger)
+{
+  region2_id a_reg = region2_sval_a.get_pointee ();
+  region2_id b_reg = region2_sval_b.get_pointee ();
+
+  /* Both are non-NULL. */
+  gcc_assert (!a_reg.null_p () && !b_reg.null_p ());
+
+  /* Have these ptr-values already been merged?  */
+
+  region2_id a_reg_in_m
+    = merger->m_map_region2s_from_a_to_m.get_dst_for_src (a_reg);
+  region2_id b_reg_in_m
+    = merger->m_map_region2s_from_b_to_m.get_dst_for_src (b_reg);
+
+  /* "null_p" here means "we haven't seen this ptr-value before".
+     If we've seen one but not the other, or we have different
+     region2s, then the merged ptr has to be "unknown".  */
+  if (a_reg_in_m != b_reg_in_m)
+    {
+      const svalue2 *merged_sval = new unknown_svalue2 (type);
+      *merged_sval = merger->m_merged_model->add_svalue2 (merged_sval);
+      return;
+    }
+
+  /* Have we seen this yet?  If so, reuse the value.  */
+  if (!a_reg_in_m.null_p ())
+    {
+      *merged_sval
+	= merger->m_merged_model->get_or_create_ptr_svalue2 (type, a_reg_in_m);
+      return;
+    }
+
+  /* Otherwise we have A/B region2s that haven't been referenced yet.  */
+
+  /* Are the region2s the "same", when seen from the tree point-of-view.
+     If so, create a merged pointer to it.  */
+  path_var pv_a = merger->m_model_a->get_representative_path_var (a_reg);
+  path_var pv_b = merger->m_model_b->get_representative_path_var (b_reg);
+  if (pv_a.m_tree
+      && pv_a == pv_b)
+    {
+      region2_id merged_pointee_reg
+	= merger->m_merged_model->get_lvalue (pv_a, NULL);
+      *merged_sval
+	= merger->m_merged_model->get_or_create_ptr_svalue2 (type,
+							    merged_pointee_reg);
+      merger->record_region2s (a_reg, b_reg, merged_pointee_reg);
+      return;
+    }
+
+  /* Handle an A/B pair of ptrs that both point at heap region2s.
+     If they both have a heap region2 in the merger model, merge them.  */
+  region2 *region2_a = merger->m_model_a->get_region2 (a_reg);
+  region2 *region2_b = merger->m_model_b->get_region2 (b_reg);
+  region2_id a_parent_reg = region2_a->get_parent ();
+  region2_id b_parent_reg = region2_b->get_parent ();
+  region2 *parent_region2_a = merger->m_model_a->get_region2 (a_parent_reg);
+  region2 *parent_region2_b = merger->m_model_b->get_region2 (b_parent_reg);
+  if (parent_region2_a
+      && parent_region2_b
+      && parent_region2_a->get_kind () == RK_HEAP
+      && parent_region2_b->get_kind () == RK_HEAP)
+    {
+	/* We have an A/B pair of ptrs that both point at heap region2s.  */
+	/* presumably we want to see if each A/B heap region2 already
+	   has a merged region2, and, if so, is it the same one.  */
+	// This check is above
+
+	region2_id merged_pointee_reg
+	  = merger->m_merged_model->add_new_malloc_region2 ();
+	*merged_sval
+	  = merger->m_merged_model->get_or_create_ptr_svalue2
+	     (type, merged_pointee_reg);
+	merger->record_region2s (a_reg, b_reg, merged_pointee_reg);
+	return;
+    }
+
+  /* Two different non-NULL pointers?  Merge to unknown.  */
+  const svalue2 *merged_sval = new unknown_svalue2 (type);
+  *merged_sval = merger->m_merged_model->add_svalue2 (merged_sval);
+  return;
+}
+#endif
+
+/* Evaluate the condition LHS OP RHS.
+   Subroutine of region_model2::eval_condition for when we have a pair of
+   pointers.  */
+
+tristate
+region_svalue2::eval_condition (const region_svalue2 *lhs,
+				enum tree_code op,
+				const region_svalue2 *rhs)
+{
+  /* See if they point to the same region2.  */
+  const region2 *lhs_reg = lhs->get_pointee ();
+  const region2 *rhs_reg = rhs->get_pointee ();
+  bool ptr_equality = lhs_reg == rhs_reg;
+  // FIXME: what about when we have unknown values?
+  /* TODO: what about child region2s where the child is the first child
+     (or descendent)?
+     Presumably we should look at base regions and compare offsets
+     within them.  */
+  switch (op)
+    {
+    default:
+      gcc_unreachable ();
+
+    case EQ_EXPR:
+      if (ptr_equality)
+	return tristate::TS_TRUE;
+      else
+	return tristate::TS_FALSE;
+      break;
+
+    case NE_EXPR:
+      if (ptr_equality)
+	return tristate::TS_FALSE;
+      else
+	return tristate::TS_TRUE;
+      break;
+
+    case GE_EXPR:
+    case LE_EXPR:
+      if (ptr_equality)
+	return tristate::TS_TRUE;
+      break;
+
+    case GT_EXPR:
+    case LT_EXPR:
+      if (ptr_equality)
+	return tristate::TS_FALSE;
+      break;
+    }
+
+  return tristate::TS_UNKNOWN;
+}
+
+/* class constant_svalue2 : public svalue2.  */
+
+/* FIXME.  */
+
+void
+constant_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    {
+      pp_string (pp, "(");
+      dump_tree (pp, get_type ());
+      pp_string (pp, ")");
+      dump_tree (pp, m_cst_expr);
+    }
+  else
+    {
+      pp_string (pp, "constant_svalue2(");
+      print_quoted_type (pp, get_type ());
+      pp_string (pp, ", ");
+      dump_tree (pp, m_cst_expr);
+      pp_string (pp, ")");
+    }
+}
+
+/* FIXME.  */
+
+void
+constant_svalue2::accept (visitor *v) const
+{
+  v->visit_constant_svalue2 (this);
+}
+
+/* Compare the fields of this constant_svalue2 with OTHER, returning true
+   if they are equal.
+   For use by svalue2::operator==.  */
+#if 0
+bool
+constant_svalue2::compare_fields (const constant_svalue2 &other) const
+{
+  return m_cst_expr == other.m_cst_expr;
+}
+#endif
+
+/* Merge the CST_SVAL_A and CST_SVAL_B using MERGER, writing the id of
+   the resulting svalue2 into *MERGED_SVAL.  */
+#if 0
+void
+constant_svalue2::merge_values (const constant_svalue2 &cst_sval_a,
+			       const constant_svalue2 &cst_sval_b,
+			       svalue2_id *merged_sval,
+			       model_merger *merger)
+{
+  tree cst_a = cst_sval_a.get_constant ();
+  tree cst_b = cst_sval_b.get_constant ();
+  const svalue2 *merged_sval;
+  if (cst_a == cst_b)
+    {
+      /* If they are the same constant, merge as that constant value.  */
+      merged_sval = new constant_svalue2 (cst_a);
+    }
+  else
+    {
+      /* Otherwise, we have two different constant values.
+	 Merge as an unknown value.
+	 TODO: impose constraints on the value?
+	 (maybe just based on A, to avoid infinite chains)  */
+      merged_sval = new unknown_svalue2 (TREE_TYPE (cst_a));
+    }
+  *merged_sval = merger->m_merged_model->add_svalue2 (merged_sval);
+}
+#endif
+
+/* Evaluate the condition LHS OP RHS.
+   Subroutine of region_model2::eval_condition for when we have a pair of
+   constants.  */
+
+tristate
+constant_svalue2::eval_condition (const constant_svalue2 *lhs,
+				  enum tree_code op,
+				  const constant_svalue2 *rhs)
+{
+  tree lhs_const = lhs->get_constant ();
+  tree rhs_const = rhs->get_constant ();
+
+  gcc_assert (CONSTANT_CLASS_P (lhs_const));
+  gcc_assert (CONSTANT_CLASS_P (rhs_const));
+
+  /* Check for comparable types.  */
+  if (types_compatible_p (TREE_TYPE (lhs_const), TREE_TYPE (rhs_const)))
+    {
+      tree comparison
+	= fold_binary (op, boolean_type_node, lhs_const, rhs_const);
+      if (comparison == boolean_true_node)
+	return tristate (tristate::TS_TRUE);
+      if (comparison == boolean_false_node)
+	return tristate (tristate::TS_FALSE);
+    }
+  return tristate::TS_UNKNOWN;
+}
+
+/* Implementation of svalue2::get_child_sval vfunc for constant_svalue2.  */
+#if 0
+svalue2_id
+constant_svalue2::get_child_sval (region2 *parent ATTRIBUTE_UNUSED,
+				region2 *child,
+				region_model2 &model,
+				region_model2_context *ctxt ATTRIBUTE_UNUSED)
+{
+  /* TODO: handle the all-zeroes case by returning an all-zeroes of the
+     child type.  */
+
+  /* Otherwise, we don't have a good way to get a child value out of a
+     constant.
+
+     Handle this case by using an unknown value.  */
+  const svalue2 *unknown_sval = new unknown_svalue2 (child->get_type ());
+  return model.add_svalue2 (unknown_sval);
+}
+#endif
+
+/* class unknown_svalue2 : public svalue2.  */
+
+/* FIXME.  */
+
+void
+unknown_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    {
+      pp_string (pp, "UNKNOWN(");
+      if (get_type ())
+	dump_tree (pp, get_type ());
+      pp_character (pp, ')');
+    }
+  else
+    {
+      pp_string (pp, "unknown_svalue2(");
+      if (get_type ())
+	dump_tree (pp, get_type ());
+      pp_character (pp, ')');
+    }
+}
+
+/* FIXME.  */
+
+void
+unknown_svalue2::accept (visitor *v) const
+{
+  v->visit_unknown_svalue2 (this);
 }
 
 /* Get a string for KIND for use in debug dumps.  */
@@ -153,16 +832,10254 @@ poison_kind_to_str (enum poison_kind kind)
     }
 }
 
-#if CHECKING_P
+/* class poisoned_svalue2 : public svalue2.  */
 
-namespace selftest {
+/* Compare the fields of this poisoned_svalue2 with OTHER, returning true
+   if they are equal.
+   For use by svalue2::operator==.  */
+#if 0
+bool
+poisoned_svalue2::compare_fields (const poisoned_svalue2 &other) const
+{
+  return m_kind == other.m_kind;
+}
+#endif
+
+/* FIXME.  */
+
+void
+poisoned_svalue2::accept (visitor *v) const
+{
+  v->visit_poisoned_svalue2 (this);
+}
+
+/* FIXME.  */
+
+void
+poisoned_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    pp_printf (pp, "POISONED(%s)", poison_kind_to_str (m_kind));
+  else
+    pp_printf (pp, "poisoned_svalue2(%s)", poison_kind_to_str (m_kind));
+}
+
+/* class setjmp_svalue2's implementation is in engine.cc, so that it can use
+   the declaration of exploded_node.  */
+
+/* class initial_svalue2 : public svalue2.  */
+
+/* FIXME.  */
+
+void
+initial_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    {
+      pp_string (pp, "INIT_VAL(");
+      m_reg->dump_to_pp (pp, simple);
+      pp_string (pp, ")");
+    }
+  else
+    {
+      pp_string (pp, "initial_svalue2(");
+      print_quoted_type (pp, get_type ());
+      pp_string (pp, ", ");
+      m_reg->dump_to_pp (pp, simple);
+      pp_string (pp, ")");
+    }
+}
+
+/* FIXME.  */
+
+void
+initial_svalue2::accept (visitor *v) const
+{
+  v->visit_initial_svalue2 (this);
+  m_reg->accept (v);
+}
 
+/* Compare the fields of this initial_svalue2 with OTHER, returning true
+   if they are equal.
+   For use by svalue2::operator==.  */
+#if 0
+bool
+initial_svalue2::compare_fields (const initial_svalue2 &other) const
+{
+  return m_reg == other.m_reg;
+}
+#endif
+
+/* class unaryop_svalue2 : public svalue2.  */
+
+void
+unaryop_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    {
+      if (m_op == NOP_EXPR)
+	{
+	  pp_string (pp, "CAST(");
+	  dump_tree (pp, get_type ());
+	  pp_string (pp, ", ");
+	  m_arg->dump_to_pp (pp, simple);
+	  pp_character (pp, ')');
+	}
+      else
+	{
+	  pp_character (pp, '(');
+	  pp_string (pp, get_tree_code_name (m_op));
+	  //pp_string (pp, op_symbol_code (m_op));
+	  m_arg->dump_to_pp (pp, simple);
+	  pp_character (pp, ')');
+	}
+    }
+  else
+    {
+      pp_string (pp, "unaryop_svalue2 (");
+      // TODO: type?
+      pp_string (pp, get_tree_code_name (m_op));
+      pp_string (pp, ", ");
+      m_arg->dump_to_pp (pp, simple);
+      pp_character (pp, ')');
+    }
+}
+
+/* FIXME.  */
+
+void
+unaryop_svalue2::accept (visitor *v) const
+{
+  v->visit_unaryop_svalue2 (this);
+  m_arg->accept (v);
+}
+
+/* class binop_svalue2 : public svalue2.  */
+
+/* FIXME.  */
+
+void
+binop_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    {
+      pp_character (pp, '(');
+      m_arg0->dump_to_pp (pp, simple);
+      pp_string (pp, op_symbol_code (m_op));
+      m_arg1->dump_to_pp (pp, simple);
+      pp_character (pp, ')');
+    }
+  else
+    {
+      pp_string (pp, "binop_svalue2 (");
+      // TODO: type?
+      pp_string (pp, get_tree_code_name (m_op));
+      pp_string (pp, ", ");
+      m_arg0->dump_to_pp (pp, simple);
+      pp_string (pp, ", ");
+      m_arg1->dump_to_pp (pp, simple);
+      pp_character (pp, ')');
+    }
+}
+
+/* FIXME.  */
+
+void
+binop_svalue2::accept (visitor *v) const
+{
+  v->visit_binop_svalue2 (this);
+  m_arg0->accept (v);
+  m_arg1->accept (v);
+}
+
+/* class sub_svalue2 : public svalue2.  */
+
+sub_svalue2::sub_svalue2 (tree type, const svalue2 *parent_svalue,
+			  const region2 *subregion)
+: svalue2 (complexity::from_pair (parent_svalue->get_complexity (),
+				  subregion->get_complexity ()),
+	   type),
+  m_parent_svalue (parent_svalue), m_subregion (subregion)
+{
+}
+
+/* FIXME.  */
+
+void
+sub_svalue2::accept (visitor *v) const
+{
+  v->visit_sub_svalue2 (this);
+  m_parent_svalue->accept (v);
+  m_subregion->accept (v);
+}
+
+void
+sub_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    {
+      pp_string (pp, "SUB(");
+      m_parent_svalue->dump_to_pp (pp, simple);
+      pp_string (pp, ", ");
+      m_subregion->dump_to_pp (pp, simple);
+      pp_character (pp, ')');
+    }
+  else
+    {
+      pp_string (pp, "sub_svalue2 (");
+      // TODO: type?
+      pp_string (pp, ", ");
+      m_parent_svalue->dump_to_pp (pp, simple);
+      pp_string (pp, ", ");
+      m_subregion->dump_to_pp (pp, simple);
+      pp_character (pp, ')');
+    }
+}
+
+/* class widening_svalue2 : public svalue2.  */
+
+/* FIXME.  */
+
+void
+widening_svalue2::accept (visitor *v) const
+{
+  v->visit_widening_svalue2 (this);
+  m_base_sval->accept (v);
+  m_iter_sval->accept (v);
+}
+
+void
+widening_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    {
+      pp_string (pp, "WIDENING(");
+      pp_character (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);
+      pp_character (pp, ')');
+    }
+  else
+    {
+      pp_string (pp, "widening_svalue2 (");
+      // TODO: type?
+      pp_string (pp, ", ");
+      pp_character (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);
+      pp_character (pp, ')');
+    }
+}
+
+/* FIXME.  */
+
+enum widening_svalue2::direction_t
+widening_svalue2::get_direction () const
+{
+  tree base_cst = m_base_sval->maybe_get_constant ();
+  if (base_cst == NULL_TREE)
+    return DIR_UNKNOWN;
+  tree iter_cst = m_iter_sval->maybe_get_constant ();
+  if (iter_cst == NULL_TREE)
+    return DIR_UNKNOWN;
+
+  tree iter_gt_base = fold_binary (GT_EXPR, boolean_type_node,
+				   iter_cst, base_cst);
+  if (iter_gt_base == boolean_true_node)
+    return DIR_ASCENDING;
+
+  tree iter_lt_base = fold_binary (LT_EXPR, boolean_type_node,
+				   iter_cst, base_cst);
+  if (iter_lt_base == boolean_true_node)
+    return DIR_DESCENDING;
+
+  return DIR_UNKNOWN;
+}
+
+/* FIXME.  */
+
+tristate
+widening_svalue2::eval_condition_without_cm (enum tree_code op,
+					     tree rhs_cst) const
+{
+  tree base_cst = m_base_sval->maybe_get_constant ();
+  if (base_cst == NULL_TREE)
+    return tristate::TS_UNKNOWN;
+  tree iter_cst = m_iter_sval->maybe_get_constant ();
+  if (iter_cst == NULL_TREE)
+    return tristate::TS_UNKNOWN;
+
+  switch (get_direction ())
+    {
+    default:
+      gcc_unreachable ();
+    case DIR_ASCENDING:
+      /* LHS is in [base_cst, +ve infinity), assuming no overflow.  */
+      switch (op)
+	{
+	case LE_EXPR:
+	case LT_EXPR:
+	  {
+	    /* [BASE, +INF) OP RHS:
+	       This is either true or false at +ve ininity,
+	       It can be true for points X where X OP RHS, so we have either
+	       "false", or "unknown".  */
+	    tree base_op_rhs = fold_binary (op, boolean_type_node,
+					    base_cst, rhs_cst);
+	    if (base_op_rhs == boolean_true_node)
+	      return tristate::TS_UNKNOWN;
+	    else
+	      return tristate::TS_FALSE;
+	  }
+
+	case GE_EXPR:
+	case GT_EXPR:
+	  {
+	    /* [BASE, +INF) OP RHS:
+	       This is true at +ve infinity.  It will be true everywhere
+	       in the range if BASE >= RHS.  */
+	    tree base_op_rhs = fold_binary (op, boolean_type_node,
+					    base_cst, rhs_cst);
+	    if (base_op_rhs == boolean_true_node)
+	      return tristate::TS_TRUE;
+	    else
+	      return tristate::TS_UNKNOWN;
+	  }
+
+	case EQ_EXPR:
+	  {
+	    /* [BASE, +INF) == RHS:
+	       Could this be true at any point in the range?  If so we
+	       have "unknown", otherwise we have "false".  */
+	    tree base_le_rhs = fold_binary (LE_EXPR, boolean_type_node,
+					    base_cst, rhs_cst);
+	    if (base_le_rhs == boolean_true_node)
+	      return tristate::TS_UNKNOWN;
+	    else
+	      return tristate::TS_FALSE;
+	  }
+
+	case NE_EXPR:
+	  {
+	    /* [BASE, +INF) != RHS:
+	       Could we have equality at any point in the range?  If so we
+	       have "unknown", otherwise we have "true".  */
+	    tree base_le_rhs = fold_binary (LE_EXPR, boolean_type_node,
+					    base_cst, rhs_cst);
+	    if (base_le_rhs == boolean_true_node)
+	      return tristate::TS_UNKNOWN;
+	    else
+	      return tristate::TS_TRUE;
+	  }
+
+	default:
+	  return tristate::TS_UNKNOWN;
+	}
+
+    case DIR_DESCENDING:
+      /* LHS is in (-ve infinity, base_cst], assuming no overflow.  */
+      // TODO:
+      return tristate::TS_UNKNOWN;
+
+    case DIR_UNKNOWN:
+      return tristate::TS_UNKNOWN;
+    }
+}
+
+/* class placeholder_svalue2 : public svalue2.  */
+
+/* FIXME.  */
+
+void
+placeholder_svalue2::accept (visitor *v) const
+{
+  v->visit_placeholder_svalue2 (this);
+}
+
+void
+placeholder_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    {
+      pp_printf (pp, "PLACEHOLDER(%qs)", m_name);
+    }
+  else
+    {
+      pp_printf (pp, "placeholder_svalue2 (%qs)", m_name);
+      // TODO: type?
+    }
+}
+
+/* class unmergeable_svalue2 : public svalue2.  */
+
+/* FIXME.  */
+
+void
+unmergeable_svalue2::accept (visitor *v) const
+{
+  v->visit_unmergeable_svalue2 (this);
+  m_arg->accept (v);
+}
+
+void
+unmergeable_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    {
+      pp_string (pp, "UNMERGEABLE(");
+      m_arg->dump_to_pp (pp, simple);
+      pp_character (pp, ')');
+    }
+  else
+    {
+      pp_string (pp, "unmergeable_svalue2 (");
+      m_arg->dump_to_pp (pp, simple);
+      pp_character (pp, ')');
+    }
+}
+
+/* class compound_svalue2 : public svalue2.  */
+
+/* FIXME.  */
+
+void
+compound_svalue2::accept (visitor *v) const
+{
+  v->visit_compound_svalue2 (this);
+  for (binding_map::iterator_t iter = m_map.begin ();
+       iter != m_map.end (); ++iter)
+    {
+      //(*iter).first.accept (v);
+      (*iter).second->accept (v);
+    }
+}
+
+void
+compound_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    {
+      pp_string (pp, "COMPOUND(");
+      m_map.dump_to_pp (pp, simple, false);
+      pp_character (pp, ')');
+    }
+  else
+    {
+      pp_string (pp, "compound_svalue2 (");
+      // TODO: type?
+      pp_string (pp, ", ");
+      pp_character (pp, '{');
+      m_map.dump_to_pp (pp, simple, false);
+      pp_string (pp, "}, ");
+      pp_character (pp, ')');
+    }
+}
+
+/* FIXME.  */
+
+complexity
+compound_svalue2::calc_complexity (const binding_map &map)
+{
+  unsigned num_child_nodes = 0;
+  unsigned max_child_depth = 0;
+  for (binding_map::iterator_t iter = map.begin ();
+       iter != map.end (); ++iter)
+    {
+      const complexity &sval_c = (*iter).second->get_complexity ();
+      num_child_nodes += sval_c.m_num_nodes;
+      max_child_depth = MAX (max_child_depth, sval_c.m_max_depth);
+    }
+  return complexity (num_child_nodes + 1, max_child_depth + 1);
+}
+
+/* class conjured_svalue2 : public svalue2.  */
+
+/* FIXME.  */
+
+void
+conjured_svalue2::accept (visitor *v) const
+{
+  v->visit_conjured_svalue2 (this);
+  m_id_reg->accept (v);
+}
+
+void
+conjured_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    {
+      pp_string (pp, "CONJURED(");
+      pp_gimple_stmt_1 (pp, m_stmt, 0, (dump_flags_t)0);
+      pp_string (pp, ", ");
+      m_id_reg->dump_to_pp (pp, simple);
+      pp_character (pp, ')');
+    }
+  else
+    {
+      pp_string (pp, "conjured_svalue2 (");
+      // TODO: type?
+      pp_string (pp, ", ");
+      pp_gimple_stmt_1 (pp, m_stmt, 0, (dump_flags_t)0);
+      pp_string (pp, ", ");
+      m_id_reg->dump_to_pp (pp, simple);
+      pp_character (pp, ')');
+    }
+}
+
+/* class region2 and its various subclasses.  */
+
+/* Get a string for KIND for use in debug dumps.  */
+
+const char *
+region2::kind_to_str (enum kind kind)
+{
+  switch (kind)
+    {
+    default:
+      gcc_unreachable ();
+    case RK_FRAME:
+      return "frame";
+    case RK_GLOBALS:
+      return "globals";
+    case RK_CODE:
+      return "code";
+    case RK_FUNCTION:
+      return "function";
+    case RK_STACK:
+      return "stack";
+    case RK_HEAP:
+      return "heap";
+    case RK_ROOT:
+      return "root";
+#if 0
+    case RK_SYMBOLIC:
+      return "symbolic";
+#endif
+    }
+}
+
+/* class region2.  */
+
+region2::~region2 ()
+{
+  delete m_cached_offset;
+}
+
+/* FIXME.  */
+
+int
+region2::cmp_ids (const region2 *reg1, const region2 *reg2)
+{
+  return (long)reg1->get_id () - (long)reg2->get_id ();
+}
+
+/* Equality operator for region2.
+   After comparing base class fields and kind, the rest of the
+   comparison is handled off to a "compare_fields" member function
+   specific to the appropriate subclass.  */
+
+bool
+region2::operator== (const region2 &other) const
+{
+  if (m_parent != other.m_parent)
+    return false;
+  if (m_type != other.m_type)
+    return false;
+
+  enum kind this_kind = get_kind ();
+  enum kind other_kind = other.get_kind ();
+  if (this_kind != other_kind)
+    return false;
+#if 0
+  /* Compare views.  */
+  if (m_view_regs.length () != other.m_view_regs.length ())
+    return false;
+  int i;
+  region2_id *rid;
+  FOR_EACH_VEC_ELT (m_view_regs, i, rid)
+    if (! (*rid == other.m_view_regs[i]))
+      return false;
+#endif
+  switch (this_kind)
+    {
+    default:
+      gcc_unreachable ();
+    case RK_FRAME:
+      {
+	const frame_region2 &this_sub
+	  = (const frame_region2 &)*this;
+	const frame_region2 &other_sub
+	  = (const frame_region2 &)other;
+	return this_sub.compare_fields (other_sub);
+      }
+#if 0
+    case RK_GLOBALS:
+      {
+	const globals_region2 &this_sub
+	  = (const globals_region2 &)*this;
+	const globals_region2 &other_sub
+	  = (const globals_region2 &)other;
+	return this_sub.compare_fields (other_sub);
+      }
+    case RK_CODE:
+      {
+	const code_region2 &this_sub
+	  = (const code_region2 &)*this;
+	const code_region2 &other_sub
+	  = (const code_region2 &)other;
+	return this_sub.compare_fields (other_sub);
+      }
+    case RK_FUNCTION:
+      {
+	const function_region2 &this_sub
+	  = (const function_region2 &)*this;
+	const function_region2 &other_sub
+	  = (const function_region2 &)other;
+	return this_sub.compare_fields (other_sub);
+      }
+    case RK_STACK:
+      {
+	const stack_region2 &this_sub
+	  = (const stack_region2 &)*this;
+	const stack_region2 &other_sub
+	  = (const stack_region2 &)other;
+	return this_sub.compare_fields (other_sub);
+      }
+    case RK_ROOT:
+      {
+	const root_region2 &this_sub
+	  = (const root_region2 &)*this;
+	const root_region2 &other_sub
+	  = (const root_region2 &)other;
+	return this_sub.compare_fields (other_sub);
+      }
+#endif
+    case RK_SYMBOLIC:
+      {
+	const symbolic_region2 &this_sub
+	  = (const symbolic_region2 &)*this;
+	const symbolic_region2 &other_sub
+	  = (const symbolic_region2 &)other;
+	return this_sub.compare_fields (other_sub);
+      }
+#if 0
+    case RK_HEAP:
+      {
+	const heap_region2 &this_sub
+	  = (const heap_region2 &)*this;
+	const heap_region2 &other_sub
+	  = (const heap_region2 &)other;
+	return this_sub.compare_fields (other_sub);
+      }
+#endif
+    case RK_DECL:
+      {
+	const decl_region2 &this_sub
+	  = (const decl_region2 &)*this;
+	const decl_region2 &other_sub
+	  = (const decl_region2 &)other;
+	return this_sub.compare_fields (other_sub);
+      }
+    case RK_FIELD:
+      {
+	const field_region2 &this_sub
+	  = (const field_region2 &)*this;
+	const field_region2 &other_sub
+	  = (const field_region2 &)other;
+	return this_sub.compare_fields (other_sub);
+      }
+    case RK_ELEMENT:
+      {
+	const element_region2 &this_sub
+	  = (const element_region2 &)*this;
+	const element_region2 &other_sub
+	  = (const element_region2 &)other;
+	return this_sub.compare_fields (other_sub);
+      }
+    case RK_OFFSET:
+      {
+	const offset_region2 &this_sub
+	  = (const offset_region2 &)*this;
+	const offset_region2 &other_sub
+	  = (const offset_region2 &)other;
+	return this_sub.compare_fields (other_sub);
+      }
+    case RK_CAST:
+      {
+	const cast_region2 &this_sub
+	  = (const cast_region2 &)*this;
+	const cast_region2 &other_sub
+	  = (const cast_region2 &)other;
+	return this_sub.compare_fields (other_sub);
+      }
+    }
+}
+
+/* FIXME.  */
+
+const region2 *
+region2::get_base_region () const
+{
+  const region2 *iter = this;
+  while (iter)
+    {
+      switch (iter->get_kind ())
+	{
+	case RK_FIELD:
+	case RK_ELEMENT:
+	case RK_OFFSET:
+	  iter = iter->get_parent_region ();
+	  continue;
+	case RK_CAST:
+	  iter = iter->dyn_cast_cast_region2 ()->get_original_region ();
+	  continue;
+	default:
+	  return iter;
+	}
+    }
+  return iter;
+}
+
+/* Return true if get_base_region() == this for this region.  */
+
+bool
+region2::base_region_p () const
+{
+  switch (get_kind ())
+    {
+    /* Region kinds representing a descendent of a base region.  */
+    case RK_FIELD:
+    case RK_ELEMENT:
+    case RK_OFFSET:
+    case RK_CAST:
+      return false;
+
+    default:
+      return true;
+    }
+}
+
+/* Return true if this region is ELDER or one of its descendents.  */
+// FIXME: unit test
+
+bool
+region2::descendent_of_p (const region2 *elder) const
+{
+  const region2 *iter = this;
+  while (iter)
+    {
+      if (iter == elder)
+	return true;
+      if (iter->get_kind () == RK_CAST)
+	iter = iter->dyn_cast_cast_region2 ()->get_original_region ();
+      else
+	iter = iter->get_parent_region ();
+    }
+  return false;
+}
+
+/* If this region is a frame_region2, or a descendent of one, return it.
+   Otherwise return NULL.  */
+
+const frame_region2 *
+region2::maybe_get_frame_region () const
+{
+  const region2 *iter = this;
+  while (iter)
+    {
+      if (const frame_region2 *frame_reg = iter->dyn_cast_frame_region2 ())
+	return frame_reg;
+      if (iter->get_kind () == RK_CAST)
+	iter = iter->dyn_cast_cast_region2 ()->get_original_region ();
+      else
+	iter = iter->get_parent_region ();
+    }
+  return NULL;
+}
+
+/* FIXME.  */
+
+tree
+region2::maybe_get_decl () const
+{
+  if (const decl_region2 *decl_reg = dyn_cast_decl_region2 ())
+    return decl_reg->get_decl ();
+  return NULL_TREE;
+}
+
+/* FIXME.  */
+
+region_offset
+region2::get_offset () const
+{
+  if(!m_cached_offset)
+    m_cached_offset = new region_offset (calc_offset ());
+  return *m_cached_offset;
+}
+
+/* FIXME.  */
+
+bool
+region2::get_byte_size (byte_size_t *out) const
+{
+  tree type = get_type ();
+
+  /* Bail out e.g. for heap-allocated regions.  */
+  if (!type)
+    return false;
+
+  HOST_WIDE_INT bytes = int_size_in_bytes (type);
+  if (bytes == -1)
+    {
+      /* FIXME: this will fail for VLAs
+	 TODO: how should we handle those?
+	 TODO: also, what happens for the "empty array final field" idiom?  */
+      return false;
+    }
+  *out = bytes;
+  return true;
+  /* FIXME: or maybe max_int_size_in_bytes (const_tree type)
+     FIXME: what about varying sizes??? */
+
+  /* See also bit_position (const_tree field)
+     and int_byte_position (const_tree field).  */
+  gcc_unreachable (); // TODO
+}
+
+/* FIXME.  */
+
+bool
+region2::get_bit_size (bit_size_t *out) const
+{
+  byte_size_t byte_size;
+  if (!get_byte_size (&byte_size))
+    return false;
+  *out = byte_size * BITS_PER_UNIT;
+  return true;
+}
+
+/* FIXME.  */
+/* FIXME: support the case where the bit offset is *within* one of the fields;
+   consider nested structs.  */
+
+static tree
+get_field_at_bit_offset (tree record_type, bit_offset_t bit_offset)
+{
+  gcc_assert (TREE_CODE (record_type) == RECORD_TYPE);
+  gcc_assert (bit_offset >= 0);
+
+  /* Find the first field that has an offset > BIT_OFFSET,
+     then return the one preceding it.
+     Skip other trees within the chain, such as FUNCTION_DECLs.  */
+  tree last_field = NULL_TREE;
+  for (tree iter = TYPE_FIELDS (record_type); iter != NULL_TREE;
+       iter = DECL_CHAIN (iter))
+    {
+      if (TREE_CODE (iter) == FIELD_DECL)
+	{
+	  int iter_field_offset = int_bit_position (iter);
+	  if (bit_offset < iter_field_offset)
+	    return last_field;
+	  last_field = iter;
+	}
+    }
+  return last_field;
+}
+
+/* FIXME.  */
+/* FIXME:
+   The bit offset is expressed relative to the base region in the cluster.
+   We need some kind of recursive thing that walks the types and components.
+   Or we need to subtract the offset, to get relative offset.
+   See p51 of my notebook.
+   Do we need a way to get the relative offset of a region, relative to
+   its parent?   */
+
+void
+region2::get_subregions_for_binding (region_model2_manager *mgr,
+				     bit_offset_t relative_bit_offset,
+				     bit_size_t size_in_bits,
+				     tree type,
+				     auto_vec <const region2 *> *out) const
+{
+  if (get_type () == NULL_TREE)
+    return;
+#if 1
+  if (relative_bit_offset == 0
+      && types_compatible_p (get_type (), type))
+    {
+      out->safe_push (this);
+      return;
+    }
+#endif
+  switch (TREE_CODE (get_type ()))
+    {
+    case ARRAY_TYPE:
+      {
+	tree element_type = TREE_TYPE (get_type ());
+	HOST_WIDE_INT hwi_byte_size = int_size_in_bytes (element_type);
+	if (hwi_byte_size > 0)
+	  {
+	    /* FIXME: for some reason this is segfaulting inside
+	       wi::divmod_internal.  */
+#if 0
+	    offset_int bits_per_element = hwi_byte_size << LOG2_BITS_PER_UNIT;
+	    //bit_offset_t*/ element_index = relative_bit_offset / bits_per_element;
+	    bit_offset_t element_index = relative_bit_offset.to_shwi () / bits_per_element.to_shwi ();
+	    tree element_index_cst
+	      = build_int_cst (integer_type_node, element_index.to_shwi ());
+	    bit_offset_t inner_bit_offset
+	      = relative_bit_offset % bits_per_element;
+	    // FIXME: type ^^^^
+#else
+	    HOST_WIDE_INT bits_per_element = hwi_byte_size << LOG2_BITS_PER_UNIT;
+	    //bit_offset_t*/ element_index = relative_bit_offset / bits_per_element;
+	    HOST_WIDE_INT element_index = (relative_bit_offset.to_shwi () / bits_per_element);
+	    tree element_index_cst
+	      = build_int_cst (integer_type_node, element_index);
+	    HOST_WIDE_INT inner_bit_offset
+	      = relative_bit_offset.to_shwi () % bits_per_element;
+	    // FIXME: type ^^^^
+#endif
+	    const region2 *subregion = mgr->get_element_region
+	      (this, element_type,
+	       mgr->get_or_create_constant_svalue2 (element_index_cst));
+	    subregion->get_subregions_for_binding (mgr, inner_bit_offset,
+						   size_in_bits, type, out);
+	  }
+      }
+      break;
+    case RECORD_TYPE:
+      {
+	/* The bit offset might be *within* one of the fields (such as
+	   with nested structs).
+	   So we want to find the enclosing field, adjust the offset,
+	   and repeat.  */
+	// TODO: maybe convert from recursion to iteration?
+	if (tree field = get_field_at_bit_offset (get_type (),
+						  relative_bit_offset))
+	  {
+	    int field_bit_offset = int_bit_position (field);
+	    const region2 *subregion = mgr->get_field_region (this, field);
+	    subregion->get_subregions_for_binding
+	      (mgr, relative_bit_offset - field_bit_offset,
+	       size_in_bits, type, out);
+	  }
+      }
+      break;
+    case UNION_TYPE:
+      {
+	for (tree field = TYPE_FIELDS (get_type ()); field != NULL_TREE;
+	     field = DECL_CHAIN (field))
+	  {
+	    const region2 *subregion = mgr->get_field_region (this, field);
+	    subregion->get_subregions_for_binding (mgr,
+						   relative_bit_offset,
+						   size_in_bits,
+						   type,
+						   out);
+	  }
+      }
+      break;
+    default:
+      /* Do nothing FIXME.  */
+      break;
+    }
+}
+
+/* FIXME.  */
+
+region_offset
+region2::calc_offset () const
+{
+  const region2 *iter_region = this;
+  bit_offset_t accum_bit_offset = 0;
+
+  while (iter_region)
+    {
+      switch (iter_region->get_kind ())
+	{
+	case RK_FIELD:
+	  {
+	    const field_region2 *field_region
+	      = (const field_region2 *)iter_region;
+	    iter_region = iter_region->get_parent_region ();
+
+	    /* Compare with e.g. gimple-fold.c's
+	       fold_nonarray_ctor_reference.  */
+	    tree field = field_region->get_field ();
+	    tree byte_offset = DECL_FIELD_OFFSET (field);
+	    tree field_offset = DECL_FIELD_BIT_OFFSET (field);
+	    /* Compute bit offset of the field.  */
+	    offset_int bitoffset
+	      = (wi::to_offset (field_offset)
+		 + (wi::to_offset (byte_offset) << LOG2_BITS_PER_UNIT));
+	    accum_bit_offset += bitoffset;
+	  }
+	  continue;
+
+	case RK_ELEMENT:
+	  {
+	    const element_region2 *element_region
+	      = (const element_region2 *)iter_region;
+	    iter_region = iter_region->get_parent_region ();
+
+	    if (tree idx_cst
+		  = element_region->get_index ()->maybe_get_constant ())
+	      {
+		gcc_assert (TREE_CODE (idx_cst) == INTEGER_CST);
+
+		tree elem_type = element_region->get_type ();
+
+		// FIXME:
+#if 0
+		/* Arithmetic on void-pointers is a GNU C extension, treating
+		   the size of a void as 1.
+		   https://gcc.gnu.org/onlinedocs/gcc/Pointer-Arith.html  */
+		if (TREE_CODE (elem_type) == VOID_TYPE)
+		  return offset_sid;
+#endif
+
+		offset_int element_idx = wi::to_offset (idx_cst);
+
+		/* First, use int_size_in_bytes, to reject the case where we
+		   have an incomplete type, or a non-constant value.  */
+		HOST_WIDE_INT hwi_byte_size = int_size_in_bytes (elem_type);
+		if (hwi_byte_size > 0)
+		  {
+		    offset_int element_bit_size
+		      = hwi_byte_size << LOG2_BITS_PER_UNIT;
+		    offset_int element_bit_offset
+		      = element_idx * element_bit_size;
+		    accum_bit_offset += element_bit_offset;
+		    continue;
+		  }
+	      }
+	    return region_offset::make_symbolic (iter_region);
+	  }
+	  continue;
+
+	case RK_OFFSET:
+	  {
+	    const offset_region2 *offset_region
+	      = (const offset_region2 *)iter_region;
+	    iter_region = iter_region->get_parent_region ();
+
+	    if (tree byte_offset_cst
+		  = offset_region->get_byte_offset ()->maybe_get_constant ())
+	      {
+		gcc_assert (TREE_CODE (byte_offset_cst) == INTEGER_CST);
+		/* Use a signed value for the byte offset, to handle
+		   negative offsets.  */
+		HOST_WIDE_INT byte_offset
+		  = wi::to_offset (byte_offset_cst).to_shwi ();
+		HOST_WIDE_INT bit_offset = byte_offset * BITS_PER_UNIT;
+		accum_bit_offset += bit_offset;
+	      }
+	    else
+	      return region_offset::make_symbolic (iter_region);
+	  }
+	  continue;
+
+#if 0
+	case RK_SYMBOLIC:
+	  {
+	    /* Get the offset of a region identified symbolically from a
+	       pointer svalue.  */
+	    const symbolic_region2 *symbolic_region
+	      = (const symbolic_region2 *)iter_region;
+	    const svalue2 *sval = symbolic_region->get_pointer ();
+	    switch (sval->get_kind ())
+	      {
+	      default:
+		gcc_unreachable ();
+	      case svalue2::SK_BINOP:
+		{
+		  const binop_svalue2 *binop_sval
+		    = as_a <const binop_svalue2 *> (sval);
+		  switch (binop_sval->get_op ())
+		    {
+		    default:
+		      gcc_unreachable ();
+		    case POINTER_PLUS_EXPR:
+		      {
+			// FIXME:
+			/* If we have a symbolic value expressing pointer arithmentic,
+			   try to convert it to a suitable region.  */
+			/* FIXME: but should it be an element_region2?  that expresses
+			   things via element indices, rather than via byte offsets.  */
+			/* Perhaps instead we should capture this as an offset relative
+			   to a base region in region_offset?  */
+			const svalue2 *ptr = binop_sval->get_arg0 ();
+			const svalue2 *offset = binop_sval->get_arg1 ();
+			gcc_unreachable ();
+		      }
+		    }
+		}
+	      }
+	    gcc_unreachable ();
+	  }
+	  break;
+#endif
+
+	case RK_CAST:
+	  {
+	    const cast_region2 *cast_reg
+	      = as_a <const cast_region2 *> (iter_region);
+	    iter_region = cast_reg->get_original_region ();
+	  }
+	  continue;
+
+	default:
+	  return region_offset::make_concrete (iter_region, accum_bit_offset);
+	}
+    }
+  return region_offset::make_concrete (iter_region, accum_bit_offset);
+}
+
+/* Set this region2's value to RHS_SVAL (or potentially a variant of it,
+   for some kinds of casts).  */
+#if 0
+void
+region2::set_value (region_model2 &model, region2 *lhs_reg, const svalue2 *rhs_sval,
+		    region_model2_context *ctxt)
+{
+  gcc_unreachable ();
+  /* Handle some kinds of casting.  */
+  if (m_type)
+    {
+      const svalue2 *sval = model.get_svalue2 (rhs_sval);
+      if (sval->get_type ())
+	rhs_sval = model.maybe_cast (m_type, rhs_sval, ctxt);
+
+      sval = model.get_svalue2 (rhs_sval);
+      if (sval->get_type ())
+	gcc_assert (m_type == sval->get_type ());
+    }
+
+  m_sval_id = rhs_sval;
+
+  /* Update views.
+     If this is a view, it becomes its parent's active view.
+     If there was already an active views, invalidate its value; otherwise
+     if the parent itself had a value, invalidate it.
+     If it's not a view, then deactivate any view that is active on this
+     region2.  */
+  {
+    if (m_is_view)
+      become_active_view (model, this_reg);
+    else
+      {
+	deactivate_any_active_view (model);
+	gcc_assert (m_active_view_reg.null_p ());
+      }
+  }
+}
+#endif
+
+
+#if 0
+/* Make this region2 (with id THIS_REG) the "active" view of its parent.
+   Any other active view has its value set to "unknown" and descendent values
+   cleared.
+   If there wasn't an active view, then set the parent's value to unknown, and
+   clear its descendent values (apart from this view).  */
+void
+region2::become_active_view (region_model2 &model, region2_id this_reg)
+{
+  gcc_assert (m_is_view);
+
+  region2 *parent_reg = model.get_region2 (m_parent_reg);
+  gcc_assert (parent_reg);
+
+  region2_id old_active_view_reg = parent_reg->m_active_view_reg;
+
+  if (old_active_view_reg == this_reg)
+    {
+      /* Already the active view: do nothing.  */
+      return;
+    }
+
+  /* We have a change of active view.  */
+  parent_reg->m_active_view_reg = this_reg;
+
+  if (old_active_view_reg.null_p ())
+    {
+      /* No previous active view, but the parent and its other children
+	 might have values.
+	 If so, invalidate those values - but not that of the new view.  */
+      region2_id_set below_region2 (&model);
+      model.get_descendents (m_parent_reg, &below_region2, this_reg);
+      for (unsigned i = 0; i < model.get_num_region2s (); i++)
+	{
+	  region2_id rid (region2_id::from_int (i));
+	  if (below_region2.region2_p (rid))
+	    {
+	      region2 *other_reg = model.get_region2 (rid);
+	      other_reg->m_sval_id = svalue2_id::null ();
+	    }
+	}
+      region2 *parent = model.get_region2 (m_parent_reg);
+      parent->m_sval_id
+	= model.add_svalue2 (new unknown_svalue2 (parent->get_type ()));
+    }
+  else
+    {
+      /* If there was an active view, invalidate it.  */
+      region2 *old_active_view = model.get_region2 (old_active_view_reg);
+      old_active_view->deactivate_view (model, old_active_view_reg);
+    }
+}
+
+/* If this region2 (with id THIS_REG) has an active view, deactivate it,
+   clearing m_active_view_reg.  */
+
+void
+region2::deactivate_any_active_view (region_model2 &model)
+{
+  if (m_active_view_reg.null_p ())
+    return;
+  region2 *view = model.get_region2 (m_active_view_reg);
+  view->deactivate_view (model, m_active_view_reg);
+  m_active_view_reg = region2_id::null ();
+}
+
+/* Clear any values for region2s below THIS_REG.
+   Set the view's value to unknown.  */
+
+void
+region2::deactivate_view (region_model2 &model, region2_id this_view_reg)
+{
+  gcc_assert (is_view_p ());
+
+  /* Purge values from old_active_this_view_reg and all its
+     descendents.  Potentially we could use a poison value
+     for this, but let's use unknown for now.  */
+  region2_id_set below_view (&model);
+  model.get_descendents (this_view_reg, &below_view, region2_id::null ());
+
+  for (unsigned i = 0; i < model.get_num_region2s (); i++)
+    {
+      region2_id rid (region2_id::from_int (i));
+      if (below_view.region2_p (rid))
+	{
+	  region2 *other_reg = model.get_region2 (rid);
+	  other_reg->m_sval_id = svalue2_id::null ();
+	}
+    }
+
+  m_sval_id = model.add_svalue2 (new unknown_svalue2 (get_type ()));
+}
+#endif
+
+/* Get a value for this region2, either its value if it has one,
+   or, failing that, "inherit" a value from first ancestor with a
+   non-null value.
+
+   For example, when getting the value for a local variable within
+   a stack frame that doesn't have one, the frame doesn't have a value
+   either, but the stack as a whole will have an "uninitialized" poison
+   value, so inherit that.  */
+#if 0
+svalue2_id
+region2::get_value (region_model2 &model, bool non_null,
+		   region_model2_context *ctxt)
+{
+  /* If this region2 has a value, use it. */
+  if (!m_sval_id.null_p ())
+    return m_sval_id;
+
+  /* Otherwise, "inherit" value from first ancestor with a
+     non-null value. */
+
+  region2 *parent = model.get_region2 (m_parent_reg);
+  if (parent)
+    {
+      svalue2_id inherited_sval
+	= parent->get_inherited_child_sval (this, model, ctxt);
+      if (!inherited_sval.null_p ())
+	return inherited_sval;
+    }
+
+  /* If a non-null value has been requested, then generate
+     a new unknown value.  Store it, so that repeated reads from this
+     region2 will yield the same unknown value.  */
+  if (non_null)
+    {
+      svalue2_id unknown_sval = model.add_svalue2 (new unknown_svalue2 (m_type));
+      m_sval_id = unknown_sval;
+      return unknown_sval;
+    }
+
+  return svalue2_id::null ();
+}
+
+/* Get a value for CHILD, inheriting from this region2.
+
+   Recurse, so this region2 will inherit a value if it doesn't already
+   have one.  */
+
+svalue2_id
+region2::get_inherited_child_sval (region2 *child,
+				 region_model2 &model,
+				 region_model2_context *ctxt)
+{
+  if (m_sval_id.null_p ())
+    {
+      /* Recurse.  */
+      if (!m_parent_reg.null_p ())
+	{
+	  region2 *parent = model.get_region2 (m_parent_reg);
+	  m_sval_id = parent->get_inherited_child_sval (this, model, ctxt);
+	}
+    }
+
+  if (!m_sval_id.null_p ())
+    {
+      /* Clone the parent's value, so that attempts to update it
+	 (e.g giving a specific value to an inherited "uninitialized"
+	 value) touch the child, and not the parent.  */
+      const svalue2 *this_value = model.get_svalue2 (m_sval_id);
+      svalue2_id new_child_sval
+	= this_value->get_child_sval (this, child, model, ctxt);
+      if (ctxt)
+	ctxt->on_inherited_svalue2 (m_sval_id, new_child_sval);
+      child->m_sval_id = new_child_sval;
+      return new_child_sval;
+    }
+
+  return svalue2_id::null ();
+}
+#endif
+
+// FIXME:
+/* Copy from SRC_REG to DST_REG, using CTXT for any issues that occur.
+   Copy across any value for the region2, and handle structs, unions
+   and arrays recursively.  */
+
+void
+region_model2::copy_region (const region2 *dst_reg, const region2 *src_reg,
+			    region_model2_context *ctxt)
+{
+  gcc_assert (dst_reg);
+  gcc_assert (src_reg);
+  if (dst_reg == src_reg)
+    return;
+
+  const svalue2 *sval = get_store_value (src_reg);
+  set_value (dst_reg, sval, ctxt);
+
+#if 0
+  /* Copy across any value for the src region2 itself.  */
+  const svalue2 *sval = src_reg->get_value (*this, true, ctxt);
+  set_value (dst_reg, sid, ctxt);
+
+  if (dst_reg->get_kind () != src_reg->get_kind ())
+    return;
+
+  /* Copy across child region2s for structs, unions, and arrays.  */
+  // FIXME
+  switch (dst_reg->get_kind ())
+    {
+    case RK_FRAME:
+    case RK_GLOBALS:
+    case RK_CODE:
+    case RK_FUNCTION:
+      return;
+    case RK_STACK:
+    case RK_HEAP:
+    case RK_ROOT:
+    case RK_SYMBOLIC:
+      return;
+    }
+#endif
+}
+
+/* Generate a hash value for this region2.  The work is done by the
+   add_to_hash vfunc.  */
+
+hashval_t
+region2::hash () const
+{
+  inchash::hash hstate;
+  add_to_hash (hstate);
+  return hstate.end ();
+}
+
+/* Print a one-liner representation of this region2 to PP, assuming
+   that this region2 is within MODEL and its id is THIS_REG.  */
+#if 0
+void
+region2::print (const region_model2 &model,
+	       region2_id this_reg,
+	       pretty_printer *pp) const
+{
+  this_reg.print (pp);
+  pp_string (pp, ": {");
+
+  /* vfunc.  */
+  print_fields (model, this_reg, pp);
+
+  pp_string (pp, "}");
+}
+#endif
+
+/* Base class implementation of region2::dump_dot_to_pp vfunc.  */
+#if 0
+void
+region2::dump_dot_to_pp (const region_model2 &model,
+			region2_id this_reg,
+			pretty_printer *pp) const
+{
+  this_reg.dump_node_name_to_pp (pp);
+  pp_printf (pp, " [shape=none,margin=0,style=filled,fillcolor=%s,label=\"",
+	     "lightgrey");
+  pp_write_text_to_stream (pp);
+  print (model, this_reg, pp);
+  pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/false);
+  pp_string (pp, "\"];");
+  pp_newline (pp);
+
+  /* Add edge to svalue2.  */
+  if (!m_sval_id.null_p ())
+    {
+      this_reg.dump_node_name_to_pp (pp);
+      pp_string (pp, " -> ");
+      m_sval_id.dump_node_name_to_pp (pp);
+      pp_string (pp, ";");
+      pp_newline (pp);
+    }
+
+  /* Add edge to parent.  */
+  if (!m_parent_reg.null_p ())
+    {
+      this_reg.dump_node_name_to_pp (pp);
+      pp_string (pp, " -> ");
+      m_parent_reg.dump_node_name_to_pp (pp);
+      pp_string (pp, ";");
+      pp_newline (pp);
+    }
+}
+#endif
+
+/* Dump a tree-like ASCII-art representation of this region2 to PP.  */
+#if 0
+void
+region2::dump_to_pp (const region_model2 &model,
+		    region2_id this_reg,
+		    pretty_printer *pp,
+		    const char *prefix,
+		    bool is_last_child) const
+{
+  print (model, this_reg, pp);
+  pp_newline (pp);
+
+  const char *new_prefix;
+  if (!m_parent_reg.null_p ())
+    new_prefix = ACONCAT ((prefix, is_last_child ? "  " : "|  ", NULL));
+  else
+    new_prefix = prefix;
+
+  const char *begin_color = colorize_start (pp_show_color (pp), "note");
+  const char *end_color = colorize_stop (pp_show_color (pp));
+  char *field_prefix
+    = ACONCAT ((begin_color, new_prefix, "|:", end_color, NULL));
+
+  if (!m_sval_id.null_p ())
+    {
+      pp_printf (pp, "%s sval: ", field_prefix);
+      model.get_svalue2 (m_sval_id)->print (model, m_sval_id, pp);
+      pp_newline (pp);
+    }
+  if (m_type)
+    {
+      pp_printf (pp, "%s type: ", field_prefix);
+      print_quoted_type (pp, m_type);
+      pp_newline (pp);
+    }
+
+  /* Find the children.  */
+
+  auto_vec<region2_id> child_regs;
+  unsigned i;
+  for (unsigned i = 0; i < model.get_num_region2s (); ++i)
+    {
+      region2_id rid = region2_id::from_int (i);
+      region2 *child = model.get_region2 (rid);
+      if (child->m_parent_reg == this_reg)
+	child_regs.safe_push (rid);
+    }
+
+  /* Print the children, using dump_child_label to label them.  */
+
+  region2_id *child_reg;
+  FOR_EACH_VEC_ELT (child_regs, i, child_reg)
+    {
+      is_last_child = (i == child_regs.length () - 1);
+      if (!this_reg.null_p ())
+	{
+	  const char *tail = is_last_child ? "`-" : "|-";
+	  pp_printf (pp, "%r%s%s%R", "note", new_prefix, tail);
+	}
+      dump_child_label (model, this_reg, *child_reg, pp);
+      model.get_region2 (*child_reg)->dump_to_pp (model, *child_reg, pp,
+						 new_prefix,
+						 is_last_child);
+    }
+}
+#endif
+
+DEBUG_FUNCTION void
+region2::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);
+  pp_newline (&pp);
+  pp_flush (&pp);
+}
+
+/* FIXME.  */
+
+DEBUG_FUNCTION label_text
+region2::get_desc (bool simple) const
+{
+  pretty_printer pp;
+  pp_format_decoder (&pp) = default_tree_printer;
+  dump_to_pp (&pp, simple);
+  return label_text::take (xstrdup (pp_formatted_text (&pp)));
+}
+
+/* FIXME.  */
+
+void
+region2::accept (visitor *v) const
+{
+  v->visit_region (this);
+  if (m_parent)
+    m_parent->accept (v);
+}
+
+/* Base implementation of region2::validate vfunc.
+   Assert that the fields of "region2" are valid; subclasses should
+   chain up their implementation to this one.  */
+#if 0
+void
+region2::validate (const region_model2 &model) const
+{
+  m_parent_reg.validate (model);
+  m_sval_id.validate (model);
+  unsigned i;
+  region2_id *view_reg;
+  FOR_EACH_VEC_ELT (m_view_regs, i, view_reg)
+    {
+      gcc_assert (!view_reg->null_p ());
+      view_reg->validate (model);
+    }
+  m_active_view_reg.validate (model);
+}
+#endif
+
+#if 0
+/* Add a new region2 with id VIEW_REG as a view of this region2.  */
+
+void
+region2::add_view (region2_id view_reg, region_model2 *model)
+{
+  gcc_assert (!view_reg.null_p ());
+  region2 *new_view = model->get_region2 (view_reg);
+  new_view->m_is_view = true;
+  gcc_assert (!new_view->m_parent_reg.null_p ());
+  gcc_assert (new_view->m_sval_id.null_p ());
+
+  //gcc_assert (new_view->get_type () != NULL_TREE);
+  // TODO: this can sometimes be NULL, when viewing through a (void *)
+
+  // TODO: the type ought to not be present yet
+
+  m_view_regs.safe_push (view_reg);
+}
+
+/* Look for a view of type TYPE of this region2, returning its id if found,
+   or null otherwise.  */
+
+region2_id
+region2::get_view (tree type, region_model2 *model) const
+{
+  unsigned i;
+  region2_id *view_reg;
+  FOR_EACH_VEC_ELT (m_view_regs, i, view_reg)
+    {
+      region2 *view = model->get_region2 (*view_reg);
+      gcc_assert (view->m_is_view);
+      if (view->get_type () == type)
+	return *view_reg;
+    }
+  return region2_id::null ();
+}
+#endif
+
+/* Return true if this is a symbolic region for deferencing an
+   unknown ptr.
+   We shouldn't attempt to bind values for this region (but
+   can unbind values for other regions).  */
+
+bool
+region2::symbolic_for_unknown_ptr_p () const
+{
+  if (const symbolic_region2 *sym_reg = dyn_cast_symbolic_region2 ())
+    if (sym_reg->get_pointer ()->get_kind () == svalue2::SK_UNKNOWN)
+      return true;
+  return false;
+}
+
+/* region2's ctor.  */
+
+region2::region2 (complexity c, unsigned id, const region2 *parent, tree type)
+: m_complexity (c), m_id (id), m_parent (parent), m_type (type),
+  m_cached_offset (NULL)
+{
+  gcc_assert (type == NULL_TREE || TYPE_P (type));
+}
+
+/* Base implementation of region2::add_to_hash vfunc; subclasses should
+   chain up to this.  */
+
+void
+region2::add_to_hash (inchash::hash &hstate) const
+{
+  /* We deliberatly omit the id, to avoid different IDs preventing
+     consolidation.  */
+  hstate.add_ptr (m_parent);
+  hstate.add_ptr (m_type);
+}
+
+/* FIXME.  */
+
+int
+region2::cmp_ptrs (const void *p1, const void *p2)
+{
+  const region2 * const *reg1 = (const region2 * const *)p1;
+  const region2 * const *reg2 = (const region2 * const *)p2;
+
+  return cmp_ids (*reg1, *reg2);
+}
+
+/* Determine if a pointer to this region2 must be non-NULL.
+
+   Generally, pointers to region2s must be non-NULL, but pointers
+   to symbolic_region2s might, in fact, be NULL.
+
+   This allows us to simulate functions like malloc and calloc with:
+   - only one "outcome" from each statement,
+   - the idea that the pointer is on the heap if non-NULL
+   - the possibility that the pointer could be NULL
+   - the idea that successive values returned from malloc are non-equal
+   - to be able to zero-fill for calloc.  */
+
+bool
+region2::non_null_p (const region_model2 &model) const
+{
+#if 1
+  /* FIXME: malloc is now simulated via a heap_allocated_region2.
+     That said, maybe we do want the possibilty of NULL for symbolic
+     regions too.  */
+  /* Are we within a symbolic_region2?  If so, it could be NULL, and we
+     have to fall back on the constraints.  */
+  if (const symbolic_region2 *sym_reg = dyn_cast_symbolic_region2 ())
+    {
+      return false; // TODO: really?
+    }
+  if (get_kind () == RK_HEAP_ALLOCATED)
+    return false;
+#endif
+
+  return true;
+}
+
+/* Comparator for trees to impose a deterministic ordering on
+   T1 and T2.  */
+
+static int
+tree_cmp (const_tree t1, const_tree t2)
+{
+  gcc_assert (t1);
+  gcc_assert (t2);
+
+  /* Test tree codes first.  */
+  if (TREE_CODE (t1) != TREE_CODE (t2))
+    return TREE_CODE (t1) - TREE_CODE (t2);
+
+  /* From this point on, we know T1 and T2 have the same tree code.  */
+
+  if (DECL_P (t1))
+    {
+      if (DECL_NAME (t1) && DECL_NAME (t2))
+	return strcmp (IDENTIFIER_POINTER (DECL_NAME (t1)),
+		       IDENTIFIER_POINTER (DECL_NAME (t2)));
+      else
+	{
+	  if (DECL_NAME (t1))
+	    return -1;
+	  else if (DECL_NAME (t2))
+	    return 1;
+	  else
+	    return DECL_UID (t1) - DECL_UID (t2);
+	}
+    }
+
+  switch (TREE_CODE (t1))
+    {
+    case SSA_NAME:
+      {
+	if (SSA_NAME_VAR (t1) && SSA_NAME_VAR (t2))
+	  {
+	    int var_cmp = tree_cmp (SSA_NAME_VAR (t1), SSA_NAME_VAR (t2));
+	    if (var_cmp)
+	      return var_cmp;
+	    return SSA_NAME_VERSION (t1) - SSA_NAME_VERSION (t2);
+	  }
+	else
+	  {
+	    if (SSA_NAME_VAR (t1))
+	      return -1;
+	    else if (SSA_NAME_VAR (t2))
+	      return 1;
+	    else
+	      return SSA_NAME_VERSION (t1) - SSA_NAME_VERSION (t2);
+	  }
+      }
+      break;
+
+    case INTEGER_CST:
+      return tree_int_cst_compare (t1, t2);
+
+    case REAL_CST:
+      {
+	const real_value *rv1 = TREE_REAL_CST_PTR (t1);
+	const real_value *rv2 = TREE_REAL_CST_PTR (t2);
+	if (real_compare (UNORDERED_EXPR, rv1, rv2))
+	  {
+	    /* Impose an arbitrary order on NaNs relative to other NaNs
+	       and to non-NaNs.  */
+	    if (int cmp_isnan = real_isnan (rv1) - real_isnan (rv2))
+	      return cmp_isnan;
+	    if (int cmp_issignaling_nan
+		  = real_issignaling_nan (rv1) - real_issignaling_nan (rv2))
+	      return cmp_issignaling_nan;
+	    return real_isneg (rv1) - real_isneg (rv2);
+	  }
+	if (real_compare (LT_EXPR, rv1, rv2))
+	  return -1;
+	if (real_compare (GT_EXPR, rv1, rv2))
+	  return 1;
+	return 0;
+      }
+
+    case STRING_CST:
+      return strcmp (TREE_STRING_POINTER (t1),
+		     TREE_STRING_POINTER (t2));
+
+    default:
+      gcc_unreachable ();
+      break;
+    }
+
+  gcc_unreachable ();
+
+  return 0;
+}
+
+/* qsort comparator for trees to impose a deterministic ordering on
+   P1 and P2.  */
+
+static int
+tree_cmp (const void *p1, const void *p2)
+{
+  const_tree t1 = *(const_tree const *)p1;
+  const_tree t2 = *(const_tree const *)p2;
+
+  return tree_cmp (t1, t2);
+}
+
+/* class frame_region2 : public space_region2.  */
+
+frame_region2::~frame_region2 ()
+{
+  for (map_t::iterator iter = m_locals.begin ();
+       iter != m_locals.end ();
+       ++iter)
+    delete (*iter).second;
+}
+
+void
+frame_region2::accept (visitor *v) const
+{
+  region2::accept (v);
+  if (m_calling_frame)
+    m_calling_frame->accept (v);
+}
+
+void
+frame_region2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    pp_printf (pp, "frame: %qs@%i", function_name (m_fun), get_stack_depth ());
+  else
+    pp_printf (pp, "frame_region2(%qs, index: %i, depth: %i)",
+	       function_name (m_fun), m_index, get_stack_depth ());
+}
+
+/* Compare the fields of this frame_region2 with OTHER, returning true
+   if they are equal.
+   For use by region2::operator==.  */
+
+bool
+frame_region2::compare_fields (const frame_region2 &other) const
+{
+#if 0
+  if (!map_region2::compare_fields (other))
+    return false;
+#endif
+  if (m_fun != other.m_fun)
+    return false;
+  if (m_index != other.m_index)
+    return false;
+  if (m_calling_frame != other.m_calling_frame)
+    return false;
+  /* We don't compare m_locals.  */
+  return true;
+}
+
+/* Implementation of region2::add_to_hash vfunc for frame_region2.  */
+#if 0
+void
+frame_region2::add_to_hash (inchash::hash &hstate) const
+{
+  map_region2::add_to_hash (hstate);
+  hstate.add_ptr (m_fun);
+  hstate.add_int (m_depth);
+}
+#endif
+
+const region2 *
+frame_region2::get_region_for_local (region_model2_manager *mgr,
+				     tree expr) const
+{
+  // TODO: could also check that VAR_DECLs are locals
+  gcc_assert (TREE_CODE (expr) == PARM_DECL
+	      || TREE_CODE (expr) == VAR_DECL
+	      || TREE_CODE (expr) == SSA_NAME
+	      || TREE_CODE (expr) == RESULT_DECL);
+
+  /* Ideally we'd use mutable here.  */
+  // TODO: when is it available?
+  map_t &mutable_locals = const_cast <map_t &> (m_locals);
+
+  if (region2 **slot = mutable_locals.get (expr))
+    return *slot;
+  decl_region2 *reg
+    = new decl_region2 (mgr->alloc_region_id (), this, expr);
+  mutable_locals.put (expr, reg);
+  return reg;
+}
+
+/* class globals_region2 : public space_region2.  */
+
+/* Compare the fields of this globals_region2 with OTHER, returning true
+   if they are equal.
+   For use by region2::operator==.  */
+#if 0
+bool
+globals_region2::compare_fields (const globals_region2 &other) const
+{
+  return map_region2::compare_fields (other);
+}
+#endif
+
+void
+globals_region2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    pp_string (pp, "::");
+  else
+    pp_string (pp, "globals");
+}
+
+/* class code_region2 : public map_region2.  */
+
+/* Compare the fields of this code_region2 with OTHER, returning true
+   if they are equal.
+   For use by region2::operator==.  */
+#if 0
+bool
+code_region2::compare_fields (const code_region2 &other) const
+{
+  return map_region2::compare_fields (other);
+}
+#endif
+
+/* FIXME.  */
+
+void
+code_region2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    pp_string (pp, "code region");
+  else
+    pp_string (pp, "code_region2()");
+}
+
+/* class function_region2 : public region2.  */
+
+/* Compare the fields of this function_region2 with OTHER, returning true
+   if they are equal.
+   For use by region2::operator==.  */
+#if 0
+bool
+function_region2::compare_fields (const function_region2 &other) const
+{
+  return map_region2::compare_fields (other);
+}
+#endif
+
+/* FIXME.  */
+
+void
+function_region2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    {
+      dump_quoted_tree (pp, m_fndecl);
+    }
+  else
+    {
+      pp_string (pp, "function_region2(");
+      dump_quoted_tree (pp, m_fndecl);
+      pp_string (pp, ")");
+    }
+}
+
+/* class label_region2 : public region2.  */
+
+/* FIXME.  */
+
+void
+label_region2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    {
+      dump_quoted_tree (pp, m_label);
+    }
+  else
+    {
+      pp_string (pp, "label_region2(");
+      dump_quoted_tree (pp, m_label);
+      pp_string (pp, ")");
+    }
+}
+
+/* class stack_region2 : public region2.  */
+
+void
+stack_region2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    pp_string (pp, "stack region");
+  else
+    pp_string (pp, "stack_region2()");
+}
+
+/* Compare the fields of this stack_region2 with OTHER, returning true
+   if they are equal.
+   For use by region2::operator==.  */
+#if 0
+bool
+stack_region2::compare_fields (const stack_region2 &other) const
+{
+  if (m_frame_regs.length () != other.m_frame_regs.length ())
+    return false;
+
+  int i;
+  region2_id *frame_reg;
+  FOR_EACH_VEC_ELT (m_frame_regs, i, frame_reg)
+    if (m_frame_regs[i] != other.m_frame_regs[i])
+      return false;
+
+  return true;
+}
+#endif
+
+/* Implementation of region2::add_to_hash vfunc for stack_region2.  */
+#if 0
+void
+stack_region2::add_to_hash (inchash::hash &hstate) const
+{
+  region2::add_to_hash (hstate);
+
+  int i;
+  region2_id *frame_reg;
+  FOR_EACH_VEC_ELT (m_frame_regs, i, frame_reg)
+    inchash::add (*frame_reg, hstate);
+}
+#endif
+
+/* Attempt to merge STACK_REGION2_A and STACK_REGION2_B using MERGER.
+   Return true if the merger is possible, false otherwise.  */
+#if 0
+bool
+stack_region2::can_merge_p (const stack_region2 *stack_region2_a,
+			   const stack_region2 *stack_region2_b,
+			   model_merger *merger)
+{
+  if (stack_region2_a->get_num_frames ()
+      != stack_region2_b->get_num_frames ())
+    return false;
+
+  region_model2 *merged_model = merger->m_merged_model;
+
+  region2_id rid_merged_stack
+    = merged_model->get_root_region2 ()->ensure_stack_region2 (merged_model);
+
+  stack_region2 *merged_stack
+    = merged_model->get_region2 <stack_region2> (rid_merged_stack);
+
+  /* First, create all frames in the merged model, without populating them.
+     The merging code assumes that all frames in the merged model already exist,
+     so we have to do this first to handle the case in which a local in an
+     older frame points at a local in a more recent frame.  */
+    for (unsigned i = 0; i < stack_region2_a->get_num_frames (); i++)
+      {
+	region2_id rid_a = stack_region2_a->get_frame_reg (i);
+	frame_region2 *frame_a = merger->get_region2_a <frame_region2> (rid_a);
+
+	region2_id rid_b = stack_region2_b->get_frame_reg (i);
+	frame_region2 *frame_b = merger->get_region2_b <frame_region2> (rid_b);
+
+	if (frame_a->get_function () != frame_b->get_function ())
+	  return false;
+
+	frame_region2 *merged_frame = new frame_region2 (rid_merged_stack,
+						       frame_a->get_function (),
+						       frame_a->get_depth ());
+	region2_id rid_merged_frame = merged_model->add_region2 (merged_frame);
+	merged_stack->push_frame (rid_merged_frame);
+      }
+
+    /* Now populate the frames we created.  */
+    for (unsigned i = 0; i < stack_region2_a->get_num_frames (); i++)
+      {
+	region2_id rid_a = stack_region2_a->get_frame_reg (i);
+	frame_region2 *frame_a = merger->get_region2_a <frame_region2> (rid_a);
+
+	region2_id rid_b = stack_region2_b->get_frame_reg (i);
+	frame_region2 *frame_b = merger->get_region2_b <frame_region2> (rid_b);
+
+	region2_id rid_merged_frame = merged_stack->get_frame_reg (i);
+	frame_region2 *merged_frame
+	  = merged_model->get_region2 <frame_region2> (rid_merged_frame);
+	if (!map_region2::can_merge_p (frame_a, frame_b,
+				      merged_frame, rid_merged_frame,
+				      merger))
+	  return false;
+      }
+
+  return true;
+}
+#endif
+
+/* For debugging purposes: look for a grandchild region2 within one of
+   the child frame region2s, where the grandchild is for a decl named
+   IDENTIFIER (or an SSA_NAME for such a decl):
+
+     stack_region2
+     `-frame_region2
+       `-region2 for decl named IDENTIFIER
+
+   returning its value, or svalue2_id::null if none are found.  */
+#if 0
+svalue2_id
+stack_region2::get_value_by_name (tree identifier,
+				 const region_model2 &model) const
+{
+  int i;
+  region2_id *frame_reg;
+  FOR_EACH_VEC_ELT (m_frame_regs, i, frame_reg)
+    {
+      frame_region2 *frame = model.get_region2<frame_region2> (*frame_reg);
+      const svalue2 *sval = frame->get_value_by_name (identifier, model);
+      if (!sid.null_p ())
+	return sid;
+    }
+
+  return svalue2_id::null ();
+}
+#endif
+
+/* class heap_region2 : public region2.  */
+
+/* Compare the fields of this heap_region2 with OTHER, returning true
+   if they are equal.
+   For use by region2::operator==.  */
+#if 0
+bool
+heap_region2::compare_fields (const heap_region2 &) const
+{
+  /* Empty.  */
+  return true;
+}
+#endif
+
+void
+heap_region2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    pp_string (pp, "heap region");
+  else
+    pp_string (pp, "heap_region2()");
+}
+
+/* class root_region2 : public region2.  */
+
+/* root_region2's default ctor.  */
+
+root_region2::root_region2 (unsigned id)
+: region2 (complexity (1, 1), id, NULL, NULL_TREE)
+{
+}
+
+#if 0
+/* Compare the fields of this root_region2 with OTHER, returning true
+   if they are equal.
+   For use by region2::operator==.  */
+
+bool
+root_region2::compare_fields (const root_region2 &other) const
+{
+  if (m_stack_reg != other.m_stack_reg)
+    return false;
+  if (m_globals_reg != other.m_globals_reg)
+    return false;
+  if (m_code_reg != other.m_code_reg)
+    return false;
+  if (m_heap_reg != other.m_heap_reg)
+    return false;
+  return true;
+}
+#endif
+
+void
+root_region2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    pp_string (pp, "root region");
+  else
+    pp_string (pp, "root_region2()");
+}
+
+/* Create a new frame_region2 for a call to FUN and push it onto
+   the stack.
+
+   If ARG_SVALS is non-NULL, use it to populate the parameters
+   in the new frame.
+   Otherwise, populate them with unknown values.
+
+   Return the region2_id of the new frame.  */
+#if 0
+region2_id
+root_region2::push_frame (region_model2 *model, function *fun,
+			 vec<svalue2_id> *arg_svals,
+			 region_model2_context *ctxt)
+{
+  gcc_assert (fun);
+  /* arg_svals can be NULL.  */
+
+  ensure_stack_region2 (model);
+  stack_region2 *stack = model->get_region2 <stack_region2> (m_stack_reg);
+
+  frame_region2 *region2 = new frame_region2 (m_stack_reg, fun,
+					   stack->get_num_frames ());
+  region2_id frame_reg = model->add_region2 (region2);
+
+  // TODO: unify these cases by building a vec of unknown?
+
+  if (arg_svals)
+    {
+      /* Arguments supplied from a caller frame.  */
+
+      tree fndecl = fun->decl;
+      unsigned idx = 0;
+      for (tree iter_parm = DECL_ARGUMENTS (fndecl); iter_parm;
+	   iter_parm = DECL_CHAIN (iter_parm), ++idx)
+	{
+	  /* If there's a mismatching declaration, the call stmt might
+	     not have enough args.  Handle this case by leaving the
+	     rest of the params as uninitialized.  */
+	  if (idx >= arg_svals->length ())
+	    break;
+	  svalue2_id arg_sval = (*arg_svals)[idx];
+	  region2_id parm_reg
+	    = region2->get_or_create (model, frame_reg, iter_parm,
+				     TREE_TYPE (iter_parm), ctxt);
+	  model->set_value (parm_reg, arg_sval, ctxt);
+
+	  /* Also do it for default SSA name (sharing the same unknown
+	     value).  */
+	  tree parm_default_ssa = ssa_default_def (fun, iter_parm);
+	  if (parm_default_ssa)
+	    {
+	      region2_id defssa_reg
+		= region2->get_or_create (model, frame_reg, parm_default_ssa,
+					 TREE_TYPE (iter_parm), ctxt);
+	      model->set_value (defssa_reg, arg_sval, ctxt);
+	    }
+	}
+    }
+  else
+    {
+      /* No known arguments (a top-level call within the analysis).  */
+
+      /* Params have a defined, unknown value; they should not inherit
+	 from the poisoned uninit value.  */
+      tree fndecl = fun->decl;
+      for (tree iter_parm = DECL_ARGUMENTS (fndecl); iter_parm;
+	   iter_parm = DECL_CHAIN (iter_parm))
+	{
+	  region2_id parm_reg
+	    = region2->get_or_create (model, frame_reg, iter_parm,
+				     TREE_TYPE (iter_parm), ctxt);
+	  svalue2_id parm_sval
+	    = model->set_to_new_unknown_value (parm_reg, TREE_TYPE (iter_parm),
+					       ctxt);
+
+	  /* Also do it for default SSA name (sharing the same unknown
+	     value).  */
+	  tree parm_default_ssa = ssa_default_def (fun, iter_parm);
+	  if (parm_default_ssa)
+	    {
+	      region2_id defssa_reg
+		= region2->get_or_create (model, frame_reg, parm_default_ssa,
+					 TREE_TYPE (iter_parm), ctxt);
+	      model->get_region2 (defssa_reg)->set_value (*model, defssa_reg,
+							 parm_sval, ctxt);
+	    }
+	}
+    }
+
+  stack->push_frame (frame_reg);
+
+  return frame_reg;
+}
+
+/* Return the region2_id of the stack region2, creating it if doesn't
+   already exist.  */
+
+region2_id
+root_region2::ensure_stack_region2 (region_model2 *model)
+{
+  if (m_stack_reg.null_p ())
+    {
+      svalue2_id uninit_sval
+	= model->add_svalue2 (new poisoned_svalue2 (POISON_KIND_UNINIT,
+						  NULL_TREE));
+      m_stack_reg
+	= model->add_region2 (new stack_region2 (model->get_root_reg (),
+					       uninit_sval));
+    }
+  return m_stack_reg;
+}
+
+/* Return the region2_id of the heap region2, creating it if doesn't
+   already exist.  */
+
+region2_id
+root_region2::ensure_heap_region2 (region_model2 *model)
+{
+  if (m_heap_reg.null_p ())
+    {
+      svalue2_id uninit_sval
+	= model->add_svalue2 (new poisoned_svalue2 (POISON_KIND_UNINIT,
+						  NULL_TREE));
+      m_heap_reg
+	= model->add_region2 (new heap_region2 (model->get_root_reg (),
+					      uninit_sval));
+    }
+  return m_heap_reg;
+}
+
+/* Attempt to merge ROOT_REGION2_A and ROOT_REGION2_B into
+   MERGED_ROOT_REGION2 using MERGER.
+   Return true if the merger is possible, false otherwise.  */
+
+bool
+root_region2::can_merge_p (const root_region2 *root_region2_a,
+			  const root_region2 *root_region2_b,
+			  root_region2 *merged_root_region2,
+			  model_merger *merger)
+{
+  /* We can only merge if the stacks are sufficiently similar.  */
+  stack_region2 *stack_a = root_region2_a->get_stack_region2 (merger->m_model_a);
+  stack_region2 *stack_b = root_region2_b->get_stack_region2 (merger->m_model_b);
+  if (stack_a && stack_b)
+    {
+      /* If the two models both have a stack, attempt to merge them.  */
+      merged_root_region2->ensure_stack_region2 (merger->m_merged_model);
+      if (!stack_region2::can_merge_p (stack_a, stack_b, merger))
+	return false;
+    }
+  else if (stack_a || stack_b)
+    /* Don't attempt to merge if one model has a stack and the other
+       doesn't.  */
+    return false;
+
+  map_region2 *globals_a = root_region2_a->get_globals_region2 (merger->m_model_a);
+  map_region2 *globals_b = root_region2_b->get_globals_region2 (merger->m_model_b);
+  if (globals_a && globals_b)
+    {
+      /* If both models have globals region2s, attempt to merge them.  */
+      region2_id merged_globals_reg
+	= merged_root_region2->ensure_globals_region2 (merger->m_merged_model);
+      map_region2 *merged_globals
+	= merged_root_region2->get_globals_region2 (merger->m_merged_model);
+      if (!map_region2::can_merge_p (globals_a, globals_b,
+				    merged_globals, merged_globals_reg,
+				    merger))
+	return false;
+    }
+  /* otherwise, merge as "no globals".  */
+
+  map_region2 *code_a = root_region2_a->get_code_region2 (merger->m_model_a);
+  map_region2 *code_b = root_region2_b->get_code_region2 (merger->m_model_b);
+  if (code_a && code_b)
+    {
+      /* If both models have code region2s, attempt to merge them.  */
+      region2_id merged_code_reg
+	= merged_root_region2->ensure_code_region2 (merger->m_merged_model);
+      map_region2 *merged_code
+	= merged_root_region2->get_code_region2 (merger->m_merged_model);
+      if (!map_region2::can_merge_p (code_a, code_b,
+				    merged_code, merged_code_reg,
+				    merger))
+	return false;
+    }
+  /* otherwise, merge as "no code".  */
+
+  heap_region2 *heap_a = root_region2_a->get_heap_region2 (merger->m_model_a);
+  heap_region2 *heap_b = root_region2_b->get_heap_region2 (merger->m_model_b);
+  if (heap_a && heap_b)
+    {
+      /* If both have a heap, create a "merged" heap.
+	 Actually merging the heap contents happens via the region_svalue2
+	 instances, as needed, when seeing pairs of region_svalue2 instances.  */
+      merged_root_region2->ensure_heap_region2 (merger->m_merged_model);
+    }
+  /* otherwise, merge as "no heap".  */
+
+  return true;
+}
+#endif
+
+/* Implementation of region2::add_to_hash vfunc for root_region2.  */
+#if 0
+void
+root_region2::add_to_hash (inchash::hash &hstate) const
+{
+  region2::add_to_hash (hstate);
+  inchash::add (m_stack_reg, hstate);
+  inchash::add (m_globals_reg, hstate);
+  inchash::add (m_code_reg, hstate);
+  inchash::add (m_heap_reg, hstate);
+}
+#endif
+
+/* For debugging purposes: look for a descendant region2 for a local
+   or global decl named IDENTIFIER (or an SSA_NAME for such a decl),
+   returning its value, or svalue2_id::null if none are found.  */
+#if 0
+svalue2_id
+root_region2::get_value_by_name (tree identifier,
+				const region_model2 &model) const
+{
+  if (stack_region2 *stack = get_stack_region2 (&model))
+    {
+      const svalue2 *sval = stack->get_value_by_name (identifier, model);
+      if (!sid.null_p ())
+	return sid;
+    }
+  if (map_region2 *globals = get_globals_region2 (&model))
+    {
+      const svalue2 *sval = globals->get_value_by_name (identifier, model);
+      if (!sid.null_p ())
+	return sid;
+    }
+  return svalue2_id::null ();
+}
+#endif
+
+/* class symbolic_region2 : public map_region2.  */
+
+/* Compare the fields of this symbolic_region2 with OTHER, returning true
+   if they are equal.
+   For use by region2::operator==.  */
+
+bool
+symbolic_region2::compare_fields (const symbolic_region2 &other) const
+{
+  return m_sval_ptr == other.m_sval_ptr;
+}
+
+/* FIXME.  */
+
+void
+symbolic_region2::accept (visitor *v) const
+{
+  region2::accept (v);
+  m_sval_ptr->accept (v);
+}
+
+/* FIXME.  */
+
+void
+symbolic_region2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    {
+      pp_string (pp, "(*");
+      m_sval_ptr->dump_to_pp (pp, simple);
+      pp_string (pp, ")");
+    }
+  else
+    {
+      pp_string (pp, "symbolic_region2(");
+      get_parent_region ()->dump_to_pp (pp, simple);
+      pp_string (pp, ", ");
+      print_quoted_type (pp, get_type ());
+      pp_string (pp, ", ");
+      m_sval_ptr->dump_to_pp (pp, simple);
+      pp_string (pp, ")");
+    }
+}
+
+/* class decl_region2 : public region2.  */
+
+void
+decl_region2::add_to_hash (inchash::hash &hstate) const
+{
+  region2::add_to_hash (hstate);
+  hstate.add_ptr (m_decl);
+}
+
+bool
+decl_region2::compare_fields (const decl_region2 &other) const
+{
+  return m_decl == other.m_decl;
+}
+
+void
+decl_region2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    pp_printf (pp, "%E", m_decl);
+  else
+    {
+      pp_string (pp, "decl_region2(");
+      get_parent_region ()->dump_to_pp (pp, simple);
+      pp_string (pp, ", ");
+      print_quoted_type (pp, get_type ());
+      pp_printf (pp, ", %qE)", m_decl);
+    }
+}
+
+/* FIXME.  */
+
+int
+decl_region2::get_stack_depth () const
+{
+  if (get_parent_region () == NULL)
+    return 0;
+  if (const frame_region2 *frame_reg
+	= get_parent_region ()->dyn_cast_frame_region2 ())
+    return frame_reg->get_stack_depth ();
+  return 0;
+}
+
+/* class field_region2 : public region2.  */
+
+void
+field_region2::add_to_hash (inchash::hash &hstate) const
+{
+  region2::add_to_hash (hstate);
+  hstate.add_ptr (m_field);
+}
+
+bool
+field_region2::compare_fields (const field_region2 &other) const
+{
+  return m_field == other.m_field;
+}
+
+void
+field_region2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    {
+      get_parent_region ()->dump_to_pp (pp, simple);
+      pp_string (pp, ".");
+      pp_printf (pp, "%E", m_field);
+    }
+  else
+    {
+      pp_string (pp, "field_region2(");
+      get_parent_region ()->dump_to_pp (pp, simple);
+      pp_string (pp, ", ");
+      print_quoted_type (pp, get_type ());
+      pp_printf (pp, ", %qE)", m_field);
+    }
+}
+
+/* class element_region2 : public region2.  */
+
+bool
+element_region2::compare_fields (const element_region2 &other) const
+{
+  return m_index == other.m_index;
+}
+
+/* FIXME.  */
+
+void
+element_region2::accept (visitor *v) const
+{
+  region2::accept (v);
+  m_index->accept (v);
+}
+
+void
+element_region2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    {
+      //pp_string (pp, "(");
+      get_parent_region ()->dump_to_pp (pp, simple);
+      pp_string (pp, "[");
+      m_index->dump_to_pp (pp, simple);
+      pp_string (pp, "]");
+      //pp_string (pp, ")");
+    }
+  else
+    {
+      pp_string (pp, "element_region2(");
+      get_parent_region ()->dump_to_pp (pp, simple);
+      pp_string (pp, ", ");
+      print_quoted_type (pp, get_type ());
+      pp_string (pp, ", ");
+      m_index->dump_to_pp (pp, simple);
+      pp_printf (pp, ")");
+    }
+}
+
+void
+element_region2::add_to_hash (inchash::hash &hstate) const
+{
+  region2::add_to_hash (hstate);
+  hstate.add_ptr (m_index);
+}
+
+/* class offset_region2 : public region2.  */
+
+bool
+offset_region2::compare_fields (const offset_region2 &other) const
+{
+  return m_byte_offset == other.m_byte_offset;
+}
+
+/* FIXME.  */
+
+void
+offset_region2::accept (visitor *v) const
+{
+  region2::accept (v);
+  m_byte_offset->accept (v);
+}
+
+void
+offset_region2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    {
+      //pp_string (pp, "(");
+      get_parent_region ()->dump_to_pp (pp, simple);
+      pp_string (pp, "+");
+      m_byte_offset->dump_to_pp (pp, simple);
+      //pp_string (pp, ")");
+    }
+  else
+    {
+      pp_string (pp, "offset_region2(");
+      get_parent_region ()->dump_to_pp (pp, simple);
+      pp_string (pp, ", ");
+      print_quoted_type (pp, get_type ());
+      pp_string (pp, ", ");
+      m_byte_offset->dump_to_pp (pp, simple);
+      pp_printf (pp, ")");
+    }
+}
+
+void
+offset_region2::add_to_hash (inchash::hash &hstate) const
+{
+  region2::add_to_hash (hstate);
+  hstate.add_ptr (m_byte_offset);
+}
+
+/* class cast_region2 : public region2.  */
+
+bool
+cast_region2::compare_fields (const cast_region2 &other) const
+{
+  return m_original_region == other.m_original_region;
+}
+
+/* FIXME.  */
+
+void
+cast_region2::accept (visitor *v) const
+{
+  region2::accept (v);
+  m_original_region->accept (v);
+}
+
+void
+cast_region2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    {
+      pp_string (pp, "CAST_REG(");
+      print_quoted_type (pp, get_type ());
+      pp_string (pp, ", ");
+      m_original_region->dump_to_pp (pp, simple);
+      pp_string (pp, ")");
+    }
+  else
+    {
+      pp_string (pp, "cast_region2(");
+      m_original_region->dump_to_pp (pp, simple);
+      pp_string (pp, ", ");
+      print_quoted_type (pp, get_type ());
+      pp_printf (pp, ")");
+    }
+}
+
+void
+cast_region2::add_to_hash (inchash::hash &hstate) const
+{
+  region2::add_to_hash (hstate);
+  hstate.add_ptr (m_original_region);
+}
+
+/* class heap_allocated_region2 : public region2.  */
+
+void
+heap_allocated_region2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  // TODO:
+  if (simple)
+    pp_printf (pp, "HEAP_ALLOCATED_REGION(%i)", get_id ());
+  else
+    pp_printf (pp, "heap_allocated_region2(%i)", get_id ());
+}
+
+/* class alloca_region2 : public region2.  */
+
+void
+alloca_region2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  // TODO:
+  if (simple)
+    pp_string (pp, "ALLOCA_REGION");
+  else
+    pp_string (pp, "alloca_region2()");
+}
+
+/* class string_region2 : public region2.  */
+
+void
+string_region2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  // TODO:
+  if (simple)
+    dump_tree (pp, m_string_cst);
+  else
+    {
+      pp_string (pp, "string_region2(");
+      dump_tree (pp, m_string_cst);
+      pp_string (pp, " (");
+      pp_pointer (pp, m_string_cst);
+      pp_string (pp, "))");
+    }
+}
+
+/* class unknown_region2 : public region2.  */
+
+void
+unknown_region2::dump_to_pp (pretty_printer *pp, bool /*simple*/) const
+{
+  pp_string (pp, "UNKNOWN_REGION");
+}
+
+/* class region_model2_manager.  */
+
+region_model2_manager::region_model2_manager ()
+: m_next_region_id (0),
+  m_root_region (alloc_region_id ()),
+  m_stack_region (alloc_region_id (), &m_root_region),
+  m_heap_region (alloc_region_id (), &m_root_region),
+  m_unknown_NULL (NULL),
+  m_max_complexity (0, 0),
+  m_code_region (alloc_region_id (), &m_root_region),
+  m_fndecls_map (), m_labels_map (),
+  m_globals_region (alloc_region_id (), &m_root_region),
+  m_globals_map (),
+  m_store2_mgr (this)
+{
+  // TODO
+}
+
+region_model2_manager::~region_model2_manager ()
+{
+  /* Delete consolidated svalues.  */
+  for (constants_map_t::iterator iter = m_constants_map.begin ();
+       iter != m_constants_map.end (); ++iter)
+    delete (*iter).second;
+  for (unknowns_map_t::iterator iter = m_unknowns_map.begin ();
+       iter != m_unknowns_map.end (); ++iter)
+    delete (*iter).second;
+  delete m_unknown_NULL;
+  for (setjmp_values_map_t::iterator iter = m_setjmp_values_map.begin ();
+       iter != m_setjmp_values_map.end (); ++iter)
+    delete (*iter).second;
+  for (poisoned_values_map_t::iterator iter = m_poisoned_values_map.begin ();
+       iter != m_poisoned_values_map.end (); ++iter)
+    delete (*iter).second;
+  for (initial_values_map_t::iterator iter = m_initial_values_map.begin ();
+       iter != m_initial_values_map.end (); ++iter)
+    delete (*iter).second;
+  for (pointer_values_map_t::iterator iter = m_pointer_values_map.begin ();
+       iter != m_pointer_values_map.end (); ++iter)
+    delete (*iter).second;
+  for (unaryop_values_map_t::iterator iter = m_unaryop_values_map.begin ();
+       iter != m_unaryop_values_map.end (); ++iter)
+    delete (*iter).second;
+  for (binop_values_map_t::iterator iter = m_binop_values_map.begin ();
+       iter != m_binop_values_map.end (); ++iter)
+    delete (*iter).second;
+  for (sub_values_map_t::iterator iter = m_sub_values_map.begin ();
+       iter != m_sub_values_map.end (); ++iter)
+    delete (*iter).second;
+  for (unmergeable_values_map_t::iterator iter
+	 = m_unmergeable_values_map.begin ();
+       iter != m_unmergeable_values_map.end (); ++iter)
+    delete (*iter).second;
+  for (widening_values_map_t::iterator iter = m_widening_values_map.begin ();
+       iter != m_widening_values_map.end (); ++iter)
+    delete (*iter).second;
+  for (compound_values_map_t::iterator iter = m_compound_values_map.begin ();
+       iter != m_compound_values_map.end (); ++iter)
+    delete (*iter).second;
+  for (conjured_values_map_t::iterator iter = m_conjured_values_map.begin ();
+       iter != m_conjured_values_map.end (); ++iter)
+    delete (*iter).second;
+
+  /* Delete consolidated regions.  */
+  for (fndecls_map_t::iterator iter = m_fndecls_map.begin ();
+       iter != m_fndecls_map.end (); ++iter)
+    delete (*iter).second;
+  for (labels_map_t::iterator iter = m_labels_map.begin ();
+       iter != m_labels_map.end (); ++iter)
+    delete (*iter).second;
+  for (globals_map_t::iterator iter = m_globals_map.begin ();
+       iter != m_globals_map.end (); ++iter)
+    delete (*iter).second;
+  for (string_map_t::iterator iter = m_string_map.begin ();
+       iter != m_string_map.end (); ++iter)
+    delete (*iter).second;
+}
+
+/* Return true if C exceeds the complexity limit for svalues.  */
+
+bool
+region_model2_manager::too_complex_p (const complexity &c) const
+{
+  /* TODO: check both num nodes and max_depth
+     TODO: use params.  */
+#if 0
+  if (c.m_num_nodes > 50)
+    return true;
+#endif
+#if 1
+  if (c.m_max_depth > param_analyzer_max_svalue_depth)
+    return true;
+#endif
+  return false;
+}
+
+/* If SVAL exceeds the complexity limit for svalues, delete it
+   and return true.
+   Otherwise update m_max_complexity and return false.  */
+
+bool
+region_model2_manager::reject_if_too_complex (svalue2 *sval)
+{
+  const complexity &c = sval->get_complexity ();
+  if (!too_complex_p (c))
+    {
+      if (m_max_complexity.m_num_nodes < c.m_num_nodes)
+	m_max_complexity.m_num_nodes = c.m_num_nodes;
+      if (m_max_complexity.m_max_depth < c.m_max_depth)
+	m_max_complexity.m_max_depth = c.m_max_depth;
+      return false;
+    }
+
+  /* TODO.  */
+  if (0)
+    {
+      pretty_printer pp;
+      pp_format_decoder (&pp) = default_tree_printer;
+      sval->dump_to_pp (&pp, true);
+      warning (0, "svalue is too complicated: %qs", pp_formatted_text (&pp));
+    }
+
+  delete sval;
+
+  return true;
+}
+
+/* Macro for imposing a complexity limit on svalues, for use within
+   region_model2_manager member functions.
+
+   If SVAL is exceeds the complexity limit, delete it and return an UNKNOWN
+   value of the same type.
+   Otherwise update m_max_complexity and carry on.  */
+
+#define RETURN_UNKNOWN_IF_TOO_COMPLEX(SVAL)			\
+  do {								\
+    svalue2 *sval_ = (SVAL);					\
+    tree type_ = sval_->get_type ();				\
+    if (reject_if_too_complex (sval_))				\
+      return get_or_create_unknown_svalue2 (type_);		\
+  } while (0)
+
+/* svalue2 consolidation.  */
+
+/* Return an svalue2 *for a constant_svalue2 for CST_EXPR,
+   creating the constant_svalue2 if necessary.
+   The constant_svalue2 instances are reused, based on pointer equality
+   of trees  */
+const svalue2 *
+region_model2_manager::get_or_create_constant_svalue2 (tree cst_expr)
+{
+  gcc_assert (cst_expr);
+
+  constant_svalue2 **slot = m_constants_map.get (cst_expr);
+  if (slot)
+    return *slot;
+  constant_svalue2 *cst_sval = new constant_svalue2 (cst_expr);
+  RETURN_UNKNOWN_IF_TOO_COMPLEX (cst_sval);
+  m_constants_map.put (cst_expr, cst_sval);
+  return cst_sval;
+  // TODO: cst equality vs tree pointer equality?
+}
+
+/* Return an svalue2 *for a unknown_svalue2 for TYPE (which can be NULL),
+   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)
+{
+  /* FIXME: remove this special case in favor of fixing traits to
+     allow for a NULL key (as opposed to it being the "empty" value) .  */
+  if (type == NULL_TREE)
+    {
+      if (!m_unknown_NULL)
+	m_unknown_NULL = new unknown_svalue2 (type);
+      return m_unknown_NULL;
+    }
+
+  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)
+{
+  /* The initial value of a cast is a cast of the initial value.  */
+  if (const cast_region2 *cast_reg = reg->dyn_cast_cast_region2 ())
+    {
+      const region2 *original_reg = cast_reg->get_original_region ();
+      return get_or_create_cast (cast_reg->get_type (),
+				 get_or_create_initial_value (original_reg));
+    }
+
+  if (initial_svalue2 **slot = m_initial_values_map.get (reg))
+    return *slot;
+  initial_svalue2 *initial_sval = new initial_svalue2 (reg->get_type (), reg);
+  RETURN_UNKNOWN_IF_TOO_COMPLEX (initial_sval);
+  m_initial_values_map.put (reg, initial_sval);
+  return initial_sval;
+}
+
+const svalue2 *
+region_model2_manager::get_or_create_setjmp_svalue2 (const setjmp_record &r,
+						     tree type)
+{
+  setjmp_svalue2::key_t key (r, type);
+  if (setjmp_svalue2 **slot = m_setjmp_values_map.get (key))
+    return *slot;
+  setjmp_svalue2 *setjmp_sval = new setjmp_svalue2 (r, type);
+  RETURN_UNKNOWN_IF_TOO_COMPLEX (setjmp_sval);
+  m_setjmp_values_map.put (key, setjmp_sval);
+  return setjmp_sval;
+}
+
+const svalue2 *
+region_model2_manager::get_or_create_poisoned_svalue2 (enum poison_kind kind,
+						       tree type)
+{
+  poisoned_svalue2::key_t key (kind, type);
+  if (poisoned_svalue2 **slot = m_poisoned_values_map.get (key))
+    return *slot;
+  poisoned_svalue2 *poisoned_sval = new poisoned_svalue2 (kind, type);
+  RETURN_UNKNOWN_IF_TOO_COMPLEX (poisoned_sval);
+  m_poisoned_values_map.put (key, poisoned_sval);
+  return poisoned_sval;
+}
+
+const svalue2 *
+region_model2_manager::get_ptr_svalue2 (tree ptr_type, const region2 *pointee)
+{
+  /* If this is a symbolic region from dereferencing a pointer, and the types
+     match, then return the original pointer.  */
+  if (const symbolic_region2 *sym_reg = pointee->dyn_cast_symbolic_region2 ())
+    if (ptr_type == sym_reg->get_pointer ()->get_type ())
+      return sym_reg->get_pointer ();
+
+  // FIXME: use ptr_type here:
+  if (region_svalue2 **slot = m_pointer_values_map.get (pointee))
+    return *slot;
+  region_svalue2 *sval = new region_svalue2 (ptr_type, pointee);
+  RETURN_UNKNOWN_IF_TOO_COMPLEX (sval);
+  m_pointer_values_map.put (pointee, sval);
+  return sval;
+}
+
+const svalue2 *
+region_model2_manager::maybe_fold_unaryop (tree type, enum tree_code op,
+					   const svalue2 *arg)
+{
+  /* Ops on "unknown" are also unknown.  */
+  if (arg->get_kind () == svalue2::SK_UNKNOWN)
+    return get_or_create_unknown_svalue2 (type);
+
+  switch (op)
+    {
+    default: break;
+    case NOP_EXPR:
+      {
+	/* Handle redundant casts.  */
+	if (arg->get_type ()
+	    && useless_type_conversion_p (arg->get_type (), type))
+	  return arg;
+
+	/* Fold "cast<TYPE> (cast <INNER_TYPE> (innermost_arg))
+	     => "cast<TYPE> (innermost_arg)",
+	   unless INNER_TYPE is narrower than TYPE.  */
+	if (const svalue2 *innermost_arg = arg->maybe_undo_cast ())
+	  {
+	    tree inner_type = arg->get_type ();
+	    if (TYPE_PRECISION (type) <= TYPE_PRECISION (inner_type))
+	      return maybe_fold_unaryop (type, op, innermost_arg);
+	  }
+      }
+      break;
+    case TRUTH_NOT_EXPR:
+      {
+	/* Invert comparisons e.g. "!(x == y)" => "x != y".  */
+	if (const binop_svalue2 *binop = arg->dyn_cast_binop_svalue2 ())
+	  if (TREE_CODE_CLASS (binop->get_op ()) == tcc_comparison)
+	    {
+	      enum tree_code inv_op
+		= invert_tree_comparison (binop->get_op (),
+					  HONOR_NANS (binop->get_type ()));
+	      if (inv_op != ERROR_MARK)
+		return get_or_create_binop (binop->get_type (), inv_op,
+					    binop->get_arg0 (),
+					    binop->get_arg1 ());
+	    }
+      }
+      break;
+    }
+
+  /* Constants.  */
+  if (tree cst = arg->maybe_get_constant ())
+    if (tree result = fold_unary (op, type, cst))
+      return get_or_create_constant_svalue2 (result);
+
+  return NULL;
+}
+
+const svalue2 *
+region_model2_manager::get_or_create_unaryop (tree type, enum tree_code op,
+					      const svalue2 *arg)
+{
+  if (const svalue2 *folded = maybe_fold_unaryop  (type, op, arg))
+    return folded;
+  unaryop_svalue2::key_t key (type, op, arg);
+  if (unaryop_svalue2 **slot = m_unaryop_values_map.get (key))
+    return *slot;
+  unaryop_svalue2 *unaryop_sval = new unaryop_svalue2 (type, op, arg);
+  RETURN_UNKNOWN_IF_TOO_COMPLEX (unaryop_sval);
+  m_unaryop_values_map.put (key, unaryop_sval);
+  return unaryop_sval;
+}
+
+const svalue2 *
+region_model2_manager::get_or_create_cast (tree type, const svalue2 *arg)
+{
+  return get_or_create_unaryop (type, NOP_EXPR, arg);
+}
+
+const svalue2 *
+region_model2_manager::maybe_fold_binop (tree type, enum tree_code op,
+					 const svalue2 *arg0,
+					 const svalue2 *arg1)
+{
+  tree cst0 = arg0->maybe_get_constant ();
+  tree cst1 = arg1->maybe_get_constant ();
+  /* (CST OP CST).  */
+  if (cst0 && cst1)
+    {
+      if (tree result = fold_binary (op, type, cst0, cst1))
+	if (CONSTANT_CLASS_P (result))
+	  return get_or_create_constant_svalue2 (result);
+    }
+
+  if (FLOAT_TYPE_P (type)
+      || (arg0->get_type () && FLOAT_TYPE_P (arg0->get_type ()))
+      || (arg1->get_type () && FLOAT_TYPE_P (arg1->get_type ())))
+    return NULL;
+
+  switch (op)
+    {
+    default:
+      break;
+    case POINTER_PLUS_EXPR:
+    case PLUS_EXPR:
+      /* (VAL + 0) -> VAL.  */
+      if (cst1 && zerop (cst1) && type == arg0->get_type ())
+	return arg0;
+      break;
+    case MINUS_EXPR:
+      /* (VAL - 0) -> VAL.  */
+      if (cst1 && zerop (cst1) && type == arg0->get_type ())
+	return arg0;
+      break;
+    case MULT_EXPR:
+      /* (VAL * 0).  */
+      if (cst1 && zerop (cst1))
+	// FIXME: check for integer type
+	return get_or_create_constant_svalue2 (build_int_cst (type, 0));
+      /* (VAL * 1) -> VAL.  */
+      if (cst1 && integer_onep (cst1))
+	return arg0;
+      break;
+    case TRUTH_ANDIF_EXPR:
+    case TRUTH_AND_EXPR:
+      if (cst1)
+	{
+	  if (zerop (cst1))
+	    /* "(ARG0 && 0)" -> "0".  */
+	    return get_or_create_constant_svalue2 (build_int_cst (type, 0));
+	  else
+	    /* "(ARG0 && nonzero-cst)" -> "ARG0".  */
+	    return get_or_create_cast (type, arg0);
+	}
+      break;
+    case TRUTH_ORIF_EXPR:
+    case TRUTH_OR_EXPR:
+      if (cst1)
+	{
+	  if (zerop (cst1))
+	    /* "(ARG0 || 0)" -> "ARG0".  */
+	    return get_or_create_cast (type, arg0);
+	  else
+	    /* "(ARG0 && nonzero-cst)" -> "nonzero-cst".  */
+	    return get_or_create_cast (type, arg1);
+	}
+      break;
+    }
+
+  /* For associative ops, fold "(X op CST_A) op CST_B)" to
+     "X op (CST_A op CST_B)".  */
+  if (cst1 && associative_tree_code (op))
+    if (const binop_svalue2 *binop = arg0->dyn_cast_binop_svalue2 ())
+      if (binop->get_op () == op
+	  && binop->get_arg1 ()->maybe_get_constant ()
+	  && type == binop->get_type ()
+	  && type == binop->get_arg0 ()->get_type ()
+	  && type == binop->get_arg1 ()->get_type ())
+	return get_or_create_binop
+	  (type, op, binop->get_arg0 (),
+	   get_or_create_binop (type, op,
+				binop->get_arg1 (), arg1));
+
+  /* FIXME.  pointer_plus_expr on pointer_plus_expr...
+     associative_tree_code is false for POINTER_PLUS_EXPR, but we
+     can fold:
+       "(PTR ptr+ CST_A) ptr+ CST_B)" to "PTR ptr+ (CST_A ptr+ CST_B)".  */
+  /* e.g. in data-model-1.c: test_42c.  */
+  if (cst1 && op == POINTER_PLUS_EXPR)
+    if (const binop_svalue2 *binop = arg0->dyn_cast_binop_svalue2 ())
+      if (binop->get_op () == POINTER_PLUS_EXPR)
+	if (tree cst01 = binop->get_arg1 ()->maybe_get_constant ())
+	  {
+	    return get_or_create_binop
+	      (type, op, binop->get_arg0 (),
+	       get_or_create_binop (size_type_node, op,
+				    binop->get_arg1 (), arg1));
+	  }
+
+  /* Ops on "unknown" are also unknown (unless we can use one of the
+     identities above).  */
+  if (arg0->get_kind () == svalue2::SK_UNKNOWN
+      || arg1->get_kind () == svalue2::SK_UNKNOWN)
+    return get_or_create_unknown_svalue2 (type);
+
+  // TODO
+
+  return NULL;
+}
+
+const svalue2 *
+region_model2_manager::get_or_create_binop (tree type, enum tree_code op,
+					    const svalue2 *arg0,
+					    const svalue2 *arg1)
+{
+  /* For commutative ops, put any constant on the RHS.  */
+  if (arg0->maybe_get_constant () && commutative_tree_code (op))
+    std::swap (arg0, arg1);
+
+  if (const svalue2 *folded = maybe_fold_binop (type, op, arg0, arg1))
+    return folded;
+
+  binop_svalue2::key_t key (type, op, arg0, arg1);
+  if (binop_svalue2 **slot = m_binop_values_map.get (key))
+    return *slot;
+  binop_svalue2 *binop_sval = new binop_svalue2 (type, op, arg0, arg1);
+  RETURN_UNKNOWN_IF_TOO_COMPLEX (binop_sval);
+  m_binop_values_map.put (key, binop_sval);
+  return binop_sval;
+}
+
+/* FIXME.  */
+
+static const svalue2 *
+maybe_get_char_from_string_cst (tree string_cst, tree byte_offset_cst,
+				region_model2_manager *mgr)
+{
+  gcc_assert (TREE_CODE (string_cst) == STRING_CST);
+
+  /* Adapted from fold_read_from_constant_string.  */
+  scalar_int_mode char_mode;
+  if (TREE_CODE (byte_offset_cst) == INTEGER_CST
+      && compare_tree_int (byte_offset_cst,
+			   TREE_STRING_LENGTH (string_cst)) < 0
+      && is_int_mode (TYPE_MODE (TREE_TYPE (TREE_TYPE (string_cst))),
+		      &char_mode)
+      && GET_MODE_SIZE (char_mode) == 1)
+    {
+      tree char_cst
+	= build_int_cst_type (TREE_TYPE (TREE_TYPE (string_cst)),
+			      (TREE_STRING_POINTER (string_cst)
+			       [TREE_INT_CST_LOW (byte_offset_cst)]));
+      return mgr->get_or_create_constant_svalue2 (char_cst);
+    }
+  return NULL;
+}
+
+/* Subroutine of region_model2_manager::get_or_create_sub_svalue2.
+   Return a folded svalue2, or NULL.  */
+
+const svalue2 *
+region_model2_manager::maybe_fold_sub_svalue2 (tree type,
+					       const svalue2 *parent_svalue2,
+					       const region2 *subregion)
+{
+  /* Subvalues of "unknown" are unknown.  */
+  if (parent_svalue2->get_kind () == svalue2::SK_UNKNOWN)
+    return get_or_create_unknown_svalue2 (type);
+
+  /* If we have a subregion of a zero-fill, it's zero.  */
+  if (const unaryop_svalue2 *unary
+      = parent_svalue2->dyn_cast_unaryop_svalue2 ())
+    {
+      if (unary->get_op () == NOP_EXPR)
+	if (tree cst = unary->get_arg ()->maybe_get_constant ())
+	  if (zerop (cst))
+	    {
+	      const svalue2 *cst_sval
+		= get_or_create_constant_svalue2 (cst);
+	      return get_or_create_cast (type, cst_sval);
+	    }
+    }
+
+  /* Handle getting individual chars from a STRING_CST.  */
+  if (tree cst = parent_svalue2->maybe_get_constant ())
+    if (TREE_CODE (cst) == STRING_CST)
+      if (const element_region2 *element_reg
+	    = subregion->dyn_cast_element_region2 ())
+	{
+	  const svalue2 *idx_sval = element_reg->get_index ();
+	  if (tree cst_idx = idx_sval->maybe_get_constant ())
+	    if (const svalue2 *char_sval
+		= maybe_get_char_from_string_cst (cst, cst_idx, this))
+	      return get_or_create_cast (type, char_sval);
+	}
+
+  /* SUB(INIT(r)).FIELD -> INIT(r.FIELD)
+     i.e.
+     Subvalue(InitialValue(R1), FieldRegion(R2, F))
+       -> InitialValue(FieldRegion(R1, F)).  */
+  if (const initial_svalue2 *init_sval
+        = parent_svalue2->dyn_cast_initial_svalue2 ())
+    {
+      if (const field_region2 *field_reg = subregion->dyn_cast_field_region2 ())
+	{
+	  const region2 *field_reg_new
+	    = get_field_region (init_sval->get_region (),
+				field_reg->get_field ());
+	  return get_or_create_initial_value (field_reg_new);
+	}
+    }
+  // TODO: etc similar for element_region
+
+  return NULL;
+}
+
+/* FIXME.  */
+
+const svalue2 *
+region_model2_manager::get_or_create_sub_svalue2 (tree type,
+						  const svalue2 *parent_svalue2,
+						  const region2 *subregion)
+{
+  if (const svalue2 *folded
+	= maybe_fold_sub_svalue2 (type, parent_svalue2, subregion))
+    return folded;
+
+  sub_svalue2::key_t key (type, parent_svalue2, subregion);
+  if (sub_svalue2 **slot = m_sub_values_map.get (key))
+    return *slot;
+  sub_svalue2 *sub_sval
+    = new sub_svalue2 (type, parent_svalue2, subregion);
+  RETURN_UNKNOWN_IF_TOO_COMPLEX (sub_sval);
+  m_sub_values_map.put (key, sub_sval);
+  return sub_sval;
+}
+
+/* FIXME.  */
+
+const svalue2 *
+region_model2_manager::get_or_create_unmergeable (const svalue2 *arg)
+{
+  if (arg->get_kind () == svalue2::SK_UNMERGEABLE)
+    return arg;
+
+  if (unmergeable_svalue2 **slot = m_unmergeable_values_map.get (arg))
+    return *slot;
+  unmergeable_svalue2 *unmergeable_sval = new unmergeable_svalue2 (arg);
+  RETURN_UNKNOWN_IF_TOO_COMPLEX (unmergeable_sval);
+  m_unmergeable_values_map.put (arg, unmergeable_sval);
+  return unmergeable_sval;
+}
+
+/* FIXME.  */
+
+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, 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, point, base_sval, iter_sval);
+  RETURN_UNKNOWN_IF_TOO_COMPLEX (widening_sval);
+  m_widening_values_map.put (key, widening_sval);
+  return widening_sval;
+}
+
+/* FIXME.  */
+
+const svalue2 *
+region_model2_manager::
+get_or_create_compound_svalue2 (tree type,
+				const binding_map &map)
+{
+  compound_svalue2::key_t tmp_key (type, &map);
+  if (compound_svalue2 **slot = m_compound_values_map.get (tmp_key))
+    return *slot;
+  compound_svalue2 *compound_sval
+    = new compound_svalue2 (type, map);
+  RETURN_UNKNOWN_IF_TOO_COMPLEX (compound_sval);
+  /* Use make_key rather than reusing the key, so that we use a
+     ptr to compound_sval's binding_map, rather than the MAP param.  */
+  m_compound_values_map.put (compound_sval->make_key (), compound_sval);
+  return compound_sval;
+}
+
+/* FIXME.  */
+
+const svalue2 *
+region_model2_manager::
+get_or_create_conjured_svalue2 (tree type, const gimple *stmt,
+				const region2 *id_reg)
+{
+  conjured_svalue2::key_t key (type, stmt, id_reg);
+  if (conjured_svalue2 **slot = m_conjured_values_map.get (key))
+    return *slot;
+  conjured_svalue2 *conjured_sval
+    = new conjured_svalue2 (type, stmt, id_reg);
+  RETURN_UNKNOWN_IF_TOO_COMPLEX (conjured_sval);
+  m_conjured_values_map.put (key, conjured_sval);
+  return conjured_sval;
+}
+
+/* region2 consolidation.  */
+
+/* FIXME.  */
+
+const function_region2 *
+region_model2_manager::get_region_for_fndecl (tree fndecl)
+{
+  gcc_assert (TREE_CODE (fndecl) == FUNCTION_DECL);
+
+  function_region2 **slot = m_fndecls_map.get (fndecl);
+  if (slot)
+    return *slot;
+  function_region2 *reg
+    = new function_region2 (alloc_region_id (), &m_code_region, fndecl);
+  m_fndecls_map.put (fndecl, reg);
+  return reg;
+}
+
+/* FIXME.  */
+
+const label_region2 *
+region_model2_manager::get_region_for_label (tree label)
+{
+  gcc_assert (TREE_CODE (label) == LABEL_DECL);
+
+  label_region2 **slot = m_labels_map.get (label);
+  if (slot)
+    return *slot;
+
+  tree fndecl = DECL_CONTEXT (label);
+  gcc_assert (fndecl && TREE_CODE (fndecl) == FUNCTION_DECL);
+
+  const function_region2 *func_reg = get_region_for_fndecl (fndecl);
+  label_region2 *reg
+    = new label_region2 (alloc_region_id (), func_reg, label);
+  m_labels_map.put (label, reg);
+  return reg;
+}
+
+/* FIXME.  */
+
+const decl_region2 *
+region_model2_manager::get_region_for_global (tree expr)
+{
+  gcc_assert (TREE_CODE (expr) == VAR_DECL);
+
+  decl_region2 **slot = m_globals_map.get (expr);
+  if (slot)
+    return *slot;
+  decl_region2 *reg
+    = new decl_region2 (alloc_region_id (), &m_globals_region, expr);
+  m_globals_map.put (expr, reg);
+  return reg;
+}
+
+/* FIXME.  */
+
+const region2 *
+region_model2_manager::get_field_region (const region2 *parent, tree field)
+{
+  // FIXME: should only alloc_region_id if consolidation fails
+  region2 *field_reg = new field_region2 (alloc_region_id (), parent, field);
+  return const_cast <region2 *> (m_fields.consolidate (field_reg));
+}
+
+/* FIXME.  */
+
+const region2 *
+region_model2_manager::get_element_region (const region2 *parent,
+					   tree element_type,
+					   const svalue2 *index)
+{
+  // FIXME: should only alloc_region_id if consolidation fails
+  region2 *element_reg
+    = new element_region2 (alloc_region_id (), parent, element_type, index);
+  return const_cast <region2 *> (m_elements.consolidate (element_reg));
+}
+
+/* FIXME.  */
+
+const region2 *
+region_model2_manager::get_offset_region (const region2 *parent,
+					  tree type,
+					  const svalue2 *byte_offset)
+{
+  /* If BYTE_OFFSET is zero, return PARENT.  */
+  if (tree cst_offset = byte_offset->maybe_get_constant ())
+    if (zerop (cst_offset))
+      return get_cast_region (parent, type);
+
+  /* Fold OFFSET_REGION(OFFSET_REGION(REG, X), Y)
+     to   OFFSET_REGION(REG, (X + Y)).  */
+  if (const offset_region2 *parent_offset_reg
+	= parent->dyn_cast_offset_region2 ())
+    {
+      const svalue2 *sval_x = parent_offset_reg->get_byte_offset ();
+      const svalue2 *sval_sum
+	= get_or_create_binop (byte_offset->get_type (),
+			       PLUS_EXPR, sval_x, byte_offset);
+      return get_offset_region (parent->get_parent_region (), type, sval_sum);
+    }
+
+  // FIXME: should only alloc_region_id if consolidation fails
+  region2 *offset_reg
+    = new offset_region2 (alloc_region_id (), parent, type, byte_offset);
+  return const_cast <region2 *> (m_offset_regions.consolidate (offset_reg));
+}
+
+/* FIXME.  */
+
+const region2 *
+region_model2_manager::get_cast_region (const region2 *original_region,
+					tree type)
+{
+  /* If types match, return ORIGINAL_REGION.  */
+  if (type == original_region->get_type ())
+    return original_region;
+
+  // FIXME: should only alloc_region_id if consolidation fails
+  region2 *cast_reg
+    = new cast_region2 (alloc_region_id (), original_region, type);
+  return const_cast <region2 *> (m_cast_regions.consolidate (cast_reg));
+}
+
+/* FIXME.  */
+
+const frame_region2 *
+region_model2_manager::get_frame_region (const frame_region2 *calling_frame,
+					 function *fun)
+{
+  int index = calling_frame ? calling_frame->get_index () + 1 : 0;
+  // FIXME: should only alloc_region_id if consolidation fails
+  frame_region2 *frame_reg
+    = new frame_region2 (alloc_region_id (), &m_stack_region, calling_frame,
+			 fun, index);
+  return const_cast <frame_region2 *> (m_frames.consolidate (frame_reg));
+}
+
+/* FIXME.  */
+
+const region2 *
+region_model2_manager::get_symbolic_region (const svalue2 *sval)
+{
+  // FIXME: should only alloc_region_id if consolidation fails
+  region2 *reg
+    = new symbolic_region2 (alloc_region_id (), &m_root_region, sval);
+  return const_cast <region2 *> (m_symbolic_regions.consolidate (reg));
+}
+
+/* FIXME.  */
+
+const string_region2 *
+region_model2_manager::get_region_for_string (tree string_cst)
+{
+  gcc_assert (TREE_CODE (string_cst) == STRING_CST);
+
+  string_region2 **slot = m_string_map.get (string_cst);
+  if (slot)
+    return *slot;
+  string_region2 *reg
+    = new string_region2 (alloc_region_id (), &m_root_region, string_cst);
+  // FIXME: is there a better choice of parent region?
+  m_string_map.put (string_cst, reg);
+  return reg;
+}
+
+/* If we see a tree code we don't know how to handle, rather than
+   ICE or generate bogus results, create a dummy region2, and notify
+   CTXT so that it can mark the new state as being not properly
+   modelled.  The exploded graph can then stop exploring that path,
+   since any diagnostics we might issue will have questionable
+   validity.  */
+
+const region2 *
+region_model2_manager::
+get_region2_for_unexpected_tree_code (region_model2_context *ctxt,
+				      tree t,
+				      const dump_location_t &loc)
+{
+  gcc_assert (ctxt);
+  tree type = TYPE_P (t) ? t : TREE_TYPE (t);
+  region2 *new_reg
+    = new unknown_region2 (alloc_region_id (), &m_root_region, type); // FIXME
+  // or should this be something else?
+  ctxt->on_unexpected_tree_code (t, loc);
+  return new_reg;
+}
+
+/* FIXME.  */
+
+const region2 *
+region_model2_manager::create_region_for_heap_alloc ()
+{
+  // FIXME: should this be a symbolic region??
+  region2 *reg
+    = new heap_allocated_region2 (alloc_region_id (), &m_heap_region);
+  m_managed_dynamic_regions.safe_push (reg);
+  // TODO: selftest; verify no leak under valgrind
+  return reg;
+  /* TODO: maybe have an ID to model the different allocators?
+     (are they different parent regions? or some kind of discriminant var?)  */
+}
+
+/* FIXME.  */
+
+const region2 *
+region_model2_manager::create_region_for_alloca (const frame_region2 *frame)
+{
+  region2 *reg = new alloca_region2 (alloc_region_id (), frame);
+  m_managed_dynamic_regions.safe_push (reg);
+  // TODO: selftest; verify no leak under valgrind
+  return reg;
+}
+
+/* FIXME.  */
+
+template <typename T>
+static void
+log_managed_object (logger *logger, const T *obj)
+{
+  logger->start_log_line ();
+  pretty_printer *pp = logger->get_printer ();
+  pp_string (pp, "    ");
+  obj->dump_to_pp (pp, true);
+  logger->end_log_line ();
+}
+
+/* FIXME.  */
+
+template <>
+void
+log_managed_object (logger *logger, const frame_region2 *obj)
+{
+  logger->start_log_line ();
+  pretty_printer *pp = logger->get_printer ();
+  pp_string (pp, "    ");
+  obj->dump_to_pp (pp, true);
+  pp_printf (pp, " [with %i region(s) for locals]", obj->get_num_locals ());
+  logger->end_log_line ();
+}
+
+/* FIXME.  */
+
+template <typename K, typename T>
+static void
+log_uniq_map (logger *logger, bool show_objs, const char *title,
+	      const hash_map<K, T*> &uniq_map)
+{
+  logger->log ("  # %s: %li", title, uniq_map.elements ());
+  if (show_objs)
+    for (typename hash_map<K, T*>::iterator iter = uniq_map.begin ();
+	 iter != uniq_map.end (); ++iter)
+      {
+	T *managed_obj = (*iter).second;
+	log_managed_object<T> (logger, managed_obj);
+      }
+}
+
+/* FIXME.  */
+
+template <typename T>
+static void
+log_uniq_manager (logger *logger, bool show_objs, const char *title,
+		  const uniq_manager<T> &uniq_mgr)
+{
+  logger->log ("  # %s: %li", title, uniq_mgr.get_count ());
+  if (show_objs)
+    for (typename uniq_manager<T>::iterator_t iter = uniq_mgr.begin ();
+	 iter != uniq_mgr.end (); ++iter)
+      {
+	T* managed_obj = (*iter).second;
+	log_managed_object<T> (logger, managed_obj);
+      }
+}
+
+/* FIXME.  */
+
+void
+region_model2_manager::log_stats (logger *logger, bool show_objs) const
+{
+  LOG_SCOPE (logger);
+  logger->log ("svalue2 consolidation");
+  log_uniq_map (logger, show_objs, "constant_svalue2", m_constants_map);
+  log_uniq_map (logger, show_objs, "unknown_svalue2", m_unknowns_map);
+  if (m_unknown_NULL)
+    log_managed_object (logger, m_unknown_NULL);
+  log_uniq_map (logger, show_objs, "poisoned_svalue2", m_poisoned_values_map);
+  log_uniq_map (logger, show_objs, "setjmp_svalue2", m_setjmp_values_map);
+  log_uniq_map (logger, show_objs, "initial_svalue2", m_initial_values_map);
+  log_uniq_map (logger, show_objs, "region_svalue2", m_pointer_values_map);
+  log_uniq_map (logger, show_objs, "unaryop_svalue2", m_unaryop_values_map);
+  log_uniq_map (logger, show_objs, "binop_svalue2", m_binop_values_map);
+  log_uniq_map (logger, show_objs, "sub_svalue2", m_sub_values_map);
+  log_uniq_map (logger, show_objs, "unmergeable_svalue2",
+		m_unmergeable_values_map);
+  log_uniq_map (logger, show_objs, "widening_svalue2", m_widening_values_map);
+  log_uniq_map (logger, show_objs, "compound_svalue2", m_compound_values_map);
+  log_uniq_map (logger, show_objs, "conjured_svalue2", m_conjured_values_map);
+  logger->log ("max accepted svalue num_nodes: %i",
+	       m_max_complexity.m_num_nodes);
+  logger->log ("max accepted svalue max_depth: %i",
+	       m_max_complexity.m_max_depth);
+
+  logger->log ("region2 consolidation");
+  logger->log ("  next region id: %i", m_next_region_id);
+  log_uniq_map (logger, show_objs, "function_region2", m_fndecls_map);
+  log_uniq_map (logger, show_objs, "label_region2", m_labels_map);
+  log_uniq_map (logger, show_objs, "decl_region2 for globals", m_globals_map);
+  log_uniq_manager (logger, show_objs, "field_region2", m_fields);
+  log_uniq_manager (logger, show_objs, "element_region2", m_elements);
+  log_uniq_manager (logger, show_objs, "offset_region2", m_offset_regions);
+  log_uniq_manager (logger, show_objs, "cast_region2", m_cast_regions);
+  log_uniq_manager (logger, show_objs, "frame_region2", m_frames);
+  log_uniq_manager (logger, show_objs, "symbolic_region2", m_symbolic_regions);
+  log_uniq_map (logger, show_objs, "string_region2", m_string_map);
+  logger->log ("  # managed dynamic regions: %i",
+	       m_managed_dynamic_regions.length ());
+  m_store2_mgr.log_stats (logger, show_objs);
+}
+
+/* FIXME.  */
+// FIXME: this is here so it can use log_uniq_manager
+
+void
+store2_manager::log_stats (logger *logger, bool show_objs) const
+{
+  LOG_SCOPE (logger);
+  log_uniq_manager (logger, show_objs, "concrete_binding2",
+		    m_concrete_binding_key_mgr);
+  log_uniq_manager (logger, show_objs, "symbolic_binding2",
+		    m_symbolic_binding_key_mgr);
+}
+
+/* class region_model2.  */
+
+/* FIXME: region_model2's default ctor.  */
+
+region_model2::region_model2 (region_model2_manager *mgr)
+: m_mgr (mgr), m_store (), m_current_frame (NULL)
+{
+  //m_root_reg = add_region2 (new root_region2 ());
+  m_constraints2 = new constraint_manager2 (mgr);
+  // TODO
+}
+
+/* region_model2's copy ctor.  */
+
+region_model2::region_model2 (const region_model2 &other)
+: m_mgr (other.m_mgr), m_store (other.m_store),
+  m_constraints2 (new constraint_manager2 (*other.m_constraints2)),
+  m_current_frame (other.m_current_frame)
+{
+}
+
+/* region_model2's dtor.  */
+
+region_model2::~region_model2 ()
+{
+  // empty?
+  delete m_constraints2;
+}
+
+/* region_model2's assignment operator.  */
+
+region_model2 &
+region_model2::operator= (const region_model2 &other)
+{
+  /* m_mgr is const.  */
+  gcc_assert (m_mgr == other.m_mgr);
+
+  m_store = other.m_store;
+
+  delete m_constraints2;
+  m_constraints2 = new constraint_manager2 (*other.m_constraints2);
+
+  m_current_frame = other.m_current_frame;
+
+  return *this;
+}
+
+/* Equality operator for region_model2.
+
+   FIXME:
+   Amongst other things this directly compares the svalue2 and region2
+   vectors and so for this to be meaningful both this and OTHER should
+   have been canonicalized.  */
+
+bool
+region_model2::operator== (const region_model2 &other) const
+{
+  /* We can only compare instances that use the same manager.  */
+  gcc_assert (m_mgr == other.m_mgr);
+
+  if (m_store != other.m_store)
+    return false;
+
+  if (*m_constraints2 != *other.m_constraints2)
+    return false;
+
+  if (m_current_frame != other.m_current_frame)
+    return false;
+
+  //gcc_checking_assert (hash () == other.hash ());
+
+  return true;
+}
+
+/* Generate a hash value for this region_model2.  */
+
+#if 0
+hashval_t
+region_model2::hash () const
+{
+  hashval_t result = 0;
+  int i;
+
+  const svalue2 *svalue2;
+  FOR_EACH_VEC_ELT (m_svalue2s, i, svalue2)
+    result ^= svalue2->hash ();
+
+  region2 *region2;
+  FOR_EACH_VEC_ELT (m_region2s, i, region2)
+    result ^= region2->hash ();
+
+  result ^= m_constraints->hash ();
+
+  return result;
+}
+
+/* Print an all-on-one-line representation of this region_model2 to PP,
+   which must support %E for trees.  */
+
+void
+region_model2::print (pretty_printer *pp) const
+{
+  int i;
+
+  pp_string (pp, "svalue2s: [");
+  const svalue2 *svalue2;
+  FOR_EACH_VEC_ELT (m_svalue2s, i, svalue2)
+    {
+      if (i > 0)
+	pp_string (pp, ", ");
+      print_svalue2 (svalue2_id::from_int (i), pp);
+    }
+
+  pp_string (pp, "], region2s: [");
+
+  region2 *region2;
+  FOR_EACH_VEC_ELT (m_region2s, i, region2)
+    {
+      if (i > 0)
+	pp_string (pp, ", ");
+      region2->print (*this, region2_id::from_int (i), pp);
+    }
+
+  pp_string (pp, "], constraints: ");
+
+  m_constraints->print (pp);
+}
+
+/* Print the svalue2 with id SID to PP.  */
+
+void
+region_model2::print_svalue2 (const svalue2 *sval, pretty_printer *pp) const
+{
+  get_svalue2 (sid)->print (*this, sid, pp);
+}
+
+/* Dump a .dot representation of this region_model2 to PP, showing
+   the values and the hierarchy of region2s.  */
+
+void
+region_model2::dump_dot_to_pp (pretty_printer *pp) const
+{
+  graphviz_out gv (pp);
+
+  pp_string (pp, "digraph \"");
+  pp_write_text_to_stream (pp);
+  pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/false);
+  pp_string (pp, "\" {\n");
+
+  gv.indent ();
+
+  pp_string (pp, "overlap=false;\n");
+  pp_string (pp, "compound=true;\n");
+
+  int i;
+
+  const svalue2 *svalue2;
+  FOR_EACH_VEC_ELT (m_svalue2s, i, svalue2)
+    svalue2->dump_dot_to_pp (*this, svalue2_id::from_int (i), pp);
+
+  region2 *region2;
+  FOR_EACH_VEC_ELT (m_region2s, i, region2)
+    region2->dump_dot_to_pp (*this, region2_id::from_int (i), pp);
+
+  /* TODO: constraints.  */
+
+  /* Terminate "digraph" */
+  gv.outdent ();
+  pp_string (pp, "}");
+  pp_newline (pp);
+}
+
+/* Dump a .dot representation of this region_model2 to FP.  */
+
+void
+region_model2::dump_dot_to_file (FILE *fp) const
+{
+  pretty_printer pp;
+  pp_format_decoder (&pp) = default_tree_printer;
+  pp.buffer->stream = fp;
+  dump_dot_to_pp (&pp);
+  pp_flush (&pp);
+}
+
+/* Dump a .dot representation of this region_model2 to PATH.  */
+
+void
+region_model2::dump_dot (const char *path) const
+{
+  FILE *fp = fopen (path, "w");
+  dump_dot_to_file (fp);
+  fclose (fp);
+}
+#endif
+
+// FIXME:
+/* Dump a representation of this model to PP, showing the
+   region2 hierarchy, the svalue2s, and any constraints.
+
+   If SIMPLE is true, show only the most pertinent information,
+   in a form that attempts to be less verbose.
+   Otherwise, show all information.
+
+   If MULTILINE... FIXME.  */
+
+void
+region_model2::dump_to_pp (pretty_printer *pp, bool simple,
+			   bool multiline) const
+{
+  /* Dump stack.  */
+  pp_printf (pp, "stack depth: %i", get_stack_depth ());
+  if (multiline)
+    pp_newline (pp);
+  else
+    pp_string (pp, " {");
+  for (const frame_region2 *iter_frame = m_current_frame; iter_frame;
+       iter_frame = iter_frame->get_calling_frame ())
+    {
+      if (multiline)
+	pp_string (pp, "  ");
+      else if (iter_frame != m_current_frame)
+	pp_string (pp, ", ");
+      pp_printf (pp, "frame (index %i): ", iter_frame->get_index ());
+      iter_frame->dump_to_pp (pp, simple);
+      if (multiline)
+	pp_newline (pp);
+    }
+  if (!multiline)
+    pp_string (pp, "}");
+
+  /* Dump store.  */
+  if (!multiline)
+    pp_string (pp, ", {");
+  m_store.dump_to_pp (pp, simple, multiline,
+		      m_mgr->get_store2_manager ());
+  if (!multiline)
+    pp_string (pp, "}");
+
+  /* Dump constraints.  */
+  pp_string (pp, "constraint_manager2:");
+  if (multiline)
+    pp_newline (pp);
+  else
+    pp_string (pp, " {");
+  m_constraints2->dump_to_pp (pp, multiline);
+  if (!multiline)
+    pp_string (pp, "}");
+}
+
+/* Dump a representation of this model to FILE.  */
+
+void
+region_model2::dump (FILE *fp, bool simple, bool multiline) const
+{
+  pretty_printer pp;
+  pp_format_decoder (&pp) = default_tree_printer;
+  pp_show_color (&pp) = pp_show_color (global_dc->printer);
+  pp.buffer->stream = fp;
+  dump_to_pp (&pp, simple, multiline);
+  pp_newline (&pp);
+  pp_flush (&pp);
+}
+
+/* Dump a multiline representation of this model to stderr.  */
+
+DEBUG_FUNCTION void
+region_model2::dump (bool simple) const
+{
+  dump (stderr, simple, true);
+}
+
+/* Dump a summary of this model to PP.  */
+
+void
+region_model2::summarize_to_pp (pretty_printer *pp, bool simple) const
+{
+  m_store.summarize_to_pp (pp, simple);
+
+  // FIXME:
+#if 0
+  /* Dump constraints.  */
+  pp_string (pp, "constraint_manager2:");
+  if (multiline)
+    pp_newline (pp);
+  else
+    pp_string (pp, " {");
+  m_constraints2->dump_to_pp (pp, multiline);
+  if (!multiline)
+    pp_string (pp, "}");
+#endif
+}
+
+/* Dump a summary of this model to stderr.  */
+
+DEBUG_FUNCTION void
+region_model2::summarize (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;
+  summarize_to_pp (&pp, simple);
+  pp_newline (&pp);
+  pp_flush (&pp);
+}
+
+/* Dump a multiline representation of this model to stderr.  */
+
+DEBUG_FUNCTION void
+region_model2::debug () const
+{
+  dump (true);
+}
+
+#if 0
+/* Dump VEC to PP, in the form "{VEC elements}: LABEL".  */
+
+static void
+dump_vec_of_tree (pretty_printer *pp,
+		  bool *is_first,
+		  const auto_vec<tree> &vec,
+		  const char *label)
+{
+  if (vec.length () == 0)
+    return;
+
+  dump_separator (pp, is_first);
+  pp_printf (pp, "{");
+  unsigned i;
+  tree key;
+  FOR_EACH_VEC_ELT (vec, i, key)
+    {
+      if (i > 0)
+	pp_string (pp, ", ");
+      dump_tree (pp, key);
+    }
+  pp_printf (pp, "}: %s", label);
+}
+
+/* Dump all *REP_PATH_VARS to PP in compact form, updating *IS_FIRST.
+   Subroutine of region_model2::dump_to_pp.  */
+
+void
+region_model2::dump_summary_of_rep_path_vars (pretty_printer *pp,
+					     auto_vec<path_var> *rep_path_vars,
+					     bool *is_first)
+{
+  /* Print pointers, constants, and poisoned values that aren't "uninit";
+     gather keys for unknown and uninit values.  */
+  unsigned i;
+  path_var *pv;
+  auto_vec<tree> unknown_trees;
+  auto_vec<tree> uninit_trees;
+  FOR_EACH_VEC_ELT (*rep_path_vars, i, pv)
+    {
+      if (TREE_CODE (pv->m_tree) == STRING_CST)
+	continue;
+      tentative_region_model2_context ctxt;
+      region2_id child_reg = get_lvalue (*pv, &ctxt);
+      if (ctxt.had_errors_p ())
+	continue;
+      region2 *child_region2 = get_region2 (child_reg);
+      if (!child_region2)
+	continue;
+      const svalue2 *sval = child_region2->get_value_direct ();
+      if (sid.null_p ())
+	continue;
+      const svalue2 *sval = get_svalue2 (sid);
+      switch (sval->get_kind ())
+	{
+	default:
+	  gcc_unreachable ();
+	case SK_REGION2:
+	  {
+	    const region_svalue2 *region2_sval
+	      = as_a <const region_svalue2 *> (sval);
+	    region2_id pointee_reg = region2_sval->get_pointee ();
+	    gcc_assert (!pointee_reg.null_p ());
+	    tree pointee = get_representative_path_var (pointee_reg).m_tree;
+	    dump_separator (pp, is_first);
+	    dump_tree (pp, pv->m_tree);
+	    pp_string (pp, ": ");
+	    pp_character (pp, '&');
+	    if (pointee)
+	      dump_tree (pp, pointee);
+	    else
+	      pointee_reg.print (pp);
+	  }
+	  break;
+	case SK_CONSTANT:
+	  dump_separator (pp, is_first);
+	  dump_tree (pp, pv->m_tree);
+	  pp_string (pp, ": ");
+	  dump_tree (pp, sval->dyn_cast_constant_svalue2 ()->get_constant ());
+	  break;
+	case SK_UNKNOWN:
+	  unknown_trees.safe_push (pv->m_tree);
+	  break;
+	case SK_POISONED:
+	  {
+	    const poisoned_svalue2 *poisoned_sval
+	      = as_a <const poisoned_svalue2 *> (sval);
+	    enum poison_kind pkind = poisoned_sval->get_poison_kind ();
+	    if (pkind == POISON_KIND_UNINIT)
+	      uninit_trees.safe_push (pv->m_tree);
+	    else
+	      {
+		dump_separator (pp, is_first);
+		dump_tree (pp, pv->m_tree);
+		pp_printf (pp, ": %s", poison_kind_to_str (pkind));
+	      }
+	  }
+	  break;
+	case SK_SETJMP:
+	  dump_separator (pp, is_first);
+	  pp_printf (pp, "setjmp: EN: %i",
+		     sval->dyn_cast_setjmp_svalue2 ()->get_enode_index ());
+	  break;
+	}
+    }
+
+  /* Print unknown and uninitialized values in consolidated form.  */
+  dump_vec_of_tree (pp, is_first, unknown_trees, "unknown");
+  dump_vec_of_tree (pp, is_first, uninit_trees, "uninit");
+}
+#endif
+
+#if 0
+
+/* Assert that this object is valid.  */
+
+void
+region_model2::validate () const
+{
+  /* Skip this in a release build.  */
+#if !CHECKING_P
+  return;
+#endif
+
+  m_constraints->validate ();
+
+  unsigned i;
+  region2 *r;
+  FOR_EACH_VEC_ELT (m_region2s, i, r)
+    r->validate (*this);
+
+  // TODO: anything else?
+
+  /* Verify that the stack region2 (if any) has an "uninitialized" value.  */
+  region2 *stack_region2 = get_root_region2 ()->get_stack_region2 (this);
+  if (stack_region2)
+    {
+      svalue2_id stack_value_sval = stack_region2->get_value_direct ();
+      const svalue2 *stack_value = get_svalue2 (stack_value_sval);
+      gcc_assert (stack_value->get_kind () == SK_POISONED);
+      poisoned_svalue2 *subclass = stack_value->dyn_cast_poisoned_svalue2 ();
+      gcc_assert (subclass);
+      gcc_assert (subclass->get_poison_kind () == POISON_KIND_UNINIT);
+    }
+}
+
+#endif
+
+/* FIXME.  */
+
+void
+region_model2::canonicalize ()
+{
+  m_store.canonicalize (m_mgr->get_store2_manager ());
+  m_constraints2->canonicalize ();
+}
+
+/* FIXME.  */
+
+bool
+region_model2::canonicalized_p () const
+{
+  region_model2 copy (*this);
+  copy.canonicalize ();
+  return *this == copy;
+}
+
+/* FIXME.  */
+
+void
+region_model2::loop_replay_hack (const region_model2 *dst_state)
+{
+  m_store.loop_replay_hack (dst_state->get_store (), m_mgr);
+}
+
+/* FIXME.  */
+
+bool
+region_model2::on_stmt (const gimple *stmt,
+			region_model2_context *ctxt)
+{
+  switch (stmt->code)
+    {
+    case GIMPLE_CALL:
+      gcc_unreachable ();
+      //return on_gcall (snode, as_a <const gcall *> (stmt));
+
+    case GIMPLE_COND:
+      /* This is a no-op; the condition is handled by the true/false
+	 superedges.  */
+      return true;
+
+    case GIMPLE_GOTO:
+      /* TODO: GIMPLE_GOTO needs to be handled by the engine, not the
+	 region_model2, but I don't think this is a regression relative to
+	 the old implementation.  */
+    case GIMPLE_SWITCH:
+    case GIMPLE_LABEL:
+      /* No-op.  */
+      return true;
+
+    case GIMPLE_ASSIGN:
+      return on_gassign (as_a <const gassign *> (stmt), ctxt);
+
+    case GIMPLE_RETURN:
+      return on_greturn (as_a <const greturn *> (stmt), ctxt);
+
+    default:
+      sorry_at (stmt->location, "unhandled stmt: %qs",
+		gimple_code_name[stmt->code]);
+      return false;
+    }
+}
+
+/* If ASSIGN is a stmt that can be modelled via
+     set_value (lhs_reg, SVALUE, CTXT)
+   for some SVALUE, get the SVALUE.
+   Otherwise return NULL.  */
+
+const svalue2 *
+region_model2::get_gassign_result (const gassign *assign,
+				   region_model2_context *ctxt)
+{
+  tree lhs = gimple_assign_lhs (assign);
+  tree rhs1 = gimple_assign_rhs1 (assign);
+  enum tree_code op = gimple_assign_rhs_code (assign);
+  switch (op)
+    {
+    default:
+      return NULL;
+
+    case POINTER_PLUS_EXPR:
+      {
+	/* e.g. "_1 = a_10(D) + 12;" */
+	tree ptr = rhs1;
+	tree offset = gimple_assign_rhs2 (assign);
+
+	const svalue2 *ptr_sval = get_rvalue (ptr, ctxt);
+	const svalue2 *offset_sval = get_rvalue (offset, ctxt);
+	/* Quoting tree.def, "the second operand [of a POINTER_PLUS_EXPR]
+	   is an integer of type sizetype".  */
+	offset_sval = m_mgr->get_or_create_cast (size_type_node, offset_sval);
+
+	const svalue2 *sval_binop
+	  = m_mgr->get_or_create_binop (TREE_TYPE (lhs), op,
+					ptr_sval, offset_sval);
+	return sval_binop;
+      }
+      break;
+
+    case POINTER_DIFF_EXPR:
+      {
+	/* e.g. "_1 = p_2(D) - q_3(D);".  */
+	tree rhs2 = gimple_assign_rhs2 (assign);
+	const svalue2 *rhs1_sval = get_rvalue (rhs1, ctxt);
+	const svalue2 *rhs2_sval = get_rvalue (rhs2, ctxt);
+
+	// TODO: perhaps fold to zero if they're known to be equal?
+
+	const svalue2 *sval_binop
+	  = m_mgr->get_or_create_binop (TREE_TYPE (lhs), op,
+					rhs1_sval, rhs2_sval);
+	return sval_binop;
+      }
+      break;
+
+    /* Assignments of the form
+	set_value (lvalue (LHS), rvalue (EXPR))
+       for various EXPR.
+       We already have the lvalue for the LHS above, as "lhs_reg".  */
+    case ADDR_EXPR: /* LHS = &RHS;  */
+    case BIT_FIELD_REF:
+    case COMPONENT_REF: /* LHS = op0.op1;  */
+    case MEM_REF:
+    case REAL_CST:
+    case COMPLEX_CST:
+    case VECTOR_CST:
+    case INTEGER_CST:
+    case ARRAY_REF:
+    case SSA_NAME: /* LHS = VAR; */
+    case VAR_DECL: /* LHS = VAR; */
+    case PARM_DECL:/* LHS = VAR; */
+    case REALPART_EXPR:
+    case IMAGPART_EXPR:
+      return get_rvalue (rhs1, ctxt);
+
+    case ABS_EXPR:
+    case ABSU_EXPR:
+    case CONJ_EXPR:
+    case BIT_NOT_EXPR:
+    case FIX_TRUNC_EXPR:
+    case FLOAT_EXPR:
+    case NEGATE_EXPR:
+    case NOP_EXPR:
+    case VIEW_CONVERT_EXPR:
+      {
+	/* Unary ops.  */
+	const svalue2 *rhs_sval = get_rvalue (rhs1, ctxt);
+	const svalue2 *sval_unaryop
+	  = m_mgr->get_or_create_unaryop (TREE_TYPE (lhs), op, rhs_sval);
+	return sval_unaryop;
+      }
+
+    case EQ_EXPR:
+    case GE_EXPR:
+    case LE_EXPR:
+    case NE_EXPR:
+    case GT_EXPR:
+    case LT_EXPR:
+    case UNORDERED_EXPR:
+    case ORDERED_EXPR:
+      {
+	tree rhs2 = gimple_assign_rhs2 (assign);
+
+	// TODO: constraints between svalue2s
+	const svalue2 *rhs1_sval = get_rvalue (rhs1, ctxt);
+	const svalue2 *rhs2_sval = get_rvalue (rhs2, ctxt);
+
+	tristate t = eval_condition (rhs1_sval, op, rhs2_sval);
+	if (t.is_known ())
+	  return get_rvalue (t.is_true ()
+			     ? boolean_true_node
+			     : boolean_false_node,
+			     ctxt);
+	else
+	  {
+	    // TODO: symbolic value for binop
+	    const svalue2 *sval_binop
+	      = m_mgr->get_or_create_binop (TREE_TYPE (lhs), op,
+					    rhs1_sval, rhs2_sval);
+	    return sval_binop;
+	  }
+      }
+      break;
+
+    case PLUS_EXPR:
+    case MINUS_EXPR:
+    case MULT_EXPR:
+    case MULT_HIGHPART_EXPR:
+    case TRUNC_DIV_EXPR:
+    case CEIL_DIV_EXPR:
+    case FLOOR_DIV_EXPR:
+    case ROUND_DIV_EXPR:
+    case TRUNC_MOD_EXPR:
+    case CEIL_MOD_EXPR:
+    case FLOOR_MOD_EXPR:
+    case ROUND_MOD_EXPR:
+    case RDIV_EXPR:
+    case EXACT_DIV_EXPR:
+    case LSHIFT_EXPR:
+    case RSHIFT_EXPR:
+    case LROTATE_EXPR:
+    case RROTATE_EXPR:
+    case BIT_IOR_EXPR:
+    case BIT_XOR_EXPR:
+    case BIT_AND_EXPR:
+    case MIN_EXPR:
+    case MAX_EXPR:
+    case COMPLEX_EXPR:
+      {
+	/* Binary ops.  */
+	tree rhs2 = gimple_assign_rhs2 (assign);
+
+	const svalue2 *rhs1_sval = get_rvalue (rhs1, ctxt);
+	const svalue2 *rhs2_sval = get_rvalue (rhs2, ctxt);
+
+	const svalue2 *sval_binop
+	  = m_mgr->get_or_create_binop (TREE_TYPE (lhs), op,
+					rhs1_sval, rhs2_sval);
+	return sval_binop;
+      }
+
+    /* Vector expressions.  In theory we could implement these elementwise,
+       but for now, simply return unknown values.  */
+    case VEC_DUPLICATE_EXPR:
+    case VEC_SERIES_EXPR:
+    case VEC_COND_EXPR:
+    case VEC_PERM_EXPR:
+      return m_mgr->get_or_create_unknown_svalue2 (TREE_TYPE (lhs));
+    }
+}
+
+// FIXME:
+/* Update this model for the ASSIGN stmt, using CTXT to report any
+   diagnostics.  */
+
+bool
+region_model2::on_gassign (const gassign *assign,
+			   region_model2_context *ctxt)
+{
+  tree lhs = gimple_assign_lhs (assign);
+  tree rhs1 = gimple_assign_rhs1 (assign);
+
+  const region2 *lhs_reg = get_lvalue (lhs, ctxt);
+
+  /* Most assignments are handled by:
+       set_value (lhs_reg, SVALUE, CTXT)
+     for some SVALUE.  */
+  if (const svalue2 *sval = get_gassign_result (assign, ctxt))
+    {
+      set_value (lhs_reg, sval, ctxt);
+      return true;
+    }
+
+  enum tree_code op = gimple_assign_rhs_code (assign);
+  switch (op)
+    {
+    default:
+      {
+	if (1)
+	  sorry_at (assign->location, "unhandled assignment op: %qs",
+		    get_tree_code_name (op));
+	gcc_unreachable ();
+	//set_to_new_unknown_value (lhs_reg, TREE_TYPE (lhs), ctxt);
+      }
+      break;
+
+    case CONSTRUCTOR:
+      {
+	if (TREE_CLOBBER_P (rhs1))
+	  {
+	    /* e.g. "x ={v} {CLOBBER};"  */
+	    /* TODO: should we remove the value, or replace it with an
+	       "unknown" value?  */
+	    clobber_region (lhs_reg);
+	  }
+	else
+	  {
+	    /* Any CONSTRUCTOR that survives to this point is either
+	       just a zero-init of everything, or a vector.  */
+	    if (!CONSTRUCTOR_NO_CLEARING (rhs1))
+	      zero_fill_region (lhs_reg);
+	    unsigned ix;
+	    tree index;
+	    tree val;
+	    FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (rhs1), ix, index, val)
+	      {
+		/* FIXME: this code assumes we're dealing with a vector.  */
+		gcc_assert (TREE_CODE (TREE_TYPE (rhs1)) == VECTOR_TYPE);
+		if (!index)
+		  index = build_int_cst (integer_type_node, ix);
+		gcc_assert (TREE_CODE (index) == INTEGER_CST);
+		const svalue2 *index_sval
+		  = m_mgr->get_or_create_constant_svalue2 (index);
+		gcc_assert (index_sval);
+		const region2 *sub_reg
+		  = m_mgr->get_element_region (lhs_reg,
+					       TREE_TYPE (val),
+					       index_sval);
+		const svalue2 *val_sval = get_rvalue (val, ctxt);
+		set_value (sub_reg, val_sval, ctxt);
+	      }
+	  }
+      }
+      break;
+
+    case STRING_CST:
+      {
+	/* e.g. "struct s2 x = {{'A', 'B', 'C', 'D'}};".  */
+	/* Add a default binding, rather than a direct one, so that array
+	   access will "inherit" the individual chars.  */
+	const svalue2 *rhs_sval = get_rvalue (rhs1, ctxt);
+	m_store.set_value (m_mgr->get_store2_manager(), lhs_reg, rhs_sval,
+			   BK_default);
+      }
+      break;
+    }
+
+  return true;
+}
+
+/* FIXME.  */
+#if 0
+bool
+region_model2::on_gcall (const supernode *snode,
+			 const gcall *call)
+{
+  region_model2_context *ctxt = NULL; // FIXME
+
+  bool unknown_effects = false;
+
+  /* Debugging/test support.  */
+  if (is_special_named_call_p (call, "__analyzer_dump", 0))
+    {
+      /* Handle the builtin "__analyzer_dump" by dumping state
+	 to stderr.  */
+      dump (false);
+      dump (true);
+    }
+  else if (is_special_named_call_p (call, "__analyzer_eval", 1))
+    {
+      /* Handle the builtin "__analyzer_eval" by evaluating the input
+	 and dumping as a dummy warning, so that test cases can use
+	 dg-warning to validate the result (and so unexpected warnings will
+	 lead to DejaGnu failures).  */
+      tree t_arg = gimple_call_arg (call, 0);
+      tristate t
+	= eval_condition (t_arg,
+			  NE_EXPR,
+			  integer_zero_node,
+			  NULL/*&ctxt*/); // FIXME
+      warning_at (call->location, 0, "%s", t.as_string ());
+      return true;
+    }
+
+  if (tree callee_fndecl = get_fndecl_for_call (call, ctxt))
+    {
+      /* Get any left-hand side for the destination of the result.  */
+      region2 *lhs_reg = NULL;
+      tree lhs_type = NULL_TREE;
+      if (tree lhs = gimple_call_lhs (call))
+	{
+	  lhs_reg = get_lvalue (lhs, ctxt);
+	  lhs_type = TREE_TYPE (lhs);
+	}
+
+      if (is_named_call_p (callee_fndecl, "malloc", call, 1))
+	{
+	  tree size_arg = gimple_call_arg (call, 0);
+	  const svalue2 *size_sval = get_rvalue (size_arg, ctxt);
+	  region2 *new_reg = create_region_for_heap_alloc (size_sval);
+	  if (lhs_reg)
+	    {
+	      const svalue2 *ptr_sval = m_mgr->get_ptr_svalue2 (lhs_type, new_reg);
+	      set_value (lhs_reg, ptr_sval, ctxt);
+	    }
+	  else
+	    {
+	      // FIXME: report a leak here?
+	    }
+	  return true;
+	}
+      else if (is_named_call_p (callee_fndecl, "__builtin_alloca", call, 1))
+	{
+	  tree size_arg = gimple_call_arg (call, 0);
+	  const svalue2 *size_sval = get_rvalue (size_arg, ctxt);
+	  region2 *new_reg = create_region_for_alloca (size_sval);
+	  if (lhs_reg)
+	    {
+	      const svalue2 *ptr_sval = m_mgr->get_ptr_svalue2 (lhs_type, new_reg);
+	      set_value (lhs_reg, ptr_sval, ctxt);
+	    }
+	  return true;
+	}
+      else if (is_named_call_p (callee_fndecl, "strlen", call, 1))
+	{
+	  tree arg = gimple_call_arg (call, 0);
+	  const svalue2 *arg_sval = get_rvalue (arg, ctxt);
+	  region2 *buf_reg = deref_rvalue (arg_sval, ctxt);
+	  if (string_region2 *str_reg = buf_reg->dyn_cast_string_region2 ())
+	    {
+	      tree str_cst = str_reg->get_string_cst ();
+	      /* TREE_STRING_LENGTH is sizeof, not strlen.  */
+	      int sizeof_cst = TREE_STRING_LENGTH (str_cst);
+	      int strlen_cst = sizeof_cst - 1;
+	      tree t_cst = build_int_cst (lhs_type, strlen_cst);
+	      const svalue2 *result_sval
+		= m_mgr->get_or_create_constant_svalue2 (t_cst);
+	      set_value (lhs_reg, result_sval, ctxt);
+	      return true;
+	    }
+	  /* Otherwise an unknown value.  */
+	  return true;
+	}
+
+      if (!fndecl_has_gimple_body_p (callee_fndecl)
+	  && !DECL_PURE_P (callee_fndecl))
+	unknown_effects = true;
+    }
+  else
+    unknown_effects = true;
+
+  if (unknown_effects)
+    handle_unrecognized_call (call, ctxt);
+
+  return true;
+}
+#endif
+
+/* A subclass of pending_diagnostic for complaining about uses of
+   poisoned values.  */
+
+class poisoned_value_diagnostic2
+: public pending_diagnostic_subclass<poisoned_value_diagnostic2>
+{
+public:
+  poisoned_value_diagnostic2 (tree expr, enum poison_kind pkind)
+  : m_expr (expr), m_pkind (pkind)
+  {}
+
+  const char *get_kind () const FINAL OVERRIDE { return "poisoned_value_diagnostic2"; }
+
+  bool operator== (const poisoned_value_diagnostic2 &other) const
+  {
+    return m_expr == other.m_expr;
+  }
+
+  bool emit (rich_location *rich_loc) FINAL OVERRIDE
+  {
+    switch (m_pkind)
+      {
+      default:
+	gcc_unreachable ();
+      case POISON_KIND_FREED:
+	{
+	  diagnostic_metadata m;
+	  m.add_cwe (416); /* "CWE-416: Use After Free".  */
+	  return warning_meta (rich_loc, m,
+			       OPT_Wanalyzer_use_after_free,
+			       "use after %<free%> of %qE",
+			       m_expr);
+	}
+	break;
+      case POISON_KIND_POPPED_STACK:
+	{
+	  /* TODO: which CWE?  */
+	  return warning_at
+	    (rich_loc,
+	     OPT_Wanalyzer_use_of_pointer_in_stale_stack_frame,
+	     "dereferencing pointer %qE to within stale stack frame",
+	     m_expr);
+	}
+	break;
+      }
+  }
+
+  label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
+  {
+    switch (m_pkind)
+      {
+      default:
+	gcc_unreachable ();
+      case POISON_KIND_FREED:
+	return ev.formatted_print ("use after %<free%> of %qE here",
+				   m_expr);
+      case POISON_KIND_POPPED_STACK:
+	return ev.formatted_print
+	  ("dereferencing pointer %qE to within stale stack frame",
+	   m_expr);
+      }
+  }
+
+private:
+  tree m_expr;
+  enum poison_kind m_pkind;
+};
+
+/* Update this model for the CALL stmt, using CTXT to report any
+   diagnostics - the first half.
+
+   Updates to the region_model2 that should be made *before* sm-states
+   are updated are done here; other updates to the region_model2 are done
+   in region_model2::on_call_post.
+
+   Return true if the function call has unknown side effects (it wasn't
+   recognized and we don't have a body for it, or are unable to tell which
+   fndecl it is).  */
+
+bool
+region_model2::on_call_pre (const gcall *call, region_model2_context *ctxt)
+{
+  /* Check for uses of poisoned values.
+     For now, special-case "free", to avoid warning about "use-after-free"
+     when "double free" would be more precise.  */
+#if 0
+  if (!is_special_named_call_p (call, "free", 1))
+    for (unsigned i = 0; i < gimple_call_num_args (call); i++)
+      check_for_poison (gimple_call_arg (call, i), ctxt);
+#endif
+
+  bool unknown_side_effects = false;
+
+  if (tree callee_fndecl = get_fndecl_for_call (call, ctxt))
+    {
+      call_details cd (call, this, ctxt);
+
+      /* The various impl_call_* member functions are implemented
+	 in region-model2-impl-calls.cc.
+	 Having them split out into separate functions makes it easier
+	 to put breakpoints on the handling of specific functions.  */
+
+      /* FIXME: should we be returning bools from these,
+	 or dropping through?
+	 Existing behavior was to return "true", but that looks wrong.  */
+      if (is_named_call_p (callee_fndecl, "malloc", call, 1))
+	return impl_call_malloc (cd);
+      else if (is_named_call_p (callee_fndecl, "calloc", call, 2))
+	return impl_call_calloc (cd);
+      else if (is_named_call_p (callee_fndecl, "__builtin_alloca", call, 1))
+	return impl_call_alloca (cd);
+      else if (gimple_call_builtin_p (call, BUILT_IN_EXPECT)
+	       || gimple_call_builtin_p (call, BUILT_IN_EXPECT_WITH_PROBABILITY)
+	       || gimple_call_internal_p (call, IFN_BUILTIN_EXPECT))
+	return impl_call_builtin_expect (cd);
+      else if (is_named_call_p (callee_fndecl, "memset", call, 3)
+	       || gimple_call_builtin_p (call, BUILT_IN_MEMSET))
+	{
+	  impl_call_memset (cd);
+	  return false;
+	}
+      else if (is_named_call_p (callee_fndecl, "strlen", call, 1))
+	{
+	  if (impl_call_strlen (cd))
+	    return false;
+	}
+      else if (!fndecl_has_gimple_body_p (callee_fndecl)
+	       && !DECL_PURE_P (callee_fndecl)
+	       && !fndecl_built_in_p (callee_fndecl))
+	unknown_side_effects = true;
+    }
+  else
+    unknown_side_effects = true;
+
+  /* Some of the above cases update the lhs of the call based on the
+     return value.  If we get here, it hasn't been done yet, so do that
+     now.  */
+  if (tree lhs = gimple_call_lhs (call))
+    {
+      const region2 *lhs_region = get_lvalue (lhs, ctxt);
+      // FIXME
+      if (TREE_CODE (lhs) == SSA_NAME)
+	{
+	  const svalue2 *sval = m_mgr->get_or_create_initial_value (lhs_region);
+	  set_value (lhs_region, sval, ctxt);
+	}
+    }
+
+#if 0
+  if (lhs_reg)
+    set_to_new_unknown_value (lhs_reg, lhs_type, ctxt);
+#endif
+
+  return unknown_side_effects;
+}
+
+/* Update this model for the CALL stmt, using CTXT to report any
+   diagnostics - the second half.
+
+   Updates to the region_model2 that should be made *after* sm-states
+   are updated are done here; other updates to the region_model2 are done
+   in region_model2::on_call_pre.
+
+   If UNKNOWN_SIDE_EFFECTS is true, also call handle_unrecognized_call
+   to purge state.  */
+
+void
+region_model2::on_call_post (const gcall *call,
+			     bool unknown_side_effects,
+			     region_model2_context *ctxt)
+{
+  if (tree callee_fndecl = get_fndecl_for_call (call, ctxt))
+    if (is_named_call_p (callee_fndecl, "free", call, 1))
+      {
+	call_details cd (call, this, ctxt);
+	impl_call_free (cd);
+	return;
+      }
+
+  if (unknown_side_effects)
+    handle_unrecognized_call (call, ctxt);
+}
+
+/* Handle a call CALL to a function with unknown behavior.
+
+   Traverse the region2s in this model, determining what region2s are
+   reachable from pointer arguments to CALL and from global variables,
+   recursively.
+
+   Set all reachable region2s to new unknown values and purge sm-state
+   from their values, and from values that point to them.  */
+void
+region_model2::handle_unrecognized_call (const gcall *call,
+					 region_model2_context *ctxt)
+{
+  tree fndecl = get_fndecl_for_call (call, ctxt);
+
+  reachable_region2s reachable_regs (&m_store, m_mgr);
+
+  /* Determine the reachable region2s and their mutability.  */
+  {
+    /* Add globals and regions that already escaped in previous
+       unknown calls.  */
+    m_store.for_each_cluster (reachable_region2s::init_cluster_cb,
+			      &reachable_regs);
+
+    /* Params that are pointers.  */
+    tree iter_param_types = NULL_TREE;
+    if (fndecl)
+      iter_param_types = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
+    for (unsigned arg_idx = 0; arg_idx < gimple_call_num_args (call); arg_idx++)
+      {
+	/* Track expected param type, where available.  */
+	tree param_type = NULL_TREE;
+	if (iter_param_types)
+	  {
+	    param_type = TREE_VALUE (iter_param_types);
+	    gcc_assert (param_type);
+	    iter_param_types = TREE_CHAIN (iter_param_types);
+	  }
+
+	tree parm = gimple_call_arg (call, arg_idx);
+	const svalue2 *parm_sval = get_rvalue (parm, ctxt);
+	reachable_regs.handle_parm (parm_sval, param_type);
+	// FIXME: what about compound parms that contain ptrs?
+      }
+  }
+
+  /* Purge sm-state for the svalues that were reachable,
+     both in non-mutable and mutable form.  */
+  for (hash_set<const svalue2 *>::iterator iter
+	 = reachable_regs.begin_reachable_svals ();
+       iter != reachable_regs.end_reachable_svals (); ++iter)
+    {
+      const svalue2 *sval = (*iter);
+      ctxt->on_unknown_change (sval, false);
+    }
+  for (hash_set<const svalue2 *>::iterator iter
+	 = reachable_regs.begin_mutable_svals ();
+       iter != reachable_regs.end_mutable_svals (); ++iter)
+    {
+      const svalue2 *sval = (*iter);
+      ctxt->on_unknown_change (sval, true);
+    }
+
+  /* Mark any clusters that have escaped.  */
+  reachable_regs.mark_escaped_clusters (m_mgr->get_store2_manager ());
+
+  /* Update bindings for all clusters that have escaped, whether above,
+     or previously.  */
+  m_store.on_unknown_fncall (call, m_mgr->get_store2_manager ());
+
+#if 0
+  /* Purge sm-state for any remaining svalue2s that point to region2s that
+     were reachable.  This helps suppress leak false-positives.
+
+     For example, if we had a malloc call that was cast to a "foo *" type,
+     we could have a temporary void * for the result of malloc which has its
+     own svalue2, not reachable from the function call, but for which the
+     "foo *" svalue2 was reachable.  If we don't purge it, the temporary will
+     be reported as a leak.  */
+  int i;
+  const svalue2 *svalue2;
+  FOR_EACH_VEC_ELT (m_svalue2s, i, svalue2)
+    if (region_const svalue2 *ptr = svalue2->dyn_cast_region_svalue2 ())
+      {
+	region2 *pointee_reg = ptr->get_pointee ();
+	if (reachable_region2s.mutable_p (pointee_reg))
+	  ctxt->on_unknown_change (svalue2_id::from_int (i));
+      }
+
+  validate ();
+#endif
+}
+
+/* FIXME.  */
+
+static void
+add_svalues_to_set_cb (const svalue2 *sval, hash_set<const svalue2 *> *out)
+{
+  out->add (sval);
+}
+
+/* FIXME.
+   Traverse the region2s in this model, determining what region2s are
+   reachable from the store.
+
+   If EXTRA_SVAL is non-NULL, treat it as an additional "root"
+   for reachability (for handling return values from functions when
+   analyzing return of the only function on the stack).
+
+   Find svalues that haven't leaked.    */
+
+void
+region_model2::get_reachable_svalues (hash_set<const svalue2 *> *out,
+				      const svalue2 *extra_sval)
+{
+  reachable_region2s reachable_regs (&m_store, m_mgr);
+
+  /* Add globals and regions that already escaped in previous
+     unknown calls.  */
+  m_store.for_each_cluster (reachable_region2s::init_cluster_cb,
+			    &reachable_regs);
+
+  if (extra_sval)
+    reachable_regs.handle_sval (extra_sval);
+
+#if 0
+  /* Mark all locals in all frames (and what's reachable from them).  */
+  for (const frame_region2 *iter_frame = get_current_frame ();
+       iter_frame; iter_frame = iter_frame->get_calling_frame ())
+    {
+      region_model2_context *ctxt = NULL; // FIXME
+      unsigned i;
+      tree name;
+      FOR_EACH_SSA_NAME (i, name, iter_frame->get_function ())
+	{
+	  /* Ignore the ".MEM" SSA names.  */
+	  if (tree var = SSA_NAME_VAR (name))
+	    if (TREE_CODE (var) == VAR_DECL)
+	      if (VAR_DECL_IS_VIRTUAL_OPERAND (var))
+		continue;
+	  const region2 *name_reg
+	    = iter_frame->get_region_for_local (m_mgr, name, ctxt);
+	  reachable_regs.add (name_reg, false);
+	}
+      //#error
+      /* FIXME: handle both initial values (but not for uninit values)
+	 and explicitly bound values.  */
+      /* FIXME: we need to get at all live SSA names in the frame
+	 that might have values; is there a way to do this?  */
+      // (and all parms/locals)
+    }
+#else
+  /* Get regions for locals that have explicitly bound values.  */
+  for (store2::cluster_map_t::iterator iter = m_store.begin ();
+       iter != m_store.end (); ++iter)
+    {
+      const region2 *base_reg = (*iter).first;
+      if (const region2 *parent = base_reg->get_parent_region ())
+	if (parent->get_kind () == region2::RK_FRAME)
+	  reachable_regs.add (base_reg, false);
+    }
+  /* TODO: we also need to count the regions for live SSA names
+     (and params) at the current program point, for those that
+     still have their initial values (but not for uninit values).  */
+#endif
+
+#if 1
+  /* Populate *OUT based on the values that were reachable.  */
+  for (hash_set<const svalue2 *>::iterator iter
+	 = reachable_regs.begin_reachable_svals ();
+       iter != reachable_regs.end_reachable_svals (); ++iter)
+    {
+      out->add (*iter);
+    }
+#else
+  /* Populate *OUT based on the clusters that were reachable.  */
+  for (hash_set<const region2 *>::iterator iter = reachable_regs.begin ();
+       iter != reachable_regs.end (); ++iter)
+    {
+      const region2 *base_reg = (*iter);
+      binding_cluster2 *cluster = m_store.get_cluster (base_reg);
+      if (cluster)
+	cluster->for_each_value (add_svalues_to_set_cb, out);
+      else
+	{
+	  /* If not explicitly bound (or escaped), add initial svalue.
+	     FIXME: what about uninit?  */
+	  const svalue2 *init = m_mgr->get_or_create_initial_value (base_reg);
+	  out->add (init);
+	}
+    }
+#endif
+}
+
+/* Update this model for the RETURN_STMT, using CTXT to report any
+   diagnostics.  */
+
+bool
+region_model2::on_greturn (const greturn *return_stmt,
+			   region_model2_context *ctxt)
+{
+  tree callee = get_current_function ()->decl;
+  tree lhs = DECL_RESULT (callee);
+  tree rhs = gimple_return_retval (return_stmt);
+
+  if (lhs && rhs)
+    copy_region (get_lvalue (lhs, ctxt),
+		 get_lvalue (rhs, ctxt),
+		 ctxt);
+  return true;
+}
+
+/* Update this model for a call and return of setjmp/sigsetjmp at CALL within
+   ENODE, using CTXT to report any diagnostics.
+
+   This is for the initial direct invocation of setjmp/sigsetjmp (which returns
+   0), as opposed to any second return due to longjmp/sigsetjmp.  */
+
+void
+region_model2::on_setjmp (const gcall *call, const exploded_node *enode,
+			 region_model2_context *ctxt)
+{
+  const svalue2 *buf_ptr = get_rvalue (gimple_call_arg (call, 0), ctxt);
+  const region2 *buf_reg = deref_rvalue (buf_ptr, gimple_call_arg (call, 0),
+					 ctxt);
+
+  /* Create a setjmp_svalue2 for this call and store it in BUF_REG's
+     region2.  */
+  if (buf_reg)
+    {
+      setjmp_record r (enode, call);
+      const svalue2 *sval
+	= m_mgr->get_or_create_setjmp_svalue2 (r, buf_reg->get_type ());
+      set_value (buf_reg, sval, ctxt);
+    }
+
+  /* Direct calls to setjmp return 0.  */
+  if (tree lhs = gimple_call_lhs (call))
+    {
+      tree zero = build_int_cst (TREE_TYPE (lhs), 0);
+      const svalue2 *new_sval = m_mgr->get_or_create_constant_svalue2 (zero);
+      const region2 *lhs_reg = get_lvalue (lhs, ctxt);
+      set_value (lhs_reg, new_sval, ctxt);
+    }
+}
+
+/* Update this region_model2 for rewinding from a "longjmp" at LONGJMP_CALL
+   to a "setjmp" at SETJMP_CALL where the final stack depth should be
+   SETJMP_STACK_DEPTH.  Pop any stack frames.  Leak detection is *not*
+   done, and should be done by the caller.  */
+
+void
+region_model2::on_longjmp (const gcall *longjmp_call, const gcall *setjmp_call,
+			   int setjmp_stack_depth, region_model2_context *ctxt)
+{
+  /* Evaluate the val, using the frame of the "longjmp".  */
+  tree fake_retval = gimple_call_arg (longjmp_call, 1);
+  const svalue2 *fake_retval_sval = get_rvalue (fake_retval, ctxt);
+
+  /* Pop any frames until we reach the stack depth of the function where
+     setjmp was called.  */
+  gcc_assert (get_stack_depth () >= setjmp_stack_depth);
+  while (get_stack_depth () > setjmp_stack_depth)
+    {
+      /* Don't purge unused svalue2s yet, as we're using fake_retval_sval.  */
+      pop_frame (NULL, false, NULL, NULL, ctxt);
+    }
+
+  gcc_assert (get_stack_depth () == setjmp_stack_depth);
+
+  /* Assign to LHS of "setjmp" in new_state.  */
+  if (tree lhs = gimple_call_lhs (setjmp_call))
+    {
+      /* Passing 0 as the val to longjmp leads to setjmp returning 1.  */
+      tree t_zero = build_int_cst (TREE_TYPE (fake_retval), 0);
+      const svalue2 *zero_sval = m_mgr->get_or_create_constant_svalue2 (t_zero);
+      tristate eq_zero = eval_condition (fake_retval_sval, EQ_EXPR, zero_sval);
+      /* If we have 0, use 1.  */
+      if (eq_zero.is_true ())
+	{
+	  tree t_one = build_int_cst (TREE_TYPE (fake_retval), 1);
+	  const svalue2 *one_sval
+	    = m_mgr->get_or_create_constant_svalue2 (t_one);
+	  fake_retval_sval = one_sval;
+	}
+      else
+	{
+	  /* Otherwise note that the value is nonzero.  */
+	  m_constraints2->add_constraint (fake_retval_sval, NE_EXPR, zero_sval);
+	}
+
+      /* Decorate the return value from setjmp as being unmergeable,
+	 so that we don't attempt to merge states with it as zero
+	 with states in which it's nonzero, leading to a clean distinction
+	 in the exploded_graph betweeen the first return and the second
+	 return.  */
+      fake_retval_sval = m_mgr->get_or_create_unmergeable (fake_retval_sval);
+
+      const region2 *lhs_reg = get_lvalue (lhs, ctxt);
+      set_value (lhs_reg, fake_retval_sval, ctxt);
+    }
+}
+
+/* Update this region_model2 for a phi stmt of the form
+     LHS = PHI <...RHS...>.
+   where RHS is for the appropriate edge.  */
+
+void
+region_model2::handle_phi (const gphi *phi,
+			   tree lhs, tree rhs, bool is_back_edge,
+			   region_model2_context *ctxt)
+{
+  /* For now, don't bother tracking the .MEM SSA names.  */
+  if (tree var = SSA_NAME_VAR (lhs))
+    if (TREE_CODE (var) == VAR_DECL)
+      if (VAR_DECL_IS_VIRTUAL_OPERAND (var))
+	return;
+
+  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:
+      /* If we have a back edge, we probably have a loop.
+	 Use an unknown value, to avoid effectively unrolling the
+	 loop.
+	 To terminate, we need to avoid generating a series of
+	 models with an unbounded monotonically increasing number of
+	 redundant unknown values; hence we need to purge svalue2s
+	 before inserting the state into the exploded graph, to
+	 collect unused svalue2s.  */
+      mark_region_as_unknown (get_lvalue (lhs, ctxt));
+    }
+  else
+#endif
+    set_value (get_lvalue (lhs, ctxt), rhs_sval, ctxt);
+
+  if (ctxt)
+    ctxt->on_phi (phi, rhs);
+}
+
+/* Implementation of region_model2::get_lvalue; the latter adds type-checking.
+
+   Get the id of the region2 for PV within this region_model2,
+   emitting any diagnostics to CTXT.  */
+
+const region2 *
+region_model2::get_lvalue_1 (path_var pv,
+			     region_model2_context *ctxt)
+{
+  tree expr = pv.m_tree;
+
+  gcc_assert (expr);
+
+
+  switch (TREE_CODE (expr))
+    {
+    default:
+#if 1
+      return m_mgr->get_region2_for_unexpected_tree_code (ctxt, expr,
+							  dump_location_t ());
+#else
+      gcc_unreachable (); // FIXME
+#endif
+
+    case ARRAY_REF:
+      {
+	tree array = TREE_OPERAND (expr, 0);
+	tree index = TREE_OPERAND (expr, 1);
+#if 0
+	// TODO: operands 2 and 3, if present:
+	gcc_assert (TREE_OPERAND (expr, 2) == NULL_TREE);
+	gcc_assert (TREE_OPERAND (expr, 3) == NULL_TREE);
+#endif
+
+	const region2 *array_reg = get_lvalue (array, ctxt);
+	const svalue2 *index_sval = get_rvalue (index, ctxt);
+	return m_mgr->get_element_region (array_reg,
+					  TREE_TYPE (TREE_TYPE (array)),
+					  index_sval);
+#if 0
+	const region2 *base_array_reg = get_region2 (array_reg);
+	array_region2 *array_reg  = base_array_reg->dyn_cast_array_region2 ();
+	if (!array_reg)
+	  {
+	    /* Normally, array_reg ought to refer to an array_region2, since
+	       array's type will be ARRAY_TYPE.  However, if we have an
+	       unexpected tree code for array, we could have a
+	       symbolic_region2 here.  If so, we're in error-handling. */
+	    gcc_assert (base_array_reg->get_type () == NULL_TREE);
+	    return make_region2_for_unexpected_tree_code (ctxt, expr,
+							 dump_location_t ());
+	  }
+	return array_reg->get_element (this, array_reg, index_sval, ctxt);
+#endif
+      }
+      break;
+
+#if 0
+    case BIT_FIELD_REF:
+      {
+	/* For now, create a view, as if a cast, ignoring the bit positions.  */
+	tree obj = TREE_OPERAND (expr, 0);
+	return get_or_create_view (get_lvalue (obj, ctxt), TREE_TYPE (expr),
+				   ctxt);
+      };
+      break;
+#endif
+    case MEM_REF:
+      {
+	tree ptr = TREE_OPERAND (expr, 0);
+	tree offset = TREE_OPERAND (expr, 1);
+	const svalue2 *ptr_sval = get_rvalue (ptr, ctxt);
+	const svalue2 *offset_sval = get_rvalue (offset, ctxt);
+	const region2 *star_ptr = deref_rvalue (ptr_sval, ptr, ctxt);
+	return m_mgr->get_offset_region (star_ptr,
+					 TREE_TYPE (expr),
+					 offset_sval);
+      }
+      break;
+
+    case FUNCTION_DECL:
+      return m_mgr->get_region_for_fndecl (expr);
+
+    case LABEL_DECL:
+      return m_mgr->get_region_for_label (expr);
+
+    case VAR_DECL:
+      /* Handle globals.  */
+      if (is_global_var (expr))
+	return m_mgr->get_region_for_global (expr);
+
+      /* Fall through.  */
+
+    case SSA_NAME:
+    case PARM_DECL:
+    case RESULT_DECL:
+      {
+	gcc_assert (TREE_CODE (expr) == SSA_NAME
+		    || TREE_CODE (expr) == PARM_DECL
+		    || TREE_CODE (expr) == VAR_DECL
+		    || TREE_CODE (expr) == RESULT_DECL);
+
+	int stack_index = pv.m_stack_depth;
+	const frame_region2 *frame = get_frame_at_index (stack_index);
+	gcc_assert (frame);
+	return frame->get_region_for_local (m_mgr, expr);
+      }
+
+    case COMPONENT_REF:
+      {
+	/* obj.field  */
+	tree obj = TREE_OPERAND (expr, 0);
+	tree field = TREE_OPERAND (expr, 1);
+	//tree obj_type = TREE_TYPE (obj);
+	// FIXME:
+#if 0
+	if (TREE_CODE (obj_type) != RECORD_TYPE
+	    && TREE_CODE (obj_type) != UNION_TYPE)
+	  return make_region2_for_unexpected_tree_code (ctxt, obj_type,
+						       dump_location_t ());
+#endif
+	const region2 *obj_reg = get_lvalue (obj, ctxt);
+	return m_mgr->get_field_region (obj_reg, field);
+      }
+      break;
+#if 0
+    case CONST_DECL:
+      {
+	tree cst_type = TREE_TYPE (expr);
+	const region2 *cst_reg
+	  = add_region2_for_type (m_root_reg, cst_type, ctxt);
+	if (tree value = DECL_INITIAL (expr))
+	  {
+	    const svalue2 *sval = get_rvalue (value, ctxt);
+	    get_region2 (cst_reg)->set_value (*this, cst_reg, sid, ctxt);
+	  }
+	return cst_reg;
+      }
+      break;
+#endif
+
+    case STRING_CST:
+      return m_mgr->get_region_for_string (expr);
+
+#if 0
+    case NOP_EXPR:
+    case VIEW_CONVERT_EXPR:
+      {
+	tree obj = TREE_OPERAND (expr, 0);
+	return get_or_create_view (get_lvalue (obj, ctxt), TREE_TYPE (expr),
+				   ctxt);
+      };
+      break;
+#endif
+    }
+}
+
+/* Assert that SRC_TYPE can be converted to DST_TYPE as a no-op.  */
+
+static void
+assert_compat_types (tree src_type, tree dst_type)
+{
+  if (src_type && dst_type && !VOID_TYPE_P (dst_type))
+    if (!(useless_type_conversion_p (src_type, dst_type)))
+      internal_error ("incompatible types: %qT and %qT", src_type, dst_type);
+}
+
+/* Get the region2 for PV within this region_model2,
+   emitting any diagnostics to CTXT.  */
+
+const region2 *
+region_model2::get_lvalue (path_var pv,
+			   region_model2_context *ctxt)
+{
+  if (pv.m_tree == NULL_TREE)
+    return NULL;
+
+  const region2 *result_reg = get_lvalue_1 (pv, ctxt);
+  assert_compat_types (result_reg->get_type (), TREE_TYPE (pv.m_tree));
+  return result_reg;
+}
+
+/* Get the region2 for EXPR within this region_model2 (assuming the most
+   recent stack frame if it's a local).  */
+
+const region2 *
+region_model2::get_lvalue (tree expr,
+			   region_model2_context *ctxt)
+{
+  return get_lvalue (path_var (expr, get_stack_depth () - 1), ctxt);
+}
+
+/* Implementation of region_model2::get_rvalue; the latter adds type-checking.
+
+   Get the value of PV within this region_model2,
+   emitting any diagnostics to CTXT.  */
+
+const svalue2 *
+region_model2::get_rvalue_1 (path_var pv,
+			     region_model2_context *ctxt)
+{
+  gcc_assert (pv.m_tree);
+
+  switch (TREE_CODE (pv.m_tree))
+    {
+    default:
+      {
+	gcc_unreachable ();
+#if 0
+	const svalue2 *unknown_sval = new unknown_svalue2 (TREE_TYPE (pv.m_tree));
+	return add_svalue2 (unknown_sval);
+#endif
+      }
+      break;
+
+    case ADDR_EXPR:
+      {
+	/* "&EXPR".  */
+	tree expr = pv.m_tree;
+	tree op0 = TREE_OPERAND (expr, 0);
+#if 0
+	if (TREE_CODE (op0) == FUNCTION_DECL)
+	  return m_mgr->get_svalue2_for_fndecl (TREE_TYPE (expr), op0, ctxt);
+	else if (TREE_CODE (op0) == LABEL_DECL)
+	  return m_mgr->get_svalue2_for_label (TREE_TYPE (expr), op0, ctxt);
+#endif
+	const region2 *expr_reg = get_lvalue (op0, ctxt);
+	return m_mgr->get_ptr_svalue2 (TREE_TYPE (expr), expr_reg);
+      }
+      break;
+
+    case BIT_FIELD_REF:
+      {
+#if 0
+	tree expr = pv.m_tree;
+	const region2 *obj = get_lvalue (TREE_OPERAND (expr, 0), ctxt);
+	const svalue2 *num_bits = get_rvalue (TREE_OPERAND (expr, 1), ctxt);
+	const svalue2 *bit_offset = get_rvalue (TREE_OPERAND (expr, 2), ctxt);
+	/* FIXME: attempting to add bitwise access will ultimately require
+	   a new svalue2 type for accessing bits from another svalue2
+	   (e.g. to handle the case where the store has no binding for the
+	   bits), and probably a new region2 type also (or maybe just the
+	   latter, using a sub_svalue2 for the former).
+	   Is it possible to attempting to spot when we're accessing known
+	   values, and have this possibly return NULL?  */
+	return maybe_get_store_bits (TREE_TYPE (expr), obj, num_bits,
+				     bit_offset);
+#else
+	return m_mgr->get_or_create_unknown_svalue2 (TREE_TYPE (pv.m_tree));
+#endif
+      }
+      break;
+
+    case SSA_NAME:
+    case VAR_DECL:
+    case PARM_DECL:
+    case RESULT_DECL:
+    case ARRAY_REF:
+      {
+	const region2 *element_reg = get_lvalue (pv, ctxt);
+	return get_store_value (element_reg);
+      }
+
+    case REALPART_EXPR:
+    case IMAGPART_EXPR:
+    case VIEW_CONVERT_EXPR:
+      {
+	tree expr = pv.m_tree;
+	tree arg = TREE_OPERAND (expr, 0);
+	const svalue2 *arg_sval = get_rvalue (arg, ctxt);
+	const svalue2 *sval_unaryop
+	  = m_mgr->get_or_create_unaryop (TREE_TYPE (expr), TREE_CODE (expr),
+					  arg_sval);
+	return sval_unaryop;
+      };
+
+    case INTEGER_CST:
+    case REAL_CST:
+    case COMPLEX_CST:
+    case VECTOR_CST:
+    case STRING_CST:
+      return m_mgr->get_or_create_constant_svalue2 (pv.m_tree);
+
+    case POINTER_PLUS_EXPR:
+	{
+	  tree expr = pv.m_tree;
+	  tree ptr = TREE_OPERAND (expr, 0);
+	  tree offset = TREE_OPERAND (expr, 1);
+	  const svalue2 *ptr_sval = get_rvalue (ptr, ctxt);
+	  const svalue2 *offset_sval = get_rvalue (offset, ctxt);
+	  const svalue2 *sval_binop
+	    = m_mgr->get_or_create_binop (TREE_TYPE (expr), POINTER_PLUS_EXPR,
+					  ptr_sval, offset_sval);
+	  return sval_binop;
+	}
+
+    /* Binary ops.  */
+    case PLUS_EXPR:
+    case MULT_EXPR:
+	{
+	  tree expr = pv.m_tree;
+	  tree arg0 = TREE_OPERAND (expr, 0);
+	  tree arg1 = TREE_OPERAND (expr, 1);
+	  const svalue2 *arg0_sval = get_rvalue (arg0, ctxt);
+	  const svalue2 *arg1_sval = get_rvalue (arg1, ctxt);
+	  const svalue2 *sval_binop
+	    = m_mgr->get_or_create_binop (TREE_TYPE (expr), TREE_CODE (expr),
+					  arg0_sval, arg1_sval);
+	  return sval_binop;
+	}
+
+    case COMPONENT_REF:
+    case MEM_REF:
+      {
+	const region2 *ref_reg = get_lvalue (pv, ctxt);
+	return get_store_value (ref_reg);
+      }
+    }
+}
+
+/* Get the value of PV within this region_model2,
+   emitting any diagnostics to CTXT.  */
+
+const svalue2 *
+region_model2::get_rvalue (path_var pv,
+			   region_model2_context *ctxt)
+{
+  if (pv.m_tree == NULL_TREE)
+    return NULL;
+
+  const svalue2 *result_sval = get_rvalue_1 (pv, ctxt);
+
+  assert_compat_types (result_sval->get_type (), TREE_TYPE (pv.m_tree));
+
+  return result_sval;
+}
+
+/* Get the value of EXPR within this region_model2 (assuming the most
+   recent stack frame if it's a local).  */
+
+const svalue2 *
+region_model2::get_rvalue (tree expr,
+			   region_model2_context *ctxt)
+{
+  return get_rvalue (path_var (expr, get_stack_depth () - 1), ctxt);
+}
+
+/* FIXME.  */
+
+const svalue2 *
+region_model2::get_store_value (const region2 *reg) const
+{
+  const svalue2 *sval
+    = m_store.get_any_binding (m_mgr->get_store2_manager (), reg);
+  if (sval)
+    {
+      if (reg->get_type ())
+	sval = m_mgr->get_or_create_cast (reg->get_type (), sval);
+      return sval;
+    }
+
+  /* Special-case: read at a constant index within a STRING_CST.  */
+  if (const offset_region2 *offset_reg = reg->dyn_cast_offset_region2 ())
+    if (tree byte_offset_cst
+	  = offset_reg->get_byte_offset ()->maybe_get_constant ())
+      if (const string_region2 *str_reg
+	  = reg->get_parent_region ()->dyn_cast_string_region2 ())
+	{
+	  tree string_cst = str_reg->get_string_cst ();
+	  if (const svalue2 *char_sval
+		= maybe_get_char_from_string_cst (string_cst, byte_offset_cst,
+						  m_mgr))
+	    return m_mgr->get_or_create_cast (reg->get_type (), char_sval);
+	}
+
+  /* Special-case: read the initial char of a STRING_CST.  */
+  // FIXME: but only if the cast is to a char?
+  if (const cast_region2 *cast_reg = reg->dyn_cast_cast_region2 ())
+    if (const string_region2 *str_reg
+	= cast_reg->get_original_region ()->dyn_cast_string_region2 ())
+      if (reg->get_type ()) // FIXME
+      {
+	tree string_cst = str_reg->get_string_cst ();
+	tree byte_offset_cst = build_int_cst (integer_type_node, 0);
+	if (const svalue2 *char_sval
+	    = maybe_get_char_from_string_cst (string_cst, byte_offset_cst,
+					      m_mgr))
+	  return m_mgr->get_or_create_cast (reg->get_type (), char_sval);
+      }
+
+  /* Otherwise we implicitly have the initial value of the region.  */
+
+  /* Special-case: to avoid having to explicitly update all previously
+     untracked globals when calling an unknown fn, we instead change
+     the default here so we implicitly have an unknown value for such
+     regions.  */
+  // FIXME: what about symbolic writes also?
+  if (m_store.called_unknown_fn_p ())
+    if (reg->get_base_region ()->get_parent_region ()->get_kind ()
+	== region2::RK_GLOBALS)
+      return m_mgr->get_or_create_unknown_svalue2 (reg->get_type ());
+
+  /* FIXME: not if touched by symbolic value; handled in
+     binding_cluster2::get_any_binding.  */
+  return m_mgr->get_or_create_initial_value (reg);
+}
+
+/* Return false if REG does not exist, true if it may do.
+   This is for detecting regions within the stack that don't exist anymore
+   after frames are popped.  */
+
+bool
+region_model2::region_exists_p (const region2 *reg) const
+{
+  /* If within a stack frame, check that the stack frame is live.  */
+  if (const frame_region2 *enclosing_frame = reg->maybe_get_frame_region ())
+    {
+      /* Check that the current frame is the enclosing frame, or is called
+	 by it.  */
+      for (const frame_region2 *iter_frame = get_current_frame (); iter_frame;
+	   iter_frame = iter_frame->get_calling_frame ())
+	if (iter_frame == enclosing_frame)
+	  return true;
+      return false;
+    }
+
+  return true;
+}
+
+/* Build a cast of SRC_EXPR to DST_TYPE, or return NULL_TREE.
+
+   Adapted from gcc::jit::playback::context::build_cast, which in turn is
+   adapted from
+     - c/c-typeck.c:build_c_cast
+     - c/c-convert.c: convert
+     - convert.h
+   Only some kinds of cast are currently supported here.  */
+#if 0
+static tree
+build_cast (tree dst_type, tree src_expr)
+{
+  tree result = targetm.convert_to_type (dst_type, src_expr);
+  if (result)
+    return result;
+  enum tree_code dst_code = TREE_CODE (dst_type);
+  switch (dst_code)
+    {
+    case INTEGER_TYPE:
+    case ENUMERAL_TYPE:
+      result = convert_to_integer (dst_type, src_expr);
+      goto maybe_fold;
+
+    case BOOLEAN_TYPE:
+      /* Compare with c_objc_common_truthvalue_conversion and
+	 c_common_truthvalue_conversion. */
+      /* For now, convert to: (src_expr != 0)  */
+      result = build2 (NE_EXPR, dst_type,
+		       src_expr,
+		       build_int_cst (TREE_TYPE (src_expr), 0));
+      goto maybe_fold;
+
+    case REAL_TYPE:
+      result = convert_to_real (dst_type, src_expr);
+      goto maybe_fold;
+
+    case POINTER_TYPE:
+      result = build1 (NOP_EXPR, dst_type, src_expr);
+      goto maybe_fold;
+
+    default:
+      return NULL_TREE;
+
+    maybe_fold:
+      if (TREE_CODE (result) != C_MAYBE_CONST_EXPR)
+	result = fold (result);
+      return result;
+    }
+}
+#endif
+
+/* If the type of SID's underlying value is DST_TYPE, return SID.
+   Otherwise, attempt to create (or reuse) an svalue2 representing an access
+   of SID as a DST_TYPE and return that value's svalue2_id.  */
+#if 0
+svalue2_id
+region_model2::maybe_cast_1 (tree dst_type, const svalue2 *sval)
+{
+  const svalue2 *sval = get_svalue2 (sid);
+  tree src_type = sval->get_type ();
+  if (src_type == dst_type)
+    return sid;
+
+  if (POINTER_TYPE_P (dst_type)
+      || POINTER_TYPE_P (src_type))
+    {
+      /* Pointer to region2.  */
+      if (region_svalue2 *ptr_sval = sval->dyn_cast_region_svalue2 ())
+	return get_or_create_ptr_svalue2 (dst_type, ptr_sval->get_pointee ());
+
+      /* Unknown pointer?   Get or create a new unknown pointer of the
+	 correct type, preserving the equality between the pointers.  */
+      if (sval->dyn_cast_unknown_svalue2 ())
+	{
+	  equiv_class &ec = m_constraints->get_equiv_class (sid);
+
+	  /* Look for an existing pointer of the correct type within the EC.  */
+	  int i;
+	  const svalue2 **equiv_sval;
+	  FOR_EACH_VEC_ELT (ec.m_vars, i, equiv_sval)
+	    {
+	      const svalue2 *equiv_val = get_svalue2 (*equiv_sval);
+	      if (equiv_val->get_type () == dst_type)
+		return *equiv_sval;
+	    }
+
+	  /* Otherwise, create a new unknown pointer of the correct type.  */
+	  const svalue2 *unknown_sval = new unknown_svalue2 (dst_type);
+	  const svalue2 *new_ptr_sval = add_svalue2 (unknown_sval);
+	  m_constraints->add_constraint (sid, EQ_EXPR, new_ptr_sval);
+	  return new_ptr_sval;
+	}
+    }
+
+  /* Attempt to cast constants.  */
+  if (tree src_cst = sval->maybe_get_constant ())
+    {
+      if (tree dst = build_cast (dst_type, src_cst))
+	if (CONSTANT_CLASS_P (dst))
+	  return get_or_create_constant_svalue2 (dst);
+    }
+
+  /* Otherwise, return a new unknown value.  */
+  const svalue2 *unknown_sval = new unknown_svalue2 (dst_type);
+  return add_svalue2 (unknown_sval);
+}
+
+/* If the type of SID's underlying value is DST_TYPE, return SID.
+   Otherwise, attempt to create (or reuse) an svalue2 representing an access
+   of SID as a DST_TYPE and return that value's svalue2_id.
+
+   If the result != SID, then call CTXT's on_cast vfunc (if CTXT is non-NULL),
+   so that sm-state can be propagated from SID to the result.  */
+
+svalue2_id
+region_model2::maybe_cast (tree dst_type, const svalue2 *sval,
+			  region_model2_context *ctxt)
+{
+  const svalue2 *result = maybe_cast_1 (dst_type, sid);
+  if (result != sid)
+    if (ctxt)
+      {
+	/* Notify ctxt about a cast, so any sm-state can be copied.  */
+	ctxt->on_cast (sid, result);
+      }
+  return result;
+}
+#endif
+
+/* Ensure that the region2 for OBJ_REG has a child region2 for FIELD;
+   return the child region2's region2_id.  */
+#if 0
+region2_id
+region_model2::get_field_region (region2 *struct_or_union_reg, tree field,
+				 region_model2_context *ctxt)
+{
+  struct_or_union_region2 *sou_reg
+    = get_region2<struct_or_union_region2> (struct_or_union_reg);
+
+  /* Inherit constness from parent type.  */
+  const int qual_mask = TYPE_QUAL_CONST;
+  int sou_quals = TYPE_QUALS (sou_reg->get_type ()) & qual_mask;
+  tree field_type = TREE_TYPE (field);
+  tree field_type_with_quals = build_qualified_type (field_type, sou_quals);
+
+  // TODO: maybe convert to a vfunc?
+  if (sou_reg->get_kind () == RK_UNION)
+    {
+      /* Union.
+	 Get a view of the union as a whole, with the type of the field.  */
+      region2 *view_reg
+	= get_or_create_view (struct_or_union_reg, field_type_with_quals, ctxt);
+      return view_reg;
+    }
+  else
+    {
+      /* Struct.  */
+      region2 *child_reg
+	= sou_reg->get_or_create (this, struct_or_union_reg, field,
+				  field_type_with_quals, ctxt);
+      return child_reg;
+    }
+}
+#endif
+
+/* Get a region2 for referencing PTR_SVAL, creating a region2 if need be, and
+   potentially generating warnings via CTXT.
+   PTR_TREE if non-NULL can be used when emitting diagnostics.  */
+
+const region2 *
+region_model2::deref_rvalue (const svalue2 *ptr_sval, tree ptr_tree,
+			     region_model2_context *ctxt)
+{
+  gcc_assert (ptr_sval);
+
+  switch (ptr_sval->get_kind ())
+    {
+    default:
+      gcc_unreachable ();
+
+    case svalue2::SK_REGION:
+      {
+	const region_svalue2 *region2_sval
+	  = as_a <const region_svalue2 *> (ptr_sval);
+	return region2_sval->get_pointee ();
+      }
+
+    case svalue2::SK_BINOP:
+#if 1
+      {
+	const binop_svalue2 *binop_sval
+	  = as_a <const binop_svalue2 *> (ptr_sval);
+	switch (binop_sval->get_op ())
+	  {
+	  case POINTER_PLUS_EXPR:
+	    {
+	      /* If we have a symbolic value expressing pointer arithmentic,
+		 try to convert it to a suitable region.  */
+	      /* FIXME: but should it be an element_region2?  that expresses
+		 things via element indices, rather than via byte offsets.  */
+	      /* Perhaps instead we should capture this as an offset relative
+		 to a base region in region_offset?  */
+	      const region2 *parent_region
+		= deref_rvalue (binop_sval->get_arg0 (), NULL_TREE, ctxt);
+	      const svalue2 *offset = binop_sval->get_arg1 ();
+	      tree type= TREE_TYPE (ptr_sval->get_type ());
+	      return m_mgr->get_offset_region (parent_region, type, offset);
+	    }
+	  default:
+	    goto create_symbolic_region2;
+	  }
+      }
+#endif
+    case svalue2::SK_CONSTANT:
+    case svalue2::SK_INITIAL:
+    case svalue2::SK_UNARYOP:
+    case svalue2::SK_SUB:
+    case svalue2::SK_WIDENING:
+    case svalue2::SK_CONJURED:
+      goto create_symbolic_region2;
+
+    case svalue2::SK_POISONED:
+      {
+	if (ctxt)
+	  {
+	    tree ptr = get_representative_tree (ptr_sval);
+	    /* If we can't get a representative tree for PTR_SVAL
+	       (e.g. if it hasn't been bound into the store), then
+	       fall back on PTR_TREE, if non-NULL.  */
+	    if (!ptr)
+	      ptr = ptr_tree;
+	    if (ptr)
+	      {
+		const poisoned_svalue2 *poisoned_sval
+		  = as_a <const poisoned_svalue2 *> (ptr_sval);
+		enum poison_kind pkind = poisoned_sval->get_poison_kind ();
+		ctxt->warn (new poisoned_value_diagnostic2 (ptr, pkind));
+	      }
+	  }
+	goto create_symbolic_region2;
+      }
+
+    case svalue2::SK_UNKNOWN:
+      {
+      create_symbolic_region2:
+	/* We need a symbolic_region2 to represent this unknown region2.
+	   FIXME:
+	   We don't know if it on the heap, stack, or a global,
+	   so use the root region2 as parent.  */
+	const region2 *new_reg = m_mgr->get_symbolic_region (ptr_sval);
+#if 0
+	/* We need to write the region2 back into the pointer,
+	   or we'll get a new, different region2 each time.
+	   We do this by changing the meaning of ptr_sval, replacing
+	   the unknown value with the ptr to the new region2.
+	   We replace the meaning of the ID rather than simply writing
+	   to PTR's lvalue since there could be several places sharing
+	   the same unknown ptr value.  */
+	const svalue2 *ptr_val
+	  = new region_svalue2 (ptr_sval->get_type (), new_reg);
+	replace_svalue2 (ptr_sval, ptr_val);
+#endif
+	return new_reg;
+      }
+
+    case svalue2::SK_SETJMP:
+      goto create_symbolic_region2;
+    }
+
+  gcc_unreachable ();
+}
+
+#if 0
+
+/* Get a region2 *for referencing PTR, creating a region2 if need be, and
+   potentially generating warnings via CTXT.  */
+
+region2_id
+region_model2::deref_rvalue (tree ptr, region_model2_context *ctxt)
+{
+  const svalue2 *ptr_sval = get_rvalue (ptr, ctxt);
+  return deref_rvalue (ptr_sval, ctxt);
+}
+#endif
+
+/* Set the value of the region2 given by LHS_REG to the value given
+   by RHS_SVAL.  */
+
+void
+region_model2::set_value (const region2 *lhs_reg, const svalue2 *rhs_sval,
+			  region_model2_context */*ctxt*/)
+{
+  gcc_assert (lhs_reg);
+  gcc_assert (rhs_sval);
+
+  m_store.set_value (m_mgr->get_store2_manager(), lhs_reg, rhs_sval,
+		     BK_direct);
+}
+
+/* Set the value of the region2 given by LHS to the value given
+   by RHS.  */
+
+void
+region_model2::set_value (tree lhs, tree rhs, region_model2_context *ctxt)
+{
+  const region2 *lhs_reg = get_lvalue (lhs, ctxt);
+  const svalue2 *rhs_sval = get_rvalue (rhs, ctxt);
+  gcc_assert (lhs_reg);
+  gcc_assert (rhs_sval);
+  set_value (lhs_reg, rhs_sval, ctxt);
+}
+
+/* FIXME.  */
+
+void
+region_model2::clobber_region (const region2 *reg)
+{
+  m_store.clobber_region (m_mgr->get_store2_manager(), reg);
+}
+
+/* FIXME.  */
+
+void
+region_model2::purge_region (const region2 *reg)
+{
+  m_store.purge_region (m_mgr->get_store2_manager(), reg);
+}
+
+/* Zero-fill REG.  */
+
+void
+region_model2::zero_fill_region (const region2 *reg)
+{
+  m_store.zero_fill_region (m_mgr->get_store2_manager(), reg);
+}
+
+/* Mark REG as having unknown content.  */
+
+void
+region_model2::mark_region_as_unknown (const region2 *reg)
+{
+  m_store.mark_region_as_unknown (m_mgr->get_store2_manager(), reg);
+}
+
+/* Determine what is known about the condition "LHS_SVAL OP RHS_SVAL" within
+   this model.  */
+
+tristate
+region_model2::eval_condition (const svalue2 *lhs,
+			       enum tree_code op,
+			       const svalue2 *rhs) const
+{
+  /* For now, make no attempt to capture constraints on floating-point
+     values.  */
+  if ((lhs->get_type () && FLOAT_TYPE_P (lhs->get_type ()))
+      || (rhs->get_type () && FLOAT_TYPE_P (rhs->get_type ())))
+    return tristate::unknown ();
+
+  tristate ts = eval_condition_without_cm (lhs, op, rhs);
+  if (ts.is_known ())
+    return ts;
+
+  /* Otherwise, try constraints.  */
+  return m_constraints2->eval_condition (lhs, op, rhs);
+}
+
+/* Determine what is known about the condition "LHS_SVAL OP RHS_SVAL" within
+   this model, without resorting to the constraint_manager.
+
+   This is exposed so that impl_region_model2_context::on_state_leak can
+   check for equality part-way through region_model2::purge_unused_svalue2s
+   without risking creating new ECs.  */
+
+tristate
+region_model2::eval_condition_without_cm (const svalue2 *lhs,
+					  enum tree_code op,
+					  const svalue2 *rhs) const
+{
+  gcc_assert (lhs);
+  gcc_assert (rhs);
+
+  /* See what we know based on the values.  */
+
+  /* For now, make no attempt to capture constraints on floating-point
+     values.  */
+  if ((lhs->get_type () && FLOAT_TYPE_P (lhs->get_type ()))
+      || (rhs->get_type () && FLOAT_TYPE_P (rhs->get_type ())))
+    return tristate::unknown ();
+
+  /* Unwrap any unmergeable values.  */
+  lhs = lhs->unwrap_any_unmergeable ();
+  rhs = rhs->unwrap_any_unmergeable ();
+
+  if (lhs == rhs)
+    {
+      /* If we have the same svalue2, then we have equality
+	 (apart from NaN-handling).
+	 TODO: should this definitely be the case for poisoned values?  */
+      /* Poisoned and unknown values are "unknowable".  */
+      if (lhs->get_kind () == svalue2::SK_POISONED
+	  || lhs->get_kind () == svalue2::SK_UNKNOWN)
+	return tristate::TS_UNKNOWN;
+
+      switch (op)
+	{
+	case EQ_EXPR:
+	case GE_EXPR:
+	case LE_EXPR:
+	  return tristate::TS_TRUE;
+
+	case NE_EXPR:
+	case GT_EXPR:
+	case LT_EXPR:
+	  return tristate::TS_FALSE;
+
+	default:
+	  /* For other ops, use the logic below.  */
+	  break;
+	}
+    }
+
+  /* If we have a pair of region_svalue2s, compare them.  */
+  if (const region_svalue2 *lhs_ptr = lhs->dyn_cast_region_svalue2 ())
+    if (const region_svalue2 *rhs_ptr = rhs->dyn_cast_region_svalue2 ())
+      {
+	tristate res = region_svalue2::eval_condition (lhs_ptr, op, rhs_ptr);
+	if (res.is_known ())
+	  return res;
+	/* Otherwise, only known through constraints.  */
+      }
+
+  /* If we have a pair of constants, compare them.  */
+  if (const constant_svalue2 *cst_lhs = lhs->dyn_cast_constant_svalue2 ())
+    if (const constant_svalue2 *cst_rhs = rhs->dyn_cast_constant_svalue2 ())
+      return constant_svalue2::eval_condition (cst_lhs, op, cst_rhs);
+
+  /* Handle comparison of a region_svalue2 against zero.  */
+
+  if (const region_svalue2 *ptr = lhs->dyn_cast_region_svalue2 ())
+    if (const constant_svalue2 *cst_rhs = rhs->dyn_cast_constant_svalue2 ())
+      if (zerop (cst_rhs->get_constant ()))
+	{
+	  /* A region_svalue2 is a non-NULL pointer, except in certain
+	     special cases (see the comment for region2::non_null_p.  */
+	  const region2 *pointee = ptr->get_pointee ();
+	  if (pointee->non_null_p (*this))
+	    {
+	      switch (op)
+		{
+		default:
+		  gcc_unreachable ();
+
+		case EQ_EXPR:
+		case GE_EXPR:
+		case LE_EXPR:
+		  return tristate::TS_FALSE;
+
+		case NE_EXPR:
+		case GT_EXPR:
+		case LT_EXPR:
+		  return tristate::TS_TRUE;
+		}
+	    }
+	}
+
+  /* Handle rejection of equality for comparisons of the initial values of
+     "external" values (such as params) with the address of locals.  */
+  if (const initial_svalue2 *init_lhs = lhs->dyn_cast_initial_svalue2 ())
+    if (const region_svalue2 *rhs_ptr = rhs->dyn_cast_region_svalue2 ())
+      {
+	tristate res = compare_initial_and_pointer (init_lhs, rhs_ptr);
+	if (res.is_known ())
+	  return res;
+      }
+  if (const initial_svalue2 *init_rhs = rhs->dyn_cast_initial_svalue2 ())
+    if (const region_svalue2 *lhs_ptr = lhs->dyn_cast_region_svalue2 ())
+      {
+	tristate res = compare_initial_and_pointer (init_rhs, lhs_ptr);
+	if (res.is_known ())
+	  return res;
+      }
+
+  if (const widening_svalue2 *widen_lhs = lhs->dyn_cast_widening_svalue2 ())
+    if (tree rhs_cst = rhs->maybe_get_constant ())
+      {
+	tristate res = widen_lhs->eval_condition_without_cm (op, rhs_cst);
+	if (res.is_known ())
+	  return res;
+      }
+#if 0
+  if (const widening_svalue2 *widen_rhs = rhs->dyn_cast_widening_svalue2 ())
+    {
+      gcc_unreachable ();
+    }
+#endif
+
+  return tristate::TS_UNKNOWN;
+}
+
+/* Subroutine of region_model2::eval_condition_without_cm, for rejecting
+   equality of INIT_VAL(PARM) with &LOCAL.  */
+
+tristate
+region_model2::compare_initial_and_pointer (const initial_svalue2 *init,
+					    const region_svalue2 *ptr) const
+{
+  const region2 *pointee = ptr->get_pointee ();
+
+  /* If we have a pointer to something within a stack frame, it can't be the
+     initial value of a param.  */
+  if (pointee->maybe_get_frame_region ())
+    {
+      const region2 *reg = init->get_region ();
+      if (tree reg_decl = reg->maybe_get_decl ())
+	if (TREE_CODE (reg_decl) == SSA_NAME)
+	  {
+	    tree ssa_name = reg_decl;
+	    if (SSA_NAME_IS_DEFAULT_DEF (ssa_name)
+		&& SSA_NAME_VAR (ssa_name)
+		&& TREE_CODE (SSA_NAME_VAR (ssa_name)) == PARM_DECL)
+	      return tristate::TS_FALSE;
+	  }
+    }
+
+  return tristate::TS_UNKNOWN;
+}
+
+/* Attempt to add the constraint "LHS OP RHS" to this region_model2.
+   If it is consistent with existing constraints, add it, and return true.
+   Return false if it contradicts existing constraints.
+   Use CTXT for reporting any diagnostics associated with the accesses.  */
+
+bool
+region_model2::add_constraint (tree lhs, enum tree_code op, tree rhs,
+			       region_model2_context *ctxt)
+{
+  /* For now, make no attempt to capture constraints on floating-point
+     values.  */
+  if (FLOAT_TYPE_P (TREE_TYPE (lhs)) || FLOAT_TYPE_P (TREE_TYPE (rhs)))
+    return true;
+
+  const svalue2 *lhs_sval = get_rvalue (lhs, ctxt);
+  const svalue2 *rhs_sval = get_rvalue (rhs, ctxt);
+
+  tristate t_cond = eval_condition (lhs_sval, op, rhs_sval);
+
+  /* If we already have the condition, do nothing.  */
+  if (t_cond.is_true ())
+    return true;
+
+  /* Reject a constraint that would contradict existing knowledge, as
+     unsatisfiable.  */
+  if (t_cond.is_false ())
+    return false;
+
+  /* Store the constraint.  */
+  m_constraints2->add_constraint (lhs_sval, op, rhs_sval);
+
+  add_any_constraints_from_ssa_def_stmt (lhs, op, rhs, ctxt);
+
+#if 0
+  /* If we now know a symbolic_region2 is non-NULL, clear its
+     m_possibly_null.  */
+  if (zerop (rhs) && op == NE_EXPR)
+    if (region_svalue2 *ptr = get_svalue2 (lhs_sval)->dyn_cast_region_svalue2 ())
+      {
+	region2 *pointee = get_region2 (ptr->get_pointee ());
+	if (symbolic_region2 *sym_reg = pointee->dyn_cast_symbolic_region2 ())
+	  sym_reg->m_possibly_null = false;
+      }
+#endif
+
+  /* Notify the context, if any.  This exists so that the state machines
+     in a program_state can be notified about the condition, and so can
+     set sm-state for e.g. unchecked->checked, both for cfg-edges, and
+     when synthesizing constraints as above.  */
+  if (ctxt)
+    ctxt->on_condition (lhs, op, rhs);
+
+  return true;
+}
+
+/* Subroutine of region_model2::add_constraint for handling optimized
+   && and || conditionals.
+
+   If we have an SSA_NAME for a boolean compared against 0,
+   look at anything implied by the def stmt and call add_constraint
+   for it (which could recurse).
+
+   For example, if we have
+      _1 = p_6 == 0B;
+      _2 = p_8 == 0B
+      _3 = _1 | _2
+    and add the constraint
+      (_3 == 0),
+    then the def stmt for _3 implies that _1 and _2 are both false,
+    and hence we can add the constraints:
+      p_6 != 0B
+      p_8 != 0B.  */
+
+void
+region_model2::add_any_constraints_from_ssa_def_stmt (tree lhs,
+						     enum tree_code op,
+						     tree rhs,
+						     region_model2_context *ctxt)
+{
+  if (TREE_CODE (lhs) != SSA_NAME)
+    return;
+
+  if (!zerop (rhs))
+    return;
+
+  if (op != NE_EXPR && op != EQ_EXPR)
+    return;
+
+  gimple *def_stmt = SSA_NAME_DEF_STMT (lhs);
+  if (const gassign *assign = dyn_cast<gassign *> (def_stmt))
+    add_any_constraints_from_gassign (op, rhs, assign, ctxt);
+  else if (gcall *call = dyn_cast<gcall *> (def_stmt))
+    add_any_constraints_from_gcall (op, rhs, call, ctxt);
+}
+
+/* Add any constraints for an SSA_NAME defined by ASSIGN
+   where the result OP RHS.  */
+
+void
+region_model2::add_any_constraints_from_gassign (enum tree_code op,
+						tree rhs,
+						const gassign *assign,
+						region_model2_context *ctxt)
+{
+  /* We have either
+     - "LHS != false" (i.e. LHS is true), or
+     - "LHS == false" (i.e. LHS is false).  */
+  bool is_true = op == NE_EXPR;
+
+  enum tree_code rhs_code = gimple_assign_rhs_code (assign);
+
+  switch (rhs_code)
+    {
+    default:
+      break;
+
+    case NOP_EXPR:
+      {
+	add_constraint (gimple_assign_rhs1 (assign), op, rhs, ctxt);
+      }
+      break;
+
+    case BIT_AND_EXPR:
+      {
+	if (is_true)
+	  {
+	    /* ...and "LHS == (rhs1 & rhs2) i.e. "(rhs1 & rhs2)" is true
+	       then both rhs1 and rhs2 must be true.  */
+	    tree rhs1 = gimple_assign_rhs1 (assign);
+	    tree rhs2 = gimple_assign_rhs2 (assign);
+	    add_constraint (rhs1, NE_EXPR, boolean_false_node, ctxt);
+	    add_constraint (rhs2, NE_EXPR, boolean_false_node, ctxt);
+	  }
+      }
+      break;
+
+    case BIT_IOR_EXPR:
+      {
+	if (!is_true)
+	  {
+	    /* ...and "LHS == (rhs1 | rhs2)
+	       i.e. "(rhs1 | rhs2)" is false
+	       then both rhs1 and rhs2 must be false.  */
+	    tree rhs1 = gimple_assign_rhs1 (assign);
+	    tree rhs2 = gimple_assign_rhs2 (assign);
+	    add_constraint (rhs1, EQ_EXPR, boolean_false_node, ctxt);
+	    add_constraint (rhs2, EQ_EXPR, boolean_false_node, ctxt);
+	  }
+      }
+      break;
+
+    case EQ_EXPR:
+    case NE_EXPR:
+      {
+	/* ...and "LHS == (rhs1 OP rhs2)"
+	   then rhs1 OP rhs2 must have the same logical value as LHS.  */
+	tree rhs1 = gimple_assign_rhs1 (assign);
+	tree rhs2 = gimple_assign_rhs2 (assign);
+	if (!is_true)
+	  rhs_code
+	    = invert_tree_comparison (rhs_code, false /* honor_nans */);
+	add_constraint (rhs1, rhs_code, rhs2, ctxt);
+      }
+      break;
+    }
+}
+
+/* Add any constraints for an SSA_NAME defined by CALL
+   where the result OP RHS.  */
+
+void
+region_model2::add_any_constraints_from_gcall (enum tree_code op,
+					       tree rhs,
+					       const gcall *call,
+					       region_model2_context *ctxt)
+{
+  if (gimple_call_builtin_p (call, BUILT_IN_EXPECT)
+      || gimple_call_builtin_p (call, BUILT_IN_EXPECT_WITH_PROBABILITY)
+      || gimple_call_internal_p (call, IFN_BUILTIN_EXPECT))
+    {
+      /* __builtin_expect's return value is its initial argument.  */
+      add_constraint (gimple_call_arg (call, 0), op, rhs, ctxt);
+    }
+}
+
+/* Determine what is known about the condition "LHS OP RHS" within
+   this model.
+   Use CTXT for reporting any diagnostics associated with the accesses.  */
+
+tristate
+region_model2::eval_condition (tree lhs,
+			       enum tree_code op,
+			       tree rhs,
+			       region_model2_context *ctxt)
+{
+  /* For now, make no attempt to model constraints on floating-point
+     values.  */
+  if (FLOAT_TYPE_P (TREE_TYPE (lhs)) || FLOAT_TYPE_P (TREE_TYPE (rhs)))
+    return tristate::unknown ();
+
+  return eval_condition (get_rvalue (lhs, ctxt),
+			 op,
+			 get_rvalue (rhs, ctxt));
+}
+
+/* If SID is a constant value, return the underlying tree constant.
+   Otherwise, return NULL_TREE.  */
+#if 0
+tree
+region_model2::maybe_get_constant (const svalue2 *sval) const
+{
+  gcc_assert (!sid.null_p ());
+  svalue2 *sval = get_svalue2 (sid);
+  return sval->maybe_get_constant ();
+}
+#endif
+
+/* Create a new child region2 of the heap (creating the heap region2 if
+   necessary).
+   Return the region2 *of the new child region2.  */
+#if 0
+region2_id
+region_model2::add_new_malloc_region2 ()
+{
+  region2 *heap_reg
+    = get_root_region2 ()->ensure_heap_region2 (this);
+  return add_region2 (new symbolic_region2 (heap_reg, NULL_TREE, true));
+}
+#endif
+
+/* Attempt to return a path_var that represents SVAL, or return NULL_TREE.
+   Use VISITED to prevent infinite mutual recursion with the overload for
+   regions.  */
+
+path_var
+region_model2::get_representative_path_var (const svalue2 *sval,
+					    hash_set <const svalue2 *> *visited) const
+{
+  if (sval == NULL)
+    return path_var (NULL_TREE, 0);
+
+  if (const svalue2 *cast_sval = sval->maybe_undo_cast ())
+    sval = cast_sval;
+
+  /* Prevent infinite recursion.  */
+  if (visited->contains (sval))
+    return path_var (NULL_TREE, 0);
+  visited->add (sval);
+
+  auto_vec<path_var> pvs;
+  m_store.get_representative_path_vars (this, visited, sval, &pvs);
+
+  if (tree cst = sval->maybe_get_constant ())
+    pvs.safe_push (path_var (cst, 0));
+
+  /* Handle string literals and various other pointers.  */
+  if (const region_svalue2 *ptr_sval = sval->dyn_cast_region_svalue2 ())
+    {
+      const region2 *reg = ptr_sval->get_pointee ();
+      if (path_var pv = get_representative_path_var (reg, visited))
+	return path_var (build1 (ADDR_EXPR,
+				 TREE_TYPE (sval->get_type ()),
+				 pv.m_tree),
+			 pv.m_stack_depth);
+    }
+
+#if CONJURED_VALUES_FOR_UNKNOWN_FNCALLS
+  /* If we have a sub_svalue, look for ways to represent the parent.  */
+  if (const sub_svalue2 *sub_sval = sval->dyn_cast_sub_svalue2 ())
+    {
+      const svalue2 *parent_sval = sub_sval->get_parent ();
+      const region2 *subreg = sub_sval->get_subregion ();
+      if (path_var parent_pv
+	    = get_representative_path_var (parent_sval, visited))
+	if (const field_region2 *field_reg = subreg->dyn_cast_field_region2 ())
+	  return path_var (build3 (COMPONENT_REF,
+				   sval->get_type (),
+				   parent_pv.m_tree,
+				   field_reg->get_field (),
+				   NULL_TREE),
+			   parent_pv.m_stack_depth);
+    }
+#endif
+
+  if (pvs.length () < 1)
+    return path_var (NULL_TREE, 0);
+
+  pvs.qsort (readability_comparator);
+  return pvs[0];
+
+#if 0
+  /* Find the first region2 that stores the value (e.g. a local) and
+     generate a representative tree for it.  */
+  unsigned i;
+  region2 *region2;
+  FOR_EACH_VEC_ELT (m_region2s, i, region2)
+    if (sid == region2->get_value_direct ())
+      {
+	path_var pv = get_representative_path_var (region2_id::from_int (i));
+	if (pv.m_tree)
+	  return pv.m_tree;
+      }
+
+  return maybe_get_constant (sid);
+#endif
+}
+
+/* Attempt to return a tree that represents SVAL, or return NULL_TREE.  */
+
+tree
+region_model2::get_representative_tree (const svalue2 *sval) const
+{
+  hash_set <const svalue2 *> visited;
+  return get_representative_path_var (sval, &visited).m_tree;
+}
+
+/* Attempt to return a path_var that represents REG, or return
+   the NULL path_var.
+   For example, a region2 for a field of a local would be a path_var
+   wrapping a COMPONENT_REF.
+   Use VISITED to prevent infinite mutual recursion with the overload for
+   svalue2s.  */
+
+path_var
+region_model2::get_representative_path_var (const region2 *reg,
+					    hash_set <const svalue2 *> *visited) const
+{
+  switch (reg->get_kind ())
+    {
+    default:
+      gcc_unreachable ();
+
+    case region2::RK_FRAME:
+    case region2::RK_GLOBALS:
+    case region2::RK_CODE:
+    case region2::RK_HEAP:
+    case region2::RK_STACK:
+    case region2::RK_ROOT:
+       /* Regions that represent memory spaces are not expressible as trees.  */
+      return path_var (NULL_TREE, 0);
+
+    case region2::RK_FUNCTION:
+      {
+	const function_region2 *function_reg
+	  = as_a <const function_region2 *> (reg);
+	return path_var (function_reg->get_fndecl (), 0);
+      }
+    case region2::RK_LABEL:
+      gcc_unreachable (); // TODO
+
+    case region2::RK_SYMBOLIC:
+      {
+	const symbolic_region2 *symbolic_reg
+	  = as_a <const symbolic_region2 *> (reg);
+	const svalue2 *pointer = symbolic_reg->get_pointer ();
+	path_var pointer_pv = get_representative_path_var (pointer, visited);
+	if (!pointer_pv)
+	  return path_var (NULL_TREE, 0);
+	tree offset = build_int_cst (pointer->get_type (), 0);
+	return path_var (build2 (MEM_REF,
+				 reg->get_type (),
+				 pointer_pv.m_tree,
+				 offset),
+			 pointer_pv.m_stack_depth);
+      }
+    case region2::RK_DECL:
+      {
+	const decl_region2 *decl_reg = as_a <const decl_region2 *> (reg);
+	return path_var (decl_reg->get_decl (), decl_reg->get_stack_depth ());
+      }
+    case region2::RK_FIELD:
+      {
+	const field_region2 *field_reg = as_a <const field_region2 *> (reg);
+	path_var parent_pv
+	  = get_representative_path_var (reg->get_parent_region (), visited);
+	if (!parent_pv)
+	  return path_var (NULL_TREE, 0);
+	return path_var (build3 (COMPONENT_REF,
+				 reg->get_type (),
+				 parent_pv.m_tree,
+				 field_reg->get_field (),
+				 NULL_TREE),
+			 parent_pv.m_stack_depth);
+      }
+
+    case region2::RK_ELEMENT:
+      {
+	const element_region2 *element_reg
+	  = as_a <const element_region2 *> (reg);
+	path_var parent_pv
+	  = get_representative_path_var (reg->get_parent_region (), visited);
+	if (!parent_pv)
+	  return path_var (NULL_TREE, 0);
+	path_var index_pv
+	  = get_representative_path_var (element_reg->get_index (), visited);
+	if (!index_pv)
+	  return path_var (NULL_TREE, 0);
+	return path_var (build4 (ARRAY_REF,
+				 reg->get_type (),
+				 parent_pv.m_tree, index_pv.m_tree,
+				 NULL_TREE, NULL_TREE),
+			 parent_pv.m_stack_depth);
+      }
+
+    case region2::RK_OFFSET:
+      {
+	const offset_region2 *offset_reg
+	  = as_a <const offset_region2 *> (reg);
+	path_var parent_pv
+	  = get_representative_path_var (reg->get_parent_region (), visited);
+	if (!parent_pv)
+	  return path_var (NULL_TREE, 0);
+	path_var offset_pv
+	  = get_representative_path_var (offset_reg->get_byte_offset (),
+					 visited);
+	if (!offset_pv)
+	  return path_var (NULL_TREE, 0);
+	return path_var (build2 (MEM_REF,
+				 reg->get_type (),
+				 parent_pv.m_tree, offset_pv.m_tree),
+			 parent_pv.m_stack_depth);
+      }
+
+    case region2::RK_CAST:
+      {
+	path_var parent_pv
+	  = get_representative_path_var (reg->get_parent_region (), visited);
+	if (!parent_pv)
+	  return path_var (NULL_TREE, 0);
+	return path_var (build1 (NOP_EXPR,
+				 reg->get_type (),
+				 parent_pv.m_tree),
+			 parent_pv.m_stack_depth);
+      }
+
+    case region2::RK_HEAP_ALLOCATED:
+    case region2::RK_ALLOCA:
+      /* No good way to express heap-allocated/alloca regions as trees.  */
+      return path_var (NULL_TREE, 0);
+
+    case region2::RK_STRING:
+      {
+	const string_region2 *string_reg = as_a <const string_region2 *> (reg);
+	return path_var (string_reg->get_string_cst (), 0);
+      }
+
+    case region2::RK_UNKNOWN:
+      return path_var (NULL_TREE, 0);
+    }
+}
+
+/* Locate all region2s that directly have value SVAL and append representative
+   path_var instances for them into *OUT.  */
+/* FIXME: other ways of getting to the svalue e.g. for initial_svalue2s?
+   Also: this might not be the best human-readable description to use.  */
+#if 0
+void
+region_model2::get_path_vars_for_svalue2 (const svalue2 *sval,
+					  int i,
+					  auto_vec<path_var> *out) const
+{
+  return m_store.get_representative_path_vars (this, sval, out);
+
+#if 0
+  unsigned i;
+  region2 *region2;
+  FOR_EACH_VEC_ELT (m_region2s, i, region2)
+    if (sval == region2->get_value_direct ())
+      {
+	path_var pv = get_representative_path_var (sval);
+	if (pv.m_tree)
+	  out->safe_push (pv);
+      }
+#endif
+}
+#endif
+
+/* Set DST_REG value to be a new unknown value of type TYPE.  */
+#if 0
+svalue2_id
+region_model2::set_to_new_unknown_value (region2 *dst_reg, tree type,
+					region_model2_context *ctxt)
+{
+  gcc_assert (!dst_reg.null_p ());
+  const svalue2 *new_sval = add_svalue2 (new unknown_svalue2 (type));
+  set_value (dst_reg, new_sval, ctxt);
+
+  // TODO: presumably purge all child region2s too (but do this in set_value?)
+
+  return new_sval;
+}
+#endif
+
+/* Update this model for any phis in SNODE, assuming we came from
+   LAST_CFG_SUPEREDGE.  */
+
+void
+region_model2::update_for_phis (const supernode *snode,
+			       const cfg_superedge *last_cfg_superedge,
+			       region_model2_context *ctxt)
+{
+  gcc_assert (last_cfg_superedge);
+
+  for (gphi_iterator gpi = const_cast<supernode *>(snode)->start_phis ();
+       !gsi_end_p (gpi); gsi_next (&gpi))
+    {
+      gphi *phi = gpi.phi ();
+
+      tree src = last_cfg_superedge->get_phi_arg (phi);
+      tree lhs = gimple_phi_result (phi);
+
+      /* Update next_state based on phi.  */
+      bool is_back_edge = last_cfg_superedge->back_edge_p ();
+      handle_phi (phi, lhs, src, is_back_edge, ctxt);
+    }
+}
+
+/* Attempt to update this model for taking EDGE (where the last statement
+   was LAST_STMT), returning true if the edge can be taken, false
+   otherwise.
+
+   For CFG superedges where LAST_STMT is a conditional or a switch
+   statement, attempt to add the relevant conditions for EDGE to this
+   model, returning true if they are feasible, or false if they are
+   impossible.
+
+   For call superedges, push frame information and store arguments
+   into parameters.
+
+   For return superedges, pop frame information and store return
+   values into any lhs.
+
+   Rejection of call/return superedges happens elsewhere, in
+   program_point::on_edge (i.e. based on program point, rather
+   than program state).  */
+
+bool
+region_model2::maybe_update_for_edge (const superedge &edge,
+				      const gimple *last_stmt,
+				      region_model2_context *ctxt)
+{
+  /* Handle frame updates for interprocedural edges.  */
+  switch (edge.m_kind)
+    {
+    default:
+      break;
+
+    case SUPEREDGE_CALL:
+      {
+	const call_superedge *call_edge = as_a <const call_superedge *> (&edge);
+	update_for_call_superedge (*call_edge, ctxt);
+      }
+      break;
+
+    case SUPEREDGE_RETURN:
+      {
+	const return_superedge *return_edge
+	  = as_a <const return_superedge *> (&edge);
+	update_for_return_superedge (*return_edge, ctxt);
+      }
+      break;
+
+    case SUPEREDGE_INTRAPROCEDURAL_CALL:
+      {
+	const callgraph_superedge *cg_sedge
+	  = as_a <const callgraph_superedge *> (&edge);
+	update_for_call_summary (*cg_sedge, ctxt);
+      }
+      break;
+    }
+
+  if (last_stmt == NULL)
+    return true;
+
+  /* Apply any constraints for conditionals/switch statements.  */
+
+  if (const gcond *cond_stmt = dyn_cast <const gcond *> (last_stmt))
+    {
+      const cfg_superedge *cfg_sedge = as_a <const cfg_superedge *> (&edge);
+      return apply_constraints_for_gcond (*cfg_sedge, cond_stmt, ctxt);
+    }
+
+  if (const gswitch *switch_stmt = dyn_cast <const gswitch *> (last_stmt))
+    {
+      const switch_cfg_superedge *switch_sedge
+	= as_a <const switch_cfg_superedge *> (&edge);
+      return apply_constraints_for_gswitch (*switch_sedge, switch_stmt,
+					    ctxt);
+    }
+
+  return true;
+}
+
+/* Push a new frame_region2 on to the stack region2.
+   Populate the frame_region2 with child region2s for the function call's
+   parameters, using values from the arguments at the callsite in the
+   caller's frame.  */
+
+void
+region_model2::update_for_call_superedge (const call_superedge &call_edge,
+					  region_model2_context *ctxt)
+{
+  /* Build a vec of argument svalue2_id, using the current top
+     frame for resolving tree expressions.  */
+  const gcall *call_stmt = call_edge.get_call_stmt ();
+  auto_vec<const svalue2 *> arg_svals (gimple_call_num_args (call_stmt));
+
+  for (unsigned i = 0; i < gimple_call_num_args (call_stmt); i++)
+    {
+      tree arg = gimple_call_arg (call_stmt, i);
+      arg_svals.quick_push (get_rvalue (arg, ctxt));
+    }
+
+  push_frame (call_edge.get_callee_function (), &arg_svals, ctxt);
+}
+
+/* Pop the top-most frame_region2 from the stack, and copy the return
+   region2's values (if any) into the region2 for the lvalue of the LHS of
+   the call (if any).  */
+void
+region_model2::update_for_return_superedge (const return_superedge &return_edge,
+					    region_model2_context *ctxt)
+{
+  /* Get the region2 for the result of the call, within the caller frame.  */
+  const region2 *result_dst_reg = NULL;
+  const gcall *call_stmt = return_edge.get_call_stmt ();
+  tree lhs = gimple_call_lhs (call_stmt);
+  if (lhs)
+    {
+      /* Normally we access the top-level frame, which is:
+	   path_var (expr, get_stack_depth () - 1)
+	 whereas here we need the caller frame, hence "- 2" here.  */
+      gcc_assert (get_stack_depth () >= 2);
+      result_dst_reg = get_lvalue (path_var (lhs, get_stack_depth () - 2),
+				   ctxt);
+    }
+
+  purge_stats2 stats;
+  pop_frame (result_dst_reg, true, &stats, NULL, ctxt);
+
+  // TODO: do something with the stats?
+
+#if 0
+  if (!lhs)
+    {
+      /* This could be a leak; try purging again, but this time,
+	 don't special-case the result sids (as was done in pop_frame).  */
+      purge_unused_svalue2s (&stats, ctxt);
+    }
+#endif
+}
+/* Update this region_model2 with a summary of the effect of calling
+   and returning from CG_SEDGE.
+
+   TODO: Currently this is extremely simplistic: we merely set the
+   return value to "unknown".  A proper implementation would e.g. update
+   sm-state, and presumably be reworked to support multiple outcomes.  */
+void
+region_model2::update_for_call_summary (const callgraph_superedge &cg_sedge,
+					region_model2_context *ctxt)
+{
+  /* For now, set any return value to "unknown".  */
+  const gcall *call_stmt = cg_sedge.get_call_stmt ();
+  tree lhs = gimple_call_lhs (call_stmt);
+  if (lhs)
+    mark_region_as_unknown (get_lvalue (lhs, ctxt));
+
+  // TODO: actually implement some kind of summary here
+}
+
+/* Given a true or false edge guarded by conditional statement COND_STMT,
+   determine appropriate constraints for the edge to be taken.
+
+   If they are feasible, add the constraints and return true.
+
+   Return false if the constraints contradict existing knowledge
+   (and so the edge should not be taken).  */
+
+bool
+region_model2::apply_constraints_for_gcond (const cfg_superedge &sedge,
+					    const gcond *cond_stmt,
+					    region_model2_context *ctxt)
+{
+  ::edge cfg_edge = sedge.get_cfg_edge ();
+  gcc_assert (cfg_edge != NULL);
+  gcc_assert (cfg_edge->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE));
+
+  enum tree_code op = gimple_cond_code (cond_stmt);
+  tree lhs = gimple_cond_lhs (cond_stmt);
+  tree rhs = gimple_cond_rhs (cond_stmt);
+  if (cfg_edge->flags & EDGE_FALSE_VALUE)
+    op = invert_tree_comparison (op, false /* honor_nans */);
+  return add_constraint (lhs, op, rhs, ctxt);
+}
+
+/* Given an EDGE guarded by SWITCH_STMT, determine appropriate constraints
+   for the edge to be taken.
+
+   If they are feasible, add the constraints and return true.
+
+   Return false if the constraints contradict existing knowledge
+   (and so the edge should not be taken).  */
+
+bool
+region_model2::apply_constraints_for_gswitch (const switch_cfg_superedge &edge,
+					      const gswitch *switch_stmt,
+					      region_model2_context *ctxt)
+{
+  tree index  = gimple_switch_index (switch_stmt);
+  tree case_label = edge.get_case_label ();
+  gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR);
+  tree lower_bound = CASE_LOW (case_label);
+  tree upper_bound = CASE_HIGH (case_label);
+  if (lower_bound)
+    {
+      if (upper_bound)
+	{
+	  /* Range.  */
+	  if (!add_constraint (index, GE_EXPR, lower_bound, ctxt))
+	    return false;
+	  return add_constraint (index, LE_EXPR, upper_bound, ctxt);
+	}
+      else
+	/* Single-value.  */
+	return add_constraint (index, EQ_EXPR, lower_bound, ctxt);
+    }
+  else
+    {
+      /* The default case.
+	 Add exclusions based on the other cases.  */
+      for (unsigned other_idx = 1;
+	   other_idx < gimple_switch_num_labels (switch_stmt);
+	   other_idx++)
+	{
+	  tree other_label = gimple_switch_label (switch_stmt,
+						  other_idx);
+	  tree other_lower_bound = CASE_LOW (other_label);
+	  tree other_upper_bound = CASE_HIGH (other_label);
+	  gcc_assert (other_lower_bound);
+	  if (other_upper_bound)
+	    {
+	      /* Exclude this range-valued case.
+		 For now, we just exclude the boundary values.
+		 TODO: exclude the values within the region2.  */
+	      if (!add_constraint (index, NE_EXPR, other_lower_bound,
+				   ctxt))
+		return false;
+	      if (!add_constraint (index, NE_EXPR, other_upper_bound,
+				   ctxt))
+		return false;
+	    }
+	  else
+	    /* Exclude this single-valued case.  */
+	    if (!add_constraint (index, NE_EXPR, other_lower_bound, ctxt))
+	      return false;
+	}
+      return true;
+    }
+}
+
+/* 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"
+   the pointer, and thus calls to unknown functions could read/write into
+   the region.  */
+
+void
+region_model2::on_top_level_param (tree param,
+				   region_model2_context *ctxt)
+{
+  if (POINTER_TYPE_P (TREE_TYPE (param)))
+    {
+      const region2 *param_reg = get_lvalue (param, ctxt);
+      const svalue2 *init_ptr_sval
+	= m_mgr->get_or_create_initial_value (param_reg);
+      const region2 *pointee_reg = m_mgr->get_symbolic_region (init_ptr_sval);
+      m_store.mark_as_escaped (pointee_reg);
+    }
+}
+
+// FIXME:
+/* Create a new frame_region2 for a call to FUN and push it onto
+   the stack.
+
+   If ARG_SVALS is non-NULL, use it to populate the parameters
+   in the new frame.
+   Otherwise, populate them with unknown values.
+
+   Return the new frame_region2.  */
+
+const region2 *
+region_model2::push_frame (function *fun, vec<const svalue2 *> *arg_svals,
+			   region_model2_context *ctxt)
+{
+  m_current_frame = m_mgr->get_frame_region (m_current_frame, fun);
+  if (arg_svals)
+    {
+      /* Arguments supplied from a caller frame.  */
+      tree fndecl = fun->decl;
+      unsigned idx = 0;
+      for (tree iter_parm = DECL_ARGUMENTS (fndecl); iter_parm;
+	   iter_parm = DECL_CHAIN (iter_parm), ++idx)
+	{
+	  /* If there's a mismatching declaration, the call stmt might
+	     not have enough args.  Handle this case by leaving the
+	     rest of the params as uninitialized.  */
+	  if (idx >= arg_svals->length ())
+	    break;
+	  const svalue2 *arg_sval = (*arg_svals)[idx];
+	  const region2 *parm_reg = get_lvalue (iter_parm, ctxt);
+	  set_value (parm_reg, arg_sval, ctxt);
+
+	  /* Also do it for default SSA name (sharing the same value).  */
+	  tree parm_default_ssa = ssa_default_def (fun, iter_parm);
+	  if (parm_default_ssa)
+	    {
+	      const region2 *defssa_reg = get_lvalue (parm_default_ssa, ctxt);
+	      set_value (defssa_reg, arg_sval, ctxt);
+	    }
+	}
+    }
+  else
+    {
+      /* Otherwise we have a top-level call within the analysis.  The params
+	 have defined but unknown initial values.
+	 Anything they point to has escaped.  */
+      tree fndecl = fun->decl;
+      for (tree iter_parm = DECL_ARGUMENTS (fndecl); iter_parm;
+	   iter_parm = DECL_CHAIN (iter_parm))
+	{
+	  on_top_level_param (iter_parm, ctxt);
+	  /* TODO: also do it for default SSA name (have them share the
+	     same unknown initial value).  */
+	  tree parm_default_ssa = ssa_default_def (fun, iter_parm);
+	  if (parm_default_ssa)
+	    on_top_level_param (parm_default_ssa, ctxt);
+	}
+    }
+
+  return m_current_frame;
+}
+
+/* Get the function of the top-most frame in this region_model2's stack.
+   There must be such a frame.  */
+
+function *
+region_model2::get_current_function () const
+{
+  const frame_region2 *frame = get_current_frame ();
+  gcc_assert (frame);
+  return frame->get_function ();
+}
+
+/* Pop the topmost frame_region2 from this region_model2's stack;
+
+   FIXME:
+   If RESULT_DST_RID is non-null, copy any return value from the frame
+   into RESULT_DST_RID's region.
+
+   FIXME:
+   Purge the frame region and all its descendent regions.
+   Convert any pointers that point into such regions into
+   POISON_KIND_POPPED_STACK svalues.
+
+   FIXME:
+   If PURGE, then purge all unused svalues, with the exception of any
+   returned values.
+
+   FIXME:
+   Accumulate stats on purged entities into STATS.  */
+
+void
+region_model2::pop_frame (const region2 *result_dst_reg,
+			  bool purge, purge_stats2 *out,
+			  const svalue2 **out_result,
+			  region_model2_context *ctxt)
+{
+  gcc_assert (m_current_frame);
+
+  /* Evaluate the result, within the callee frame.  */
+  const frame_region2 *frame_reg = m_current_frame;
+#if 0
+  svalue2_id_set returned_svals;
+#endif
+  tree fndecl = m_current_frame->get_function ()->decl;
+  tree result = DECL_RESULT (fndecl);
+  if (result && TREE_TYPE (result) != void_type_node)
+    {
+      if (result_dst_reg)
+	{
+	  /* Copy the result to RESULT_DST_REG.  */
+	  copy_region (result_dst_reg,
+		       get_lvalue (result, ctxt),
+		       ctxt);
+	}
+      if (out_result)
+	*out_result = get_rvalue (result, ctxt);
+      if (purge)
+	{
+#if 0
+	  /* Populate returned_svals, to avoid purging them.  */
+	  region2_id return_reg = model->get_lvalue (result, NULL);
+	  region2_id_set returned_regs (model);
+	  model->get_descendents (return_reg, &returned_regs,
+				  region2_id::null ());
+	  for (unsigned i = 0; i < model->get_num_region2s (); i++)
+	    {
+	      region2_id rid = region2_id::from_int (i);
+	      if (returned_regs.region2_p (rid))
+		{
+		  const svalue2 *sval = model->get_region2 (rid)->get_value_direct ();
+		  returned_svals.add_svalue2 (sid);
+		}
+	    }
+#endif
+	}
+    }
+
+  /* Pop the frame.  */
+  m_current_frame = m_current_frame->get_calling_frame ();
+
+#if 1
+  unbind_region2_and_descendents (frame_reg,
+				  POISON_KIND_POPPED_STACK,
+				  out,
+				  ctxt ? ctxt->get_logger () : NULL);
+#endif
+
+  /* Delete unused svalue2s, but don't delete the return value(s).  */
+#if 0
+  if (purge)
+    purge_unused_svalue2s (out, ctxt, &returned_svals);
+#endif
+}
+
+
+/* Get the number of frames in this region_model2's stack.  */
+
+int
+region_model2::get_stack_depth () const
+{
+  const frame_region2 *frame = get_current_frame ();
+  if (frame)
+    return frame->get_stack_depth ();
+  else
+    return 0;
+}
+
+/* FIXME.  */
+
+const frame_region2 *
+region_model2::get_frame_at_index (int index) const
+{
+  const frame_region2 *frame = get_current_frame ();
+  if (frame)
+    {
+      gcc_assert (index >= 0);
+      gcc_assert (index <= frame->get_index ());
+      while (index != frame->get_index ())
+	{
+	  frame = frame->get_calling_frame ();
+	  gcc_assert (frame);
+	}
+      return frame;
+    }
+  else
+    return 0;
+}
+
+/* Get the function * at DEPTH within the call stack.  */
+#if 0
+function *
+region_model2::get_function_at_depth (unsigned depth) const
+{
+  stack_region2 *stack = get_root_region2 ()->get_stack_region2 (this);
+  gcc_assert (stack);
+  region2 *frame_reg = stack->get_frame_reg (depth);
+  frame_region2 *frame = get_region2 <frame_region2> (frame_reg);
+  return frame->get_function ();
+}
+#endif
+
+/* Change the meaning of SID to be NEW_SVAL
+   (e.g. when deferencing an unknown pointer, the pointer
+   becomes a pointer to a symbolic region2, so that all users
+   of the former unknown pointer are now effectively pointing
+   at the same region2).  */
+#if 0
+void
+region_model2::replace_svalue2 (const svalue2 *sval, const svalue2 *new_sval)
+{
+  gcc_assert (!sid.null_p ());
+  int idx = sid.as_int ();
+
+  gcc_assert (m_svalue2s[idx]);
+  gcc_assert (m_svalue2s[idx]->get_type () == new_sval->get_type ());
+  delete m_svalue2s[idx];
+
+  m_svalue2s[idx] = new_sval;
+}
+#endif
+
+/* Helper class for region_model2::purge_unused_svalue2s.  */
+
+#if 0
+class restrict_to_used_svalue2s : public purge_criteria
+{
+public:
+  restrict_to_used_svalue2s (const auto_sbitmap &used) : m_used (used) {}
+
+  bool should_purge_p (const svalue2 *sval) const FINAL OVERRIDE
+  {
+    gcc_assert (!sid.null_p ());
+    return !bitmap_bit_p (m_used, sid.as_int ());
+  }
+
+private:
+  const auto_sbitmap &m_used;
+};
+
+/* Remove unused svalue2s from this model, accumulating stats into STATS.
+   Unused svalue2s are deleted.  Doing so could reorder the svalue2s, and
+   thus change the meaning of svalue2_ids.
+
+   If CTXT is non-NULL, then it is notified about svalue2 *remappings,
+   and about svalue2_ids that are about to be deleted.  This allows e.g.
+   for warning about resource leaks, for the case where the svalue2
+   represents a resource handle in the user code (e.g. a FILE * or a malloc
+   buffer).
+
+   Amongst other things, removing unused svalue2s is important for ensuring
+   that the analysis of loops terminates.  Otherwise, we could generate a
+   succession of models with unreferenced "unknown" values, where the
+   number of redundant unknown values could grow without bounds, and each
+   such model would be treated as distinct.
+
+   If KNOWN_USED_SVALS is non-NULL, treat *KNOWN_USED_SVALS as used (this is for
+   handling values being returned from functions as their frame is popped,
+   since otherwise we'd have to simultaneously determine both the rvalue
+   of the return expr in the callee frame and the lvalue for the gcall's
+   assignment in the caller frame, and it seems cleaner to express all
+   lvalue and rvalue lookups implicitly relative to a "current" frame).
+   The svalue2_ids in *KNOWN_USED_SVALS are not remapped and hence this
+   call makes it invalid.  */
+
+void
+region_model2::purge_unused_svalue2s (purge_stats *stats,
+				    region_model2_context *ctxt,
+				    svalue2_id_set *known_used_svals)
+{
+  // TODO: might want to avoid a vfunc call just to do logging here:
+  logger *logger = ctxt ? ctxt->get_logger () : NULL;
+
+  LOG_SCOPE (logger);
+
+  auto_sbitmap used (m_svalue2s.length ());
+  bitmap_clear (used);
+
+  if (known_used_svals)
+    {
+      /* We can't use an sbitmap for known_used_svals as the number of
+	 svalue2s could have grown since it was created.  */
+      for (unsigned i = 0; i < get_num_svalue2s (); i++)
+	if (known_used_svals->svalue2_p (svalue2_id::from_int (i)))
+	  bitmap_set_bit (used, i);
+    }
+
+  /* Walk the region2s, marking sids that are used.  */
+  unsigned i;
+  region2 *r;
+  FOR_EACH_VEC_ELT (m_region2s, i, r)
+    {
+      const svalue2 *sval = r->get_value_direct ();
+      if (!sid.null_p ())
+	bitmap_set_bit (used, sid.as_int ());
+    }
+
+  /* Now purge any constraints involving svalue2s we don't care about.  */
+  restrict_to_used_svalue2s criterion (used);
+  m_constraints->purge (criterion, stats);
+
+  /* Mark any sids that are in constraints that survived.  */
+  {
+    equiv_class *ec;
+    FOR_EACH_VEC_ELT (m_constraints->m_equiv_classes, i, ec)
+      {
+	int j;
+	const svalue2 **sid;
+	FOR_EACH_VEC_ELT (ec->m_vars, j, sid)
+	  {
+	    gcc_assert (!sid->null_p ());
+	    bitmap_set_bit (used, sid->as_int ());
+	  }
+      }
+  }
+
+  /* Build a mapping from old-sid to new-sid so that we can preserve
+     order of the used IDs and move all redundant ones to the end.
+     Iterate though svalue2 IDs, adding used ones to the front of
+     the new list, and unused ones to the back.  */
+  svalue2_id_map map (m_svalue2s.length ());
+  int next_used_new_sval = 0;
+  int after_next_unused_new_sval = m_svalue2s.length ();
+  for (unsigned i = 0; i < m_svalue2s.length (); i++)
+    {
+      const svalue2 *src (svalue2_id::from_int (i));
+      if (bitmap_bit_p (used, i))
+	{
+	  if (logger)
+	    logger->log ("sv%i is used", i);
+	  map.put (src, svalue2_id::from_int (next_used_new_sval++));
+	}
+      else
+	{
+	  if (logger)
+	    logger->log ("sv%i is unused", i);
+	  map.put (src, svalue2_id::from_int (--after_next_unused_new_sval));
+	}
+    }
+  /* The two insertion points should have met.  */
+  gcc_assert (next_used_new_sval == after_next_unused_new_sval);
+
+  /* Now walk the region2s and the constraints, remapping sids,
+     so that all the redundant svalue2s are at the end.  */
+  remap_svalue2_ids (map);
+
+  if (logger)
+    {
+      logger->start_log_line ();
+      logger->log_partial ("map: ");
+      map.dump_to_pp (logger->get_printer ());
+      logger->end_log_line ();
+    }
+
+  /* Notify any client about the remapping and pending deletion.
+     Potentially this could trigger leak warnings.  */
+  if (ctxt)
+    {
+      ctxt->remap_svalue2_ids (map);
+      int num_client_items_purged
+	= ctxt->on_svalue2_purge (svalue2_id::from_int (next_used_new_sval), map);
+      if (stats)
+	stats->m_num_client_items += num_client_items_purged;
+    }
+
+  /* Drop the redundant svalue2s from the end of the vector.  */
+  while ((signed)m_svalue2s.length () > next_used_new_sval)
+    {
+      if (logger)
+	{
+	  const svalue2 *victim = svalue2_id::from_int (m_svalue2s.length () - 1);
+	  logger->log ("deleting sv%i (was sv%i)",
+		       victim.as_int (),
+		       map.get_src_for_dst (victim).as_int ());
+	}
+      delete m_svalue2s.pop ();
+      if (stats)
+	stats->m_num_svalue2s++;
+    }
+
+  validate ();
+}
+#endif
+
+/* Delete all region2s within SET_TO_PURGE, remapping region2 IDs for
+   other region2s.  It's required that there are no uses of the
+   region2s within the set (or the region2 IDs will become invalid).
+
+   Accumulate stats to STATS.  */
+#if 0
+void
+region_model2::purge_region2s (const region2_id_set &set_to_purge,
+			     purge_stats *stats,
+			     logger *)
+{
+  /* Build a mapping from old-rid to new-rid so that we can preserve
+     order of the used IDs and move all redundant ones to the end.
+     Iterate though region2 IDs, adding used ones to the front of
+     the new list, and unused ones to the back.  */
+  region2_id_map map (m_region2s.length ());
+  int next_used_new_reg = 0;
+  int after_next_unused_new_reg = m_region2s.length ();
+  for (unsigned i = 0; i < m_region2s.length (); i++)
+    {
+      region2 *src (region2_id::from_int (i));
+      if (set_to_purge.region2_p (src))
+	map.put (src, region2_id::from_int (--after_next_unused_new_reg));
+      else
+	map.put (src, region2_id::from_int (next_used_new_reg++));
+    }
+  /* The two insertion points should have met.  */
+  gcc_assert (next_used_new_reg == after_next_unused_new_reg);
+
+  /* Now walk the region2s and svalue2s, remapping rids,
+     so that all the redundant region2s are at the end.  */
+  remap_region2_ids (map);
+
+  /* Drop the redundant region2s from the end of the vector.  */
+  while ((signed)m_region2s.length () > next_used_new_reg)
+    {
+      delete m_region2s.pop ();
+      if (stats)
+	stats->m_num_region2s++;
+    }
+}
+#endif
+/* Populate *OUT with RID and all of its descendents.
+   If EXCLUDE_REG is non-null, then don't add it or its descendents.  */
+#if 0
+void
+region_model2::get_descendents (region2 *reg, region2_id_set *out,
+			       region2 *exclude_reg) const
+{
+  out->add_region2 (rid);
+
+  bool changed = true;
+  while (changed)
+    {
+      changed = false;
+      unsigned i;
+      region2 *r;
+      FOR_EACH_VEC_ELT (m_region2s, i, r)
+	{
+	  region2 *iter_reg = region2_id::from_int (i);
+	  if (iter_reg == exclude_reg)
+	    continue;
+	  if (!out->region2_p (iter_reg))
+	    {
+	      region2 *parent_reg = r->get_parent ();
+	      if (!parent_reg.null_p ())
+		if (out->region2_p (parent_reg))
+		  {
+		    out->add_region2 (iter_reg);
+		    changed = true;
+		  }
+	    }
+	}
+    }
+}
+#endif
+
+// FIXME
+/* Delete RID and all descendent region2s.
+   Find any pointers to such region2s; convert them to
+   poisoned values of kind PKIND.
+   Accumulate stats on purged entities into STATS.  */
+
+void
+region_model2::unbind_region2_and_descendents (const region2 *reg,
+					       enum poison_kind pkind,
+					       purge_stats2 *stats,
+					       logger *logger)
+{
+  /* Gather a set of base regions to be unbound.  */
+  hash_set<const region2 *> base_regs;
+  for (store2::cluster_map_t::iterator iter = m_store.begin ();
+       iter != m_store.end (); ++iter)
+    {
+      const region2 *iter_base_reg = (*iter).first;
+      if (iter_base_reg->descendent_of_p (reg))
+	base_regs.add (iter_base_reg);
+    }
+  for (hash_set<const region2 *>::iterator iter = base_regs.begin ();
+       iter != base_regs.end (); ++iter)
+    m_store.purge_cluster (*iter);
+
+  /* Find any pointers to REG or its descendents; convert to poisoned.  */
+  poison_any_pointers_to_descendents (reg, pkind);
+}
+
+/* Implementation of BindingVisitor.
+   Update the bound svalues for regions below REG to use poisoned
+   values instead.  */
+
+struct bad_pointer_finder
+{
+  bad_pointer_finder (const region2 *reg, enum poison_kind pkind,
+		      region_model2_manager *mgr)
+  : m_reg (reg), m_pkind (pkind), m_mgr (mgr), m_count (0)
+  {}
+
+  void on_binding (const binding_key2 *key, const svalue2 *&sval)
+  {
+    if (const region_svalue2 *ptr_sval = sval->dyn_cast_region_svalue2 ())
+      {
+	const region2 *ptr_dst = ptr_sval->get_pointee ();
+	/* Poison ptrs to descendents of REG, but not to REG itself,
+	   otherwise double-free detection doesn't work (since sm-state
+	   for "free" is stored on the original ptr svalue).  */
+	if (ptr_dst->descendent_of_p (m_reg)
+	    && ptr_dst != m_reg)
+	  {
+	    sval = m_mgr->get_or_create_poisoned_svalue2 (m_pkind,
+							  sval->get_type ());
+	    ++m_count;
+	  }
+      }
+  }
+
+  const region2 *m_reg;
+  enum poison_kind m_pkind;
+  region_model2_manager *const m_mgr;
+  int m_count;
+};
+
+/* Find any pointers to REG or its descendents; convert them to
+   poisoned values of kind PKIND.
+   Return the number of pointers that were poisoned.  */
+// FIXME: ^^ unit test for this?
+
+int
+region_model2::poison_any_pointers_to_descendents (const region2 *reg,
+						   enum poison_kind pkind)
+{
+  bad_pointer_finder bv (reg, pkind, m_mgr);
+  m_store.for_each_binding (bv);
+  return bv.m_count;
+}
+
+/* Attempt to merge THIS with OTHER_MODEL, writing the result
+   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);
+  gcc_assert (m_mgr == other_model.m_mgr);
+  gcc_assert (m_mgr == out_model->m_mgr);
+
+  if (m_current_frame != other_model.m_current_frame)
+    return false;
+  out_model->m_current_frame = m_current_frame;
+
+  model2_merger m (this, &other_model, point, out_model);
+
+  if (!store2::can_merge_p (&m_store, &other_model.m_store,
+			    &out_model->m_store, m_mgr->get_store2_manager (),
+			    &m))
+    return false;
+
+  /* Merge constraints.  */
+  constraint_manager2::merge (*m_constraints2,
+			      *other_model.m_constraints2,
+			      out_model->m_constraints2,
+			      m);
+
+  return true;
+}
+
+/* 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.  */
+#if 0
+svalue2_id
+region_model2::get_value_by_name (const char *name) const
+{
+  gcc_assert (name);
+  tree identifier = get_identifier (name);
+  return get_root_region2 ()->get_value_by_name (identifier, *this);
+}
+#endif
+/* Generate or reuse an svalue2 *within this model for an index
+   into an array of type PTR_TYPE, based on OFFSET_SVAL.  */
+#if 0
+svalue2_id
+region_model2::convert_byte_offset_to_array_index (tree ptr_type,
+						  svalue2 *offset_sval)
+{
+  gcc_assert (POINTER_TYPE_P (ptr_type));
+
+  if (tree offset_cst = maybe_get_constant (offset_sval))
+    {
+      tree elem_type = TREE_TYPE (ptr_type);
+
+      /* Arithmetic on void-pointers is a GNU C extension, treating the size
+	 of a void as 1.
+	 https://gcc.gnu.org/onlinedocs/gcc/Pointer-Arith.html  */
+      if (TREE_CODE (elem_type) == VOID_TYPE)
+	return offset_sval;
+
+      /* First, use int_size_in_bytes, to reject the case where we have an
+	 incomplete type, or a non-constant value.  */
+      HOST_WIDE_INT hwi_byte_size = int_size_in_bytes (elem_type);
+      if (hwi_byte_size > 0)
+	{
+	  /* Now call size_in_bytes to get the answer in tree form.  */
+	  tree byte_size = size_in_bytes (elem_type);
+	  gcc_assert (byte_size);
+	  /* Try to get a constant by dividing, ensuring that we're in a
+	     signed representation first.  */
+	  tree index
+	    = fold_binary (TRUNC_DIV_EXPR, ssizetype,
+			   fold_convert (ssizetype, offset_cst),
+			   fold_convert (ssizetype, byte_size));
+	  if (index && TREE_CODE (index) == INTEGER_CST)
+	    return get_or_create_constant_svalue2 (index);
+	}
+    }
+
+  /* Otherwise, we don't know the array index; generate a new unknown value.
+     TODO: do we need to capture the relationship between two unknown
+     values (the offset and the index)?  */
+  return add_svalue2 (new unknown_svalue2 (integer_type_node));
+}
+
+/* Get a region2 of type TYPE for PTR_SVAL[OFFSET_SVAL/sizeof (*PTR_SVAL)].
+
+   If OFFSET_SVAL is known to be zero, then dereference PTR_SVAL.
+   Otherwise, impose a view of "typeof(*PTR_SVAL)[]" on *PTR_SVAL,
+   and then get a view of type TYPE on the relevant array element.  */
+
+region2_id
+region_model2::get_or_create_mem_ref (tree type,
+				     const svalue2 *ptr_sval,
+				     const svalue2 *offset_sval,
+				     region_model2_context *ctxt)
+{
+  const svalue2 *ptr_sval = get_svalue2 (ptr_sval);
+  tree ptr_type = ptr_sval->get_type ();
+  gcc_assert (ptr_type);
+
+  region2 *raw_reg = deref_rvalue (ptr_sval, ctxt);
+
+  const svalue2 *offset_sval = get_svalue2 (offset_sval);
+  tree offset_type = offset_sval->get_type ();
+  gcc_assert (offset_type);
+
+  if (constant_svalue2 *cst_sval = offset_sval->dyn_cast_constant_svalue2 ())
+    {
+      if (zerop (cst_sval->get_constant ()))
+	{
+	  /* Handle the zero offset case.  */
+	  return get_or_create_view (raw_reg, type, ctxt);
+	}
+
+      /* If we're already within an array of the correct type,
+	 then we want to reuse that array, rather than starting
+	 a new view.
+	 If so, figure out our raw_reg's offset from its parent,
+	 if we can, and use that to offset OFFSET_SVAL, and create
+	 the element within the parent region2.  */
+      region2 *raw_reg = get_region2 (raw_reg);
+      region2 *parent_reg = raw_reg->get_parent ();
+      tree parent_type = get_region2 (parent_reg)->get_type ();
+      if (parent_type
+	  && TREE_CODE (parent_type) == ARRAY_TYPE)
+	{
+	  // TODO: check we have the correct parent type
+	  array_region2 *parent_array = get_region2 <array_region2> (parent_reg);
+	  array_region2::key_t key_for_raw_reg;
+	  if (parent_array->get_key_for_child_region2 (raw_reg,
+						      &key_for_raw_reg))
+	    {
+	      /* Convert from offset to index.  */
+	      const svalue2 *index_sval
+		= convert_byte_offset_to_array_index (ptr_type, offset_sval);
+	      if (tree index_cst
+		    = get_svalue2 (index_sval)->maybe_get_constant ())
+		{
+		  array_region2::key_t index_offset
+		    = array_region2::key_from_constant (index_cst);
+		  array_region2::key_t index_rel_to_parent
+		    = key_for_raw_reg + index_offset;
+		  tree index_rel_to_parent_cst
+		    = wide_int_to_tree (integer_type_node,
+					index_rel_to_parent);
+		  const svalue2 *index_sval
+		    = get_or_create_constant_svalue2 (index_rel_to_parent_cst);
+
+		  /* Carry on, using the parent region2 and adjusted index.  */
+		  region2 *element_reg
+		    = parent_array->get_element (this, raw_reg, index_sval,
+						 ctxt);
+		  return get_or_create_view (element_reg, type, ctxt);
+		}
+	    }
+	}
+    }
+
+  tree array_type = build_array_type (TREE_TYPE (ptr_type),
+				      integer_type_node);
+  region2 *array_view_reg = get_or_create_view (raw_reg, array_type, ctxt);
+  array_region2 *array_reg = get_region2 <array_region2> (array_view_reg);
+
+  const svalue2 *index_sval
+    = convert_byte_offset_to_array_index (ptr_type, offset_sval);
+
+  region2 *element_reg
+    = array_reg->get_element (this, array_view_reg, index_sval, ctxt);
+
+  return get_or_create_view (element_reg, type, ctxt);
+}
+
+/* Get a region2 of type TYPE for PTR_SVAL + OFFSET_SVAL.
+
+   If OFFSET_SVAL is known to be zero, then dereference PTR_SVAL.
+   Otherwise, impose a view of "typeof(*PTR_SVAL)[]" on *PTR_SVAL,
+   and then get a view of type TYPE on the relevant array element.  */
+
+region2_id
+region_model2::get_or_create_pointer_plus_expr (tree type,
+					       const svalue2 *ptr_sval,
+					       const svalue2 *offset_in_bytes_sval,
+					       region_model2_context *ctxt)
+{
+  return get_or_create_mem_ref (type,
+				ptr_sval,
+				offset_in_bytes_sval,
+				ctxt);
+}
+
+/* Get or create a view of type TYPE of the region2 with id RAW_ID.
+   Return the id of the view (or RAW *if it of the same type).  */
+
+region2_id
+region_model2::get_or_create_view (region2 *raw_reg, tree type,
+				  region_model2_context *ctxt)
+{
+  region2 *raw_region2 = get_region2 (raw_reg);
+
+  gcc_assert (TYPE_P (type));
+  if (type != raw_region2->get_type ())
+    {
+      /* If the region2 already has a view of the requested type,
+	 reuse it.  */
+      region2 *existing_view_reg = raw_region2->get_view (type, this);
+      if (!existing_view_reg.null_p ())
+	return existing_view_reg;
+
+      /* Otherwise, make one (adding it to the region_model2 and
+	 to the viewed region2).  */
+      region2 *view_reg = add_region2_for_type (raw_reg, type, ctxt);
+      raw_region2->add_view (view_reg, this);
+      // TODO: something to signify that this is a "view"
+      return view_reg;
+    }
+
+  return raw_reg;
+}
+#endif
+
+/* Attempt to get the fndecl used at CALL, if known, or NULL_TREE
+   otherwise.  */
+
+tree
+region_model2::get_fndecl_for_call (const gcall *call,
+				    region_model2_context *ctxt)
+{
+  tree fn_ptr = gimple_call_fn (call);
+  if (fn_ptr == NULL_TREE)
+    return NULL_TREE;
+  const svalue2 *fn_ptr_sval = get_rvalue (fn_ptr, ctxt);
+  if (const region_svalue2 *fn_ptr_ptr
+	= fn_ptr_sval->dyn_cast_region_svalue2 ())
+    {
+      const region2 *reg = fn_ptr_ptr->get_pointee ();
+      if (const function_region2 *fn_reg = reg->dyn_cast_function_region2 ())
+	{
+	  tree fn_decl = fn_reg->get_fndecl ();
+	  cgraph_node *node = cgraph_node::get (fn_decl);
+	  if (!node)
+	    return NULL_TREE;
+	  const cgraph_node *ultimate_node = node->ultimate_alias_target ();
+	  if (ultimate_node)
+	    return ultimate_node->decl;
+	}
+    }
+
+  return NULL_TREE;
+}
+
+/* Would be much simpler to use a lambda here, if it were supported.  */
+
+struct append_ssa_names_cb_data
+{
+  const region_model2 *model;
+  auto_vec<const decl_region2 *> *out;
+};
+
+/* FIXME.  */
+
+void
+region_model2::
+get_ssa_name_regions_for_current_frame (auto_vec<const decl_region2 *> *out)
+  const
+{
+  append_ssa_names_cb_data data;
+  data.model = this;
+  data.out = out;
+  m_store.for_each_cluster (append_ssa_names_cb, &data);
+}
+
+/* FIXME.  */
+
+void
+region_model2::append_ssa_names_cb (const region2 *base_reg,
+				    append_ssa_names_cb_data *cb_data)
+{
+  if (base_reg->get_parent_region () != cb_data->model->m_current_frame)
+    return;
+  if (const decl_region2 *decl_reg = base_reg->dyn_cast_decl_region2 ())
+    {
+      if (TREE_CODE (decl_reg->get_decl ()) == SSA_NAME)
+	cb_data->out->safe_push (decl_reg);
+    }
+}
+
+/* FIXME.  */
+
+const region2 *
+region_model2::create_region_for_heap_alloc (const svalue2 *size_in_bytes)
+{
+  const region2 *reg = m_mgr->create_region_for_heap_alloc ();
+  record_dynamic_extents (reg, size_in_bytes);
+  return reg;
+}
+
+/* FIXME.  */
+
+const region2 *
+region_model2::create_region_for_alloca (const svalue2 *size_in_bytes)
+{
+  const region2 *reg = m_mgr->create_region_for_alloca (m_current_frame);
+  record_dynamic_extents (reg, size_in_bytes);
+  return reg;
+}
+
+/* FIXME.  */
+
+void
+region_model2::record_dynamic_extents (const region2 *reg,
+				       const svalue2 *size_in_bytes)
+{
+#if 0
+  const svalue2 *extents_sval = m_mgr->get_or_create_extents_svalue2 (reg);
+  add_constraint (extents_sval, EQ_EXPR, size_in_bytes);
+  /* TODO: This needs the ability to add constraints in the model based on an
+     svalue2.  */
+  /* TODO: should be capture where we got the extents from?  (so that we can
+     display that to the user)  Perhaps we can just walk backwards along
+     the exploded_path to find where the constraint was added.  */
+  /* FIXME: or should the dynamic extents instead be recorded in a special
+     data map? */
+#endif
+}
+
+/* struct model2_merger.  */
+
+/* Dump a multiline representation of this merger to PP.  */
+
+void
+model2_merger::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  pp_string (pp, "model A:");
+  pp_newline (pp);
+  m_model_a->dump_to_pp (pp, simple, true);
+  pp_newline (pp);
+
+  pp_string (pp, "model B:");
+  pp_newline (pp);
+  m_model_b->dump_to_pp (pp, simple, true);
+  pp_newline (pp);
+
+  pp_string (pp, "merged model:");
+  pp_newline (pp);
+  m_merged_model->dump_to_pp (pp, simple, true);
+  pp_newline (pp);
+
+#if ITERATED_BINOPS
+  pp_string (pp, "iterated binops:");
+  pp_newline (pp);
+  unsigned i;
+  const binop_svalue2 *binop;
+  FOR_EACH_VEC_ELT (m_iterated_binops, i, binop)
+    {
+      pp_string (pp, "  ");
+      binop->dump_to_pp (pp, simple);
+      pp_newline (pp);
+    }
+#endif
+}
+
+/* Dump a multiline representation of this merger to FILE.  */
+
+void
+model2_merger::dump (FILE *fp, 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 = fp;
+  dump_to_pp (&pp, simple);
+  pp_flush (&pp);
+}
+
+/* Dump a multiline representation of this merger to stderr.  */
+
+DEBUG_FUNCTION void
+model2_merger::dump (bool simple) const
+{
+  dump (stderr, simple);
+}
+
+/* FIXME.  */
+#if ITERATED_BINOPS
+void
+model2_merger::add_iterating_widening_binop_thing (const binop_svalue2 *binop_sval)
+{
+  // TODO!
+  m_iterated_binops.safe_push (binop_sval);
+}
+#endif
+
+/* struct svalue2_id_merger_mapping.  */
+
+#if 0
+/* svalue2_id_merger_mapping's ctor.  */
+
+svalue2_id_merger_mapping::svalue2_id_merger_mapping (const region_model2 &a,
+						    const region_model2 &b)
+: m_map_from_a_to_m (a.get_num_svalue2s ()),
+  m_map_from_b_to_m (b.get_num_svalue2s ())
+{
+}
+
+/* Dump a multiline representation of this to PP.  */
+
+void
+svalue2_id_merger_mapping::dump_to_pp (pretty_printer *pp) const
+{
+  pp_string (pp, "svalue2 *map: model A to merged model:");
+  pp_newline (pp);
+  m_map_from_a_to_m.dump_to_pp (pp);
+  pp_newline (pp);
+
+  pp_string (pp, "svalue2 *map: model B to merged model:");
+  pp_newline (pp);
+  m_map_from_b_to_m.dump_to_pp (pp);
+  pp_newline (pp);
+}
+
+/* Dump a multiline representation of this to FILE.  */
+
+void
+svalue2_id_merger_mapping::dump (FILE *fp) const
+{
+  pretty_printer pp;
+  pp_format_decoder (&pp) = default_tree_printer;
+  pp_show_color (&pp) = pp_show_color (global_dc->printer);
+  pp.buffer->stream = fp;
+  dump_to_pp (&pp);
+  pp_flush (&pp);
+}
+
+/* Dump a multiline representation of this to stderr.  */
+
+DEBUG_FUNCTION void
+svalue2_id_merger_mapping::dump () const
+{
+  dump (stderr);
+}
+#endif
+
+} // namespace ana
+
+/* Dump RMODEL fully to stderr (i.e. without summarization).  */
+
+DEBUG_FUNCTION void
+debug (const region_model2 &rmodel)
+{
+  rmodel.dump (false);
+}
+
+/* class engine2.  */
+
+/* FIXME.  */
+
+void
+engine2::log_stats (logger *logger) const
+{
+  m_mgr.log_stats (logger, true);
+}
+
+namespace ana {
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Build a constant tree of the given type from STR.  */
+
+static tree
+build_real_cst_from_string (tree type, const char *str)
+{
+  REAL_VALUE_TYPE real;
+  real_from_string (&real, str);
+  return build_real (type, real);
+}
+
+/* Append various "interesting" constants to OUT (e.g. NaN).  */
+
+static void
+append_interesting_constants (auto_vec<tree> *out)
+{
+  out->safe_push (build_int_cst (integer_type_node, 0));
+  out->safe_push (build_int_cst (integer_type_node, 42));
+  out->safe_push (build_int_cst (unsigned_type_node, 0));
+  out->safe_push (build_int_cst (unsigned_type_node, 42));
+  out->safe_push (build_real_cst_from_string (float_type_node, "QNaN"));
+  out->safe_push (build_real_cst_from_string (float_type_node, "-QNaN"));
+  out->safe_push (build_real_cst_from_string (float_type_node, "SNaN"));
+  out->safe_push (build_real_cst_from_string (float_type_node, "-SNaN"));
+  out->safe_push (build_real_cst_from_string (float_type_node, "0.0"));
+  out->safe_push (build_real_cst_from_string (float_type_node, "-0.0"));
+  out->safe_push (build_real_cst_from_string (float_type_node, "Inf"));
+  out->safe_push (build_real_cst_from_string (float_type_node, "-Inf"));
+}
+
+/* Verify that tree_cmp is a well-behaved comparator for qsort, even
+   if the underlying constants aren't comparable.  */
+
+static void
+test_tree_cmp_on_constants ()
+{
+  auto_vec<tree> csts;
+  append_interesting_constants (&csts);
+
+  /* Try sorting every triple. */
+  const unsigned num = csts.length ();
+  for (unsigned i = 0; i < num; i++)
+    for (unsigned j = 0; j < num; j++)
+      for (unsigned k = 0; k < num; k++)
+	{
+	  auto_vec<tree> v (3);
+	  v.quick_push (csts[i]);
+	  v.quick_push (csts[j]);
+	  v.quick_push (csts[k]);
+	  v.qsort (tree_cmp);
+	}
+}
+
+/* Implementation detail of the ASSERT_CONDITION_*2 macros.  */
+
+void
+assert_condition2 (const location &loc,
+		   region_model2 &model,
+		   const svalue2 *lhs, tree_code op, const svalue2 *rhs,
+		   tristate expected)
+{
+  tristate actual = model.eval_condition (lhs, op, rhs);
+  ASSERT_EQ_AT (loc, actual, expected);
+}
+
+/* Implementation detail of the ASSERT_CONDITION_*2 macros.  */
+
+void
+assert_condition2 (const location &loc,
+		   region_model2 &model,
+		   tree lhs, tree_code op, tree rhs,
+		   tristate expected)
+{
+  tristate actual = model.eval_condition (lhs, op, rhs, NULL);
+  ASSERT_EQ_AT (loc, actual, expected);
+}
+
+/* Implementation detail of ASSERT_DUMP_TREE_EQ.  */
+
+static void
+assert_dump_tree_eq (const location &loc, tree t, const char *expected)
+{
+  auto_fix_quotes sentinel;
+  pretty_printer pp;
+  pp_format_decoder (&pp) = default_tree_printer;
+  dump_tree (&pp, t);
+  ASSERT_STREQ_AT (loc, pp_formatted_text (&pp), expected);
+}
+
+/* Assert that dump_tree (T) is EXPECTED.  */
+
+#define ASSERT_DUMP_TREE_EQ(T, EXPECTED) \
+  SELFTEST_BEGIN_STMT							\
+  assert_dump_tree_eq ((SELFTEST_LOCATION), (T), (EXPECTED)); \
+  SELFTEST_END_STMT
+
+/* Implementation detail of ASSERT_DUMP_EQ.  */
+
+static void
+assert_dump_eq (const location &loc,
+		const region_model2 &model,
+		bool summarize,
+		const char *expected)
+{
+  auto_fix_quotes sentinel;
+  pretty_printer pp;
+  pp_format_decoder (&pp) = default_tree_printer;
+
+  model.dump_to_pp (&pp, summarize, true);
+  ASSERT_STREQ_AT (loc, pp_formatted_text (&pp), expected);
+}
+
+/* Assert that MODEL.dump_to_pp (SUMMARIZE) is EXPECTED.  */
+
+#define ASSERT_DUMP_EQ(MODEL, SUMMARIZE, EXPECTED) \
+  SELFTEST_BEGIN_STMT							\
+  assert_dump_eq ((SELFTEST_LOCATION), (MODEL), (SUMMARIZE), (EXPECTED)); \
+  SELFTEST_END_STMT
+
+/* Smoketest for region_model2::dump_to_pp.  */
+
+static void
+test_dump ()
+{
+  region_model2_manager mgr;
+  region_model2 model (&mgr);
+
+  ASSERT_DUMP_EQ (model, false,
+		  "stack depth: 0\n"
+		  "m_called_unknown_fn: FALSE\n"
+		  "constraint_manager2:\n"
+		  "  equiv classes:\n"
+		  "  constraints:\n");
+  ASSERT_DUMP_EQ (model, true,
+		  "stack depth: 0\n"
+		  "m_called_unknown_fn: FALSE\n"
+		  "constraint_manager2:\n"
+		  "  equiv classes:\n"
+		  "  constraints:\n");
+}
+
+/* Helper function for selftests.  Create a struct or union type named NAME,
+   with the fields given by the FIELD_DECLS in FIELDS.
+   If IS_STRUCT is true create a RECORD_TYPE (aka a struct), otherwise
+   create a UNION_TYPE.  */
+
+static tree
+make_test_compound_type (const char *name, bool is_struct,
+			 const auto_vec<tree> *fields)
+{
+  tree t = make_node (is_struct ? RECORD_TYPE : UNION_TYPE);
+  TYPE_SIZE (t) = 0;
+
+  tree fieldlist = NULL;
+  int i;
+  tree field;
+  FOR_EACH_VEC_ELT (*fields, i, field)
+    {
+      gcc_assert (TREE_CODE (field) == FIELD_DECL);
+      DECL_CONTEXT (field) = t;
+      fieldlist = chainon (field, fieldlist);
+    }
+  fieldlist = nreverse (fieldlist);
+  TYPE_FIELDS (t) = fieldlist;
+
+  layout_type (t);
+
+#if 1
+  TYPE_NAME (t) = get_identifier (name);
+#else
+  tree id = get_identifier (name);
+  tree implicit_decl = build_decl (UNKNOWN_LOCATION, TYPE_DECL, id, t);
+  DECL_ARTIFICIAL (implicit_decl) = 1;
+  //SET_DECL_IMPLICIT_TYPEDEF_P (implicit_decl);
+  TYPE_NAME (t) = implicit_decl;
+  TYPE_STUB_DECL (t) = implicit_decl;
+
+  /* FIXME: ^^^ this needs to be a decl, or selftest-c++ crashes
+     printing types, treating the TYPE_NAME as a decl.
+     C++ FE uses create_implicit_typedef and makes a  TYPE_DECL.  */
+#endif
+
+  return t;
+}
+
+/* Selftest fixture for creating the type "struct coord {int x; int y; };".  */
+
+struct coord_test2
+{
+  coord_test2 ()
+  {
+    auto_vec<tree> fields;
+    m_x_field = build_decl (UNKNOWN_LOCATION, FIELD_DECL,
+			       get_identifier ("x"), integer_type_node);
+    fields.safe_push (m_x_field);
+    m_y_field = build_decl (UNKNOWN_LOCATION, FIELD_DECL,
+			       get_identifier ("y"), integer_type_node);
+    fields.safe_push (m_y_field);
+    m_coord_type = make_test_compound_type ("coord", true, &fields);
+  }
+
+  tree m_x_field;
+  tree m_y_field;
+  tree m_coord_type;
+};
+
+/* Verify that dumps can show struct fields.  */
+
+static void
+test_dump_2 ()
+{
+  coord_test2 ct;
+
+  tree c = build_global_decl ("c", ct.m_coord_type);
+  tree c_x = build3 (COMPONENT_REF, TREE_TYPE (ct.m_x_field),
+		     c, ct.m_x_field, NULL_TREE);
+  tree c_y = build3 (COMPONENT_REF, TREE_TYPE (ct.m_y_field),
+		     c, ct.m_y_field, NULL_TREE);
+
+  tree int_17 = build_int_cst (integer_type_node, 17);
+  tree int_m3 = build_int_cst (integer_type_node, -3);
+
+  region_model2_manager mgr;
+  region_model2 model (&mgr);
+  model.set_value (c_x, int_17, NULL);
+  model.set_value (c_y, int_m3, NULL);
+
+  // TODO: test get_value
+
+  /* Verify get_offset for "c.x".  */
+  {
+    const region2 *c_x_reg = model.get_lvalue (c_x, NULL);
+    region_offset offset = c_x_reg->get_offset ();
+    ASSERT_EQ (offset.get_base_region (), model.get_lvalue (c, NULL));
+    ASSERT_EQ (offset.get_bit_offset (), 0);
+  }
+
+  /* Verify get_offset for "c.y".  */
+  {
+    const region2 *c_y_reg = model.get_lvalue (c_y, NULL);
+    region_offset offset = c_y_reg->get_offset ();
+    ASSERT_EQ (offset.get_base_region (), model.get_lvalue (c, NULL));
+    ASSERT_EQ (offset.get_bit_offset (), INT_TYPE_SIZE);
+  }
+
+  // FIXME: test of casting to an "int *"
+
+  // FIXME: rename this test; it's doing much more that just dumps
+
+  /* Simplified dump.  */
+#if 0
+  ASSERT_DUMP_EQ (model, true,
+		  "base region: {c} has cluster: {"
+		  "binding key: {region: c, kind: direct, offset: 0},"
+		  " value: {17},"
+		  " binding key: {region: c, kind: direct, offset: 32},"
+		  " value: {-3}"
+		  "}");
+#endif
+
+  /* Full dump.  */
+#if 0
+  ASSERT_DUMP_EQ
+    (model, false,
+     "base region: {decl_region2(globals, `struct coord', `c')} has cluster: {binding key: {region: field_region2(decl_region2(globals, `struct coord', `c'), `int', `x'), kind: direct, offset: 0}, value: {constant_svalue2(`int', 17)}, binding key: {region: field_region2(decl_region2(globals, `struct coord', `c'), `int', `y'), kind: direct, offset: 0}, value: {constant_svalue2(`int', -3)}}");
+#endif
+}
+
+#if 0
+
+/* Verify that dumps can show array elements.  */
+
+static void
+test_dump_3 ()
+{
+  tree tlen = size_int (10);
+  tree arr_type = build_array_type (char_type_node, build_index_type (tlen));
+
+  tree a = build_global_decl ("a", arr_type);
+
+  region_model2 model;
+  tree int_0 = build_int_cst (integer_type_node, 0);
+  tree a_0 = build4 (ARRAY_REF, char_type_node,
+		     a, int_0, NULL_TREE, NULL_TREE);
+  tree char_A = build_int_cst (char_type_node, 'A');
+  model.set_value (a_0, char_A, NULL);
+
+  /* Simplified dump.  */
+  ASSERT_DUMP_EQ (model, true, "a[0]: 65");
+
+  /* Full dump.  */
+  ASSERT_DUMP_EQ
+    (model, false,
+     "r0: {kind: `root', parent: null, sval: null}\n"
+     "`-globals: r1: {kind: `globals', parent: r0, sval: null, map: {`a': r2}}\n"
+     "  `-`a': r2: {kind: `array', parent: r1, sval: null, type: `char[11]', array: {[0]: r3}}\n"
+     "    |: type: `char[11]'\n"
+     "    `-[0]: r3: {kind: `primitive', parent: r2, sval: sv1, type: `char'}\n"
+     "      |: sval: sv1: {type: `char', `65'}\n"
+     "      |: type: `char'\n"
+     "svalue2s:\n"
+     "  sv0: {type: `int', `0'}\n"
+     "  sv1: {type: `char', `65'}\n"
+     "constraint manager:\n"
+     "  equiv classes:\n"
+     "  constraints:\n");
+}
+
+#endif
+/* Verify that region_model2::get_representative_tree works as expected.  */
+
+static void
+test_get_representative_tree ()
+{
+  region_model2_manager mgr;
+
+  /* STRING_CST.  */
+  {
+    tree string_cst = build_string (4, "foo");
+    region_model2 m (&mgr);
+    const svalue2 *str_sval = m.get_rvalue (string_cst, NULL);
+    tree rep = m.get_representative_tree (str_sval);
+    ASSERT_EQ (rep, string_cst);
+  }
+
+  /* String literal.  */
+  {
+    tree string_cst_ptr = build_string_literal (4, "foo");
+    region_model2 m (&mgr);
+    const svalue2 *str_sval = m.get_rvalue (string_cst_ptr, NULL);
+    tree rep = m.get_representative_tree (str_sval);
+    ASSERT_DUMP_TREE_EQ (rep, "&\"foo\"[0]");
+  }
+
+  /* Value of a primitive local.  */
+  {
+    // TODO
+  }
+
+  /* Value of an element within an array.  */
+  {
+    tree tlen = size_int (10);
+    tree arr_type = build_array_type (char_type_node, build_index_type (tlen));
+    tree a = build_global_decl ("a", arr_type);
+    placeholder_svalue2 test_sval (char_type_node, "test value");
+
+    /* Value of a[3].  */
+    {
+      test_region_model2_context ctxt;
+      region_model2 model (&mgr);
+      tree int_3 = build_int_cst (integer_type_node, 3);
+      tree a_3 = build4 (ARRAY_REF, char_type_node,
+			 a, int_3, NULL_TREE, NULL_TREE);
+      const region2 *a_3_reg = model.get_lvalue (a_3, &ctxt);
+      model.set_value (a_3_reg, &test_sval, &ctxt);
+      tree rep = model.get_representative_tree (&test_sval);
+      ASSERT_DUMP_TREE_EQ (rep, "a[3]");
+    }
+
+    /* Value of a[0].  */
+    {
+      test_region_model2_context ctxt;
+      region_model2 model (&mgr);
+      tree idx = build_int_cst (integer_type_node, 0);
+      tree a_0 = build4 (ARRAY_REF, char_type_node,
+			 a, idx, NULL_TREE, NULL_TREE);
+      const region2 *a_0_reg = model.get_lvalue (a_0, &ctxt);
+      model.set_value (a_0_reg, &test_sval, &ctxt);
+      tree rep = model.get_representative_tree (&test_sval);
+      ASSERT_DUMP_TREE_EQ (rep, "a[0]");
+    }
+  }
+
+  /* Value of a field within a struct.  */
+  {
+    coord_test2 ct;
+
+    tree c = build_global_decl ("c", ct.m_coord_type);
+    tree c_x = build3 (COMPONENT_REF, TREE_TYPE (ct.m_x_field),
+		       c, ct.m_x_field, NULL_TREE);
+    tree c_y = build3 (COMPONENT_REF, TREE_TYPE (ct.m_y_field),
+		       c, ct.m_y_field, NULL_TREE);
+
+    test_region_model2_context ctxt;
+
+    /* Value of initial field.  */
+    {
+      region_model2 m (&mgr);
+      const region2 *c_x_reg = m.get_lvalue (c_x, &ctxt);
+      placeholder_svalue2 test_sval_x (integer_type_node, "test x val");
+      m.set_value (c_x_reg, &test_sval_x, &ctxt);
+      tree rep = m.get_representative_tree (&test_sval_x);
+      ASSERT_DUMP_TREE_EQ (rep, "c.x");
+    }
+
+    /* Value of non-initial field.  */
+    {
+      region_model2 m (&mgr);
+      const region2 *c_y_reg = m.get_lvalue (c_y, &ctxt);
+      placeholder_svalue2 test_sval_y (integer_type_node, "test y val");
+      m.set_value (c_y_reg, &test_sval_y, &ctxt);
+      tree rep = m.get_representative_tree (&test_sval_y);
+      ASSERT_DUMP_TREE_EQ (rep, "c.y");
+    }
+  }
+
+  /* Value of a field within a union.  */
+  {
+    // TODO
+  }
+}
+
+/* Verify that calling region_model2::get_rvalue repeatedly on the same
+   tree constant retrieves the same svalue2 *.  */
+
+static void
+test_unique_constants ()
+{
+  tree int_0 = build_int_cst (integer_type_node, 0);
+  tree int_42 = build_int_cst (integer_type_node, 42);
+
+  test_region_model2_context ctxt;
+  region_model2_manager mgr;
+  region_model2 model (&mgr);
+  ASSERT_EQ (model.get_rvalue (int_0, &ctxt), model.get_rvalue (int_0, &ctxt));
+  ASSERT_EQ (model.get_rvalue (int_42, &ctxt),
+	     model.get_rvalue (int_42, &ctxt));
+  ASSERT_NE (model.get_rvalue (int_0, &ctxt), model.get_rvalue (int_42, &ctxt));
+  ASSERT_EQ (ctxt.get_num_diagnostics (), 0);
+
+  /* A "(const int)42" will be a different tree from "(int)42)"...  */
+  tree const_int_type_node
+    = build_qualified_type (integer_type_node, TYPE_QUAL_CONST);
+  tree const_int_42 = build_int_cst (const_int_type_node, 42);
+  ASSERT_NE (int_42, const_int_42);
+  /* It should have a different const_svalue2.  */
+  const svalue2 *int_42_sval = model.get_rvalue (int_42, &ctxt);
+  const svalue2 *const_int_42_sval = model.get_rvalue (const_int_42, &ctxt);
+  ASSERT_NE (int_42_sval, const_int_42_sval);
+  /* But they should compare as equal.  */
+  ASSERT_CONDITION_TRUE2 (model, int_42_sval, EQ_EXPR, const_int_42_sval);
+  ASSERT_CONDITION_FALSE2 (model, int_42_sval, NE_EXPR, const_int_42_sval);
+}
+
+/* Verify that each type gets its own singleton unknown_svalue2 within a
+   region_model2_manager, and that NULL_TREE gets its own singleton.  */
+
+static void
+test_unique_unknowns ()
+{
+  region_model2_manager mgr;
+  const svalue2 *unknown_int
+    = mgr.get_or_create_unknown_svalue2 (integer_type_node);
+  /* Repeated calls with the same type should get the same "unknown"
+     svalue2.  */
+  const svalue2 *unknown_int_2
+    = mgr.get_or_create_unknown_svalue2 (integer_type_node);
+  ASSERT_EQ (unknown_int, unknown_int_2);
+
+  /* Different types (or the NULL type) should have different
+     unknown_svalue2s.  */
+  const svalue2 *unknown_NULL_type = mgr.get_or_create_unknown_svalue2 (NULL);
+  ASSERT_NE (unknown_NULL_type, unknown_int);
+
+  /* Repeated calls with NULL for the type should get the same "unknown"
+     svalue2.  */
+  const svalue2 *unknown_NULL_type_2 = mgr.get_or_create_unknown_svalue2 (NULL);
+  ASSERT_EQ (unknown_NULL_type, unknown_NULL_type_2);
+}
+
+#if 0
+/* Check that operator== and hashing works as expected for the
+   various svalue2 subclasses.  */
+
+static void
+test_svalue2_equality ()
+{
+  tree int_42 = build_int_cst (integer_type_node, 42);
+  tree int_0 = build_int_cst (integer_type_node, 0);
+
+  /* Create pairs instances of the various subclasses of svalue2,
+     testing for hash and equality between (this, this) and
+     (this, other of same subclass). */
+  const svalue2 *ptr_to_r0
+    = new region_svalue2 (ptr_type_node, region2_id::from_int (0));
+  const svalue2 *ptr_to_r1
+    = new region_svalue2 (ptr_type_node, region2_id::from_int (1));
+
+  ASSERT_EQ (ptr_to_r0->hash (), ptr_to_r0->hash ());
+  ASSERT_EQ (*ptr_to_r0, *ptr_to_r0);
+
+  ASSERT_NE (ptr_to_r0->hash (), ptr_to_r1->hash ());
+  ASSERT_NE (*ptr_to_r0, *ptr_to_r1);
+
+  const svalue2 *cst_int_42 = new constant_svalue2 (int_42);
+  const svalue2 *cst_int_0 = new constant_svalue2 (int_0);
+
+  ASSERT_EQ (cst_int_42->hash (), cst_int_42->hash ());
+  ASSERT_EQ (*cst_int_42, *cst_int_42);
+
+  ASSERT_NE (cst_int_42->hash (), cst_int_0->hash ());
+  ASSERT_NE (*cst_int_42, *cst_int_0);
+
+  const svalue2 *uninit = new poisoned_svalue2 (POISON_KIND_UNINIT, NULL_TREE);
+  const svalue2 *freed = new poisoned_svalue2 (POISON_KIND_FREED, NULL_TREE);
+
+  ASSERT_EQ (uninit->hash (), uninit->hash ());
+  ASSERT_EQ (*uninit, *uninit);
+
+  ASSERT_NE (uninit->hash (), freed->hash ());
+  ASSERT_NE (*uninit, *freed);
+
+  const svalue2 *unknown_0 = new unknown_svalue2 (ptr_type_node);
+  const svalue2 *unknown_1 = new unknown_svalue2 (ptr_type_node);
+  ASSERT_EQ (unknown_0->hash (), unknown_0->hash ());
+  ASSERT_EQ (*unknown_0, *unknown_0);
+  ASSERT_EQ (*unknown_1, *unknown_1);
+
+  /* Comparisons between different kinds of svalue2.  */
+  ASSERT_NE (*ptr_to_r0, *cst_int_42);
+  ASSERT_NE (*ptr_to_r0, *uninit);
+  ASSERT_NE (*ptr_to_r0, *unknown_0);
+  ASSERT_NE (*cst_int_42, *ptr_to_r0);
+  ASSERT_NE (*cst_int_42, *uninit);
+  ASSERT_NE (*cst_int_42, *unknown_0);
+  ASSERT_NE (*uninit, *ptr_to_r0);
+  ASSERT_NE (*uninit, *cst_int_42);
+  ASSERT_NE (*uninit, *unknown_0);
+  ASSERT_NE (*unknown_0, *ptr_to_r0);
+  ASSERT_NE (*unknown_0, *cst_int_42);
+  ASSERT_NE (*unknown_0, *uninit);
+
+  delete ptr_to_r0;
+  delete ptr_to_r1;
+  delete cst_int_42;
+  delete cst_int_0;
+  delete uninit;
+  delete freed;
+  delete unknown_0;
+  delete unknown_1;
+}
+
+/* Check that operator== and hashing works as expected for the
+   various region2 subclasses.  */
+
+static void
+test_region2_equality ()
+{
+  region2 *r0
+    = new primitive_region2 (region2_id::from_int (3), integer_type_node);
+  region2 *r1
+    = new primitive_region2 (region2_id::from_int (4), integer_type_node);
+
+  ASSERT_EQ (*r0, *r0);
+  ASSERT_EQ (r0->hash (), r0->hash ());
+  ASSERT_NE (*r0, *r1);
+  ASSERT_NE (r0->hash (), r1->hash ());
+
+  delete r0;
+  delete r1;
+
+  // TODO: test coverage for the map within a map_region2
+}
+#endif
+
+/* Verify that initial_svalue2 are handled as expected.  */
+
+static void
+test_initial_svalue2_folding ()
+{
+  region_model2_manager mgr;
+  tree x = build_global_decl ("x", integer_type_node);
+  tree y = build_global_decl ("y", integer_type_node);
+
+  test_region_model2_context ctxt;
+  region_model2 model (&mgr);
+  const svalue2 *x_init = model.get_rvalue (x, &ctxt);
+  const svalue2 *y_init = model.get_rvalue (y, &ctxt);
+  ASSERT_NE (x_init, y_init);
+  const region2 *x_reg = model.get_lvalue (x, &ctxt);
+  ASSERT_EQ (x_init, mgr.get_or_create_initial_value (x_reg));
+
+}
+
+/* Verify that unary ops are folded as expected.  */
+
+static void
+test_unaryop_svalue2_folding ()
+{
+  region_model2_manager mgr;
+  tree x = build_global_decl ("x", integer_type_node);
+  tree y = build_global_decl ("y", integer_type_node);
+
+  test_region_model2_context ctxt;
+  region_model2 model (&mgr);
+  const svalue2 *x_init = model.get_rvalue (x, &ctxt);
+  const svalue2 *y_init = model.get_rvalue (y, &ctxt);
+  const region2 *x_reg = model.get_lvalue (x, &ctxt);
+  ASSERT_EQ (x_init, mgr.get_or_create_initial_value (x_reg));
+
+  /* "(int)x" -> "x".  */
+  ASSERT_EQ (x_init, mgr.get_or_create_cast (integer_type_node, x_init));
+
+  /* "(void *)x" -> something other than "x".  */
+  ASSERT_NE (x_init, mgr.get_or_create_cast (ptr_type_node, x_init));
+
+  /* "!(x == y)" -> "x != y".  */
+  ASSERT_EQ (mgr.get_or_create_unaryop
+	       (boolean_type_node, TRUTH_NOT_EXPR,
+		mgr.get_or_create_binop (boolean_type_node, EQ_EXPR,
+					 x_init, y_init)),
+	     mgr.get_or_create_binop (boolean_type_node, NE_EXPR,
+				      x_init, y_init));
+  /* "!(x > y)" -> "x <= y".  */
+  ASSERT_EQ (mgr.get_or_create_unaryop
+	       (boolean_type_node, TRUTH_NOT_EXPR,
+		mgr.get_or_create_binop (boolean_type_node, GT_EXPR,
+					 x_init, y_init)),
+	     mgr.get_or_create_binop (boolean_type_node, LE_EXPR,
+				      x_init, y_init));
+}
+
+/* Verify that binops on constant svalues are folded.  */
+
+static void
+test_binop_svalue2_folding ()
+{
+#define NUM_CSTS 10
+  tree cst_int[NUM_CSTS];
+  region_model2_manager mgr;
+  const svalue2 *cst_sval[NUM_CSTS];
+  for (int i = 0; i < NUM_CSTS; i++)
+    {
+      cst_int[i] = build_int_cst (integer_type_node, i);
+      cst_sval[i] = mgr.get_or_create_constant_svalue2 (cst_int[i]);
+      ASSERT_EQ (cst_sval[i]->get_kind (), svalue2::SK_CONSTANT);
+      ASSERT_EQ (cst_sval[i]->maybe_get_constant (), cst_int[i]);
+    }
+
+  for (int i = 0; i < NUM_CSTS; i++)
+    for (int j = 0; j < NUM_CSTS; j++)
+      {
+	if (i != j)
+	  ASSERT_NE (cst_sval[i], cst_sval[j]);
+	if (i + j < NUM_CSTS)
+	  {
+	    const svalue2 *sum
+	      = mgr.get_or_create_binop (integer_type_node, PLUS_EXPR,
+					 cst_sval[i], cst_sval[j]);
+	    ASSERT_EQ (sum, cst_sval[i + j]);
+	  }
+	if (i - j >= 0)
+	  {
+	    const svalue2 *difference
+	      = mgr.get_or_create_binop (integer_type_node, MINUS_EXPR,
+					 cst_sval[i], cst_sval[j]);
+	    ASSERT_EQ (difference, cst_sval[i - j]);
+	  }
+	if (i * j < NUM_CSTS)
+	  {
+	    const svalue2 *product
+	      = mgr.get_or_create_binop (integer_type_node, MULT_EXPR,
+					 cst_sval[i], cst_sval[j]);
+	    ASSERT_EQ (product, cst_sval[i * j]);
+	  }
+	const svalue2 *eq = mgr.get_or_create_binop (integer_type_node, EQ_EXPR,
+					       cst_sval[i], cst_sval[j]);
+	ASSERT_EQ (eq, i == j ? cst_sval[1] : cst_sval [0]);
+	const svalue2 *neq = mgr.get_or_create_binop (integer_type_node, NE_EXPR,
+						cst_sval[i], cst_sval[j]);
+	ASSERT_EQ (neq, i != j ? cst_sval[1] : cst_sval [0]);
+	// etc
+      }
+
+  tree x = build_global_decl ("x", integer_type_node);
+
+  test_region_model2_context ctxt;
+  region_model2 model (&mgr);
+  const svalue2 *x_init = model.get_rvalue (x, &ctxt);
+
+  /* PLUS_EXPR folding.  */
+  const svalue2 *x_init_plus_zero
+    = mgr.get_or_create_binop (integer_type_node, PLUS_EXPR,
+			       x_init, cst_sval[0]);
+  ASSERT_EQ (x_init_plus_zero, x_init);
+  const svalue2 *zero_plus_x_init
+    = mgr.get_or_create_binop (integer_type_node, PLUS_EXPR,
+			       cst_sval[0], x_init);
+  ASSERT_EQ (zero_plus_x_init, x_init);
+
+  /* MULT_EXPR folding.  */
+  const svalue2 *x_init_times_zero
+    = mgr.get_or_create_binop (integer_type_node, MULT_EXPR,
+			       x_init, cst_sval[0]);
+  ASSERT_EQ (x_init_times_zero, cst_sval[0]);
+  const svalue2 *zero_times_x_init
+    = mgr.get_or_create_binop (integer_type_node, MULT_EXPR,
+			       cst_sval[0], x_init);
+  ASSERT_EQ (zero_times_x_init, cst_sval[0]);
+
+  const svalue2 *x_init_times_one
+    = mgr.get_or_create_binop (integer_type_node, MULT_EXPR,
+			       x_init, cst_sval[1]);
+  ASSERT_EQ (x_init_times_one, x_init);
+  const svalue2 *one_times_x_init
+    = mgr.get_or_create_binop (integer_type_node, MULT_EXPR,
+			       cst_sval[1], x_init);
+  ASSERT_EQ (one_times_x_init, x_init);
+
+  // etc
+  // TODO: do we want to use the match-and-simplify DSL for this?
+
+  /* Verify that binops put any constants on the RHS.  */
+  const svalue2 *four_times_x_init
+    = mgr.get_or_create_binop (integer_type_node, MULT_EXPR,
+			       cst_sval[4], x_init);
+  const svalue2 *x_init_times_four
+    = mgr.get_or_create_binop (integer_type_node, MULT_EXPR,
+			       x_init, cst_sval[4]);
+  ASSERT_EQ (four_times_x_init, x_init_times_four);
+  const binop_svalue2 *binop = four_times_x_init->dyn_cast_binop_svalue2 ();
+  ASSERT_EQ (binop->get_op (), MULT_EXPR);
+  ASSERT_EQ (binop->get_arg0 (), x_init);
+  ASSERT_EQ (binop->get_arg1 (), cst_sval[4]);
+
+  /* Verify that ((x + 1) + 1) == (x + 2).  */
+  const svalue2 *x_init_plus_one
+    = mgr.get_or_create_binop (integer_type_node, PLUS_EXPR,
+			       x_init, cst_sval[1]);
+  const svalue2 *x_init_plus_two
+    = mgr.get_or_create_binop (integer_type_node, PLUS_EXPR,
+			       x_init, cst_sval[2]);
+  const svalue2 *x_init_plus_one_plus_one
+    = mgr.get_or_create_binop (integer_type_node, PLUS_EXPR,
+			       x_init_plus_one, cst_sval[1]);
+  ASSERT_EQ (x_init_plus_one_plus_one, x_init_plus_two);
+}
+
+/* Verify that sub_svalue2s are folded as expected.  */
+
+static void
+test_sub_svalue2_folding ()
+{
+  coord_test2 ct;
+  tree c = build_global_decl ("c", ct.m_coord_type);
+  tree c_x = build3 (COMPONENT_REF, TREE_TYPE (ct.m_x_field),
+		     c, ct.m_x_field, NULL_TREE);
+
+  region_model2_manager mgr;
+  region_model2 model (&mgr);
+  test_region_model2_context ctxt;
+  const region2 *c_x_reg = model.get_lvalue (c_x, &ctxt);
+
+  /* Verify that sub_svalue2 of "unknown" simply
+     yields an unknown.  */
+
+  const svalue2 *unknown = mgr.get_or_create_unknown_svalue2 (ct.m_coord_type);
+  const svalue2 *sub = mgr.get_or_create_sub_svalue2 (TREE_TYPE (ct.m_x_field),
+						      unknown, c_x_reg);
+  ASSERT_EQ (sub->get_kind (), svalue2::SK_UNKNOWN);
+  ASSERT_EQ (sub->get_type (), TREE_TYPE (ct.m_x_field));
+}
+
+/* Test that region2::descendent_of_p works as expected.  */
+
+static void
+test_descendent_of_p ()
+{
+  region_model2_manager mgr;
+  const region2 *stack = mgr.get_stack_region ();
+  const region2 *heap = mgr.get_heap_region ();
+  const region2 *code = mgr.get_code_region ();
+  const region2 *globals = mgr.get_globals_region ();
+
+  /* descendent_of_p should return true when used on the region itself.  */
+  ASSERT_TRUE (stack->descendent_of_p (stack));
+  ASSERT_FALSE (stack->descendent_of_p (heap));
+  ASSERT_FALSE (stack->descendent_of_p (code));
+  ASSERT_FALSE (stack->descendent_of_p (globals));
+
+  tree x = build_global_decl ("x", integer_type_node);
+  const region2 *x_reg = mgr.get_region_for_global (x);
+  ASSERT_TRUE (x_reg->descendent_of_p (globals));
+
+  /* A cast_region2 should be a descendent of the original region.  */
+  const region2 *cast_reg = mgr.get_cast_region (x_reg, ptr_type_node);
+  ASSERT_TRUE (cast_reg->descendent_of_p (x_reg));
+}
+
+#if 0
+/* A subclass of purge_criteria for selftests: purge all const svalue2 *instances.  */
+
+class purge_all_svalue2_ids : public purge_criteria
+{
+public:
+  bool should_purge_p (svalue2_id) const FINAL OVERRIDE
+  {
+    return true;
+  }
+};
+
+/* A subclass of purge_criteria: purge a specific svalue2_id.  */
+
+class purge_one_svalue2 *: public purge_criteria
+{
+public:
+  purge_one_svalue2 *(svalue2 *victim) : m_victim (victim) {}
+
+  purge_one_svalue2 *(region_model2 model, tree expr)
+  : m_victim (model.get_rvalue (expr, NULL)) {}
+
+  bool should_purge_p (svalue2 *sval) const FINAL OVERRIDE
+  {
+    return sid == m_victim;
+  }
+
+private:
+  svalue2 *m_victim;
+};
+
+/* Check that constraint_manager::purge works for individual svalue2_ids.  */
+
+static void
+test_purging_by_criteria ()
+{
+  tree int_42 = build_int_cst (integer_type_node, 42);
+  tree int_0 = build_int_cst (integer_type_node, 0);
+
+  tree x = build_global_decl ("x", integer_type_node);
+  tree y = build_global_decl ("y", integer_type_node);
+
+  {
+    region_model2 model0;
+    region_model2 model1;
+
+    ADD_SAT_CONSTRAINT (model1, x, EQ_EXPR, y);
+    ASSERT_NE (model0, model1);
+
+    purge_stats stats_for_px;
+    purge_one_svalue2 *px (model1, x);
+    model1.get_constraints ()->purge (px, &stats_for_px);
+    ASSERT_EQ (stats_for_px.m_num_equiv_classes, 0);
+
+    purge_stats stats_for_py;
+    purge_one_svalue2 *py (model1.get_rvalue (y, NULL));
+    model1.get_constraints ()->purge (py, &stats_for_py);
+    ASSERT_EQ (stats_for_py.m_num_equiv_classes, 1);
+
+    ASSERT_EQ (*model0.get_constraints (), *model1.get_constraints ());
+  }
+
+  {
+    region_model2 model0;
+    region_model2 model1;
+
+    ADD_SAT_CONSTRAINT (model1, x, EQ_EXPR, int_42);
+    ASSERT_NE (model0, model1);
+    ASSERT_CONDITION_TRUE (model1, x, EQ_EXPR, int_42);
+
+    purge_stats stats;
+    model1.get_constraints ()->purge (purge_one_svalue2 *(model1, x), &stats);
+
+    ASSERT_CONDITION_UNKNOWN (model1, x, EQ_EXPR, int_42);
+  }
+
+  {
+    region_model2 model0;
+    region_model2 model1;
+
+    ADD_SAT_CONSTRAINT (model1, x, GE_EXPR, int_0);
+    ADD_SAT_CONSTRAINT (model1, x, LE_EXPR, int_42);
+    ASSERT_NE (model0, model1);
+
+    ASSERT_CONDITION_TRUE (model1, x, GE_EXPR, int_0);
+    ASSERT_CONDITION_TRUE (model1, x, LE_EXPR, int_42);
+
+    purge_stats stats;
+    model1.get_constraints ()->purge (purge_one_svalue2 *(model1, x), &stats);
+
+    ASSERT_CONDITION_UNKNOWN (model1, x, GE_EXPR, int_0);
+    ASSERT_CONDITION_UNKNOWN (model1, x, LE_EXPR, int_42);
+  }
+
+  {
+    region_model2 model0;
+    region_model2 model1;
+
+    ADD_SAT_CONSTRAINT (model1, x, NE_EXPR, int_42);
+    ADD_SAT_CONSTRAINT (model1, y, NE_EXPR, int_0);
+    ASSERT_NE (model0, model1);
+    ASSERT_CONDITION_TRUE (model1, x, NE_EXPR, int_42);
+    ASSERT_CONDITION_TRUE (model1, y, NE_EXPR, int_0);
+
+    purge_stats stats;
+    model1.get_constraints ()->purge (purge_one_svalue2 *(model1, x), &stats);
+    ASSERT_NE (model0, model1);
+
+    ASSERT_CONDITION_UNKNOWN (model1, x, NE_EXPR, int_42);
+    ASSERT_CONDITION_TRUE (model1, y, NE_EXPR, int_0);
+  }
+
+  {
+    region_model2 model0;
+    region_model2 model1;
+
+    ADD_SAT_CONSTRAINT (model1, x, NE_EXPR, int_42);
+    ADD_SAT_CONSTRAINT (model1, y, NE_EXPR, int_0);
+    ASSERT_NE (model0, model1);
+    ASSERT_CONDITION_TRUE (model1, x, NE_EXPR, int_42);
+    ASSERT_CONDITION_TRUE (model1, y, NE_EXPR, int_0);
+
+    purge_stats stats;
+    model1.get_constraints ()->purge (purge_all_svalue2_ids (), &stats);
+    ASSERT_CONDITION_UNKNOWN (model1, x, NE_EXPR, int_42);
+    ASSERT_CONDITION_UNKNOWN (model1, y, NE_EXPR, int_0);
+  }
+
+}
+
+/* Test that region_model2::purge_unused_svalue2s works as expected.  */
+
+static void
+test_purge_unused_svalue2s ()
+{
+  tree int_42 = build_int_cst (integer_type_node, 42);
+  tree int_0 = build_int_cst (integer_type_node, 0);
+  tree x = build_global_decl ("x", integer_type_node);
+  tree y = build_global_decl ("y", integer_type_node);
+
+  test_region_model2_context ctxt;
+  region_model2 model;
+  model.set_to_new_unknown_value (model.get_lvalue (x, &ctxt), TREE_TYPE (x),
+				  &ctxt);
+  model.set_to_new_unknown_value (model.get_lvalue (x, &ctxt), TREE_TYPE (x),
+				  &ctxt);
+  model.set_to_new_unknown_value (model.get_lvalue (x, &ctxt), TREE_TYPE (x),
+				  &ctxt);
+  model.add_constraint (x, NE_EXPR, int_42, &ctxt);
+
+  model.set_value (model.get_lvalue (x, &ctxt),
+		   model.get_rvalue (int_42, &ctxt),
+		   &ctxt);
+  model.add_constraint (y, GT_EXPR, int_0, &ctxt);
+
+  /* The redundant unknown values should have been purged.  */
+  purge_stats purged;
+  model.purge_unused_svalue2s (&purged, NULL);
+  ASSERT_EQ (purged.m_num_svalue2s, 3);
+
+  /* and the redundant constraint on an old, unknown value for x should
+     have been purged.  */
+  ASSERT_EQ (purged.m_num_equiv_classes, 1);
+  ASSERT_EQ (purged.m_num_constraints, 1);
+  ASSERT_EQ (model.get_constraints ()->m_constraints.length (), 2);
+
+  /* ...but we should still have x == 42.  */
+  ASSERT_EQ (model.eval_condition (x, EQ_EXPR, int_42, &ctxt),
+	     tristate::TS_TRUE);
+
+  /* ...and we should still have the constraint on y.  */
+  ASSERT_EQ (model.eval_condition (y, GT_EXPR, int_0, &ctxt),
+	     tristate::TS_TRUE);
+
+  ASSERT_EQ (ctxt.get_num_diagnostics (), 0);
+}
+#endif
+
+/* Verify that simple assignments work as expected.  */
+
+static void
+test_assignment ()
+{
+  tree int_0 = build_int_cst (integer_type_node, 0);
+  tree x = build_global_decl ("x", integer_type_node);
+  tree y = build_global_decl ("y", integer_type_node);
+
+  /* "x == 0", then use of y, then "y = 0;".  */
+  region_model2_manager mgr;
+  region_model2 model (&mgr);
+  ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, int_0);
+  ASSERT_CONDITION_UNKNOWN2 (model, y, EQ_EXPR, int_0);
+  model.set_value (model.get_lvalue (y, NULL),
+		   model.get_rvalue (int_0, NULL),
+		   NULL);
+  ASSERT_CONDITION_TRUE2 (model, y, EQ_EXPR, int_0);
+  ASSERT_CONDITION_TRUE2 (model, y, EQ_EXPR, x);
+
+#if 0
+  ASSERT_DUMP_EQ (model, true, "y: 0, {x}: unknown, x == y");
+#endif
+}
+
+/* Verify that compound assignments work as expected.  */
+
+static void
+test_compound_assignment ()
+{
+  coord_test2 ct;
+
+  tree c = build_global_decl ("c", ct.m_coord_type);
+  tree c_x = build3 (COMPONENT_REF, TREE_TYPE (ct.m_x_field),
+		     c, ct.m_x_field, NULL_TREE);
+  tree c_y = build3 (COMPONENT_REF, TREE_TYPE (ct.m_y_field),
+		     c, ct.m_y_field, NULL_TREE);
+  tree d = build_global_decl ("d", ct.m_coord_type);
+  tree d_x = build3 (COMPONENT_REF, TREE_TYPE (ct.m_x_field),
+		     d, ct.m_x_field, NULL_TREE);
+  tree d_y = build3 (COMPONENT_REF, TREE_TYPE (ct.m_y_field),
+		     d, ct.m_y_field, NULL_TREE);
+
+  tree int_17 = build_int_cst (integer_type_node, 17);
+  tree int_m3 = build_int_cst (integer_type_node, -3);
+
+  region_model2_manager mgr;
+  region_model2 model (&mgr);
+  model.set_value (c_x, int_17, NULL);
+  model.set_value (c_y, int_m3, NULL);
+
+#if 0
+  ASSERT_DUMP_EQ (model, true, "c.x: 17, c.y: -3");
+#endif
+
+  /* Copy c to d.  */
+  model.copy_region (model.get_lvalue (d, NULL), model.get_lvalue (c, NULL),
+		     NULL);
+  /* Check that the fields have the same svalue2s.  */
+  ASSERT_EQ (model.get_rvalue (c_x, NULL), model.get_rvalue (d_x, NULL));
+  ASSERT_EQ (model.get_rvalue (c_y, NULL), model.get_rvalue (d_y, NULL));
+}
+
+/* Verify the details of pushing and popping stack frames.  */
+
+static void
+test_stack_frames ()
+{
+  tree int_42 = build_int_cst (integer_type_node, 42);
+  tree int_10 = build_int_cst (integer_type_node, 10);
+  tree int_5 = build_int_cst (integer_type_node, 5);
+  tree int_0 = build_int_cst (integer_type_node, 0);
+
+  auto_vec <tree> param_types;
+  tree parent_fndecl = make_fndecl (integer_type_node,
+				    "parent_fn",
+				    param_types);
+  allocate_struct_function (parent_fndecl, true);
+
+  tree child_fndecl = make_fndecl (integer_type_node,
+				   "child_fn",
+				   param_types);
+  allocate_struct_function (child_fndecl, true);
+
+  /* "a" and "b" in the parent frame.  */
+  tree a = build_decl (UNKNOWN_LOCATION, PARM_DECL,
+		       get_identifier ("a"),
+		       integer_type_node);
+  tree b = build_decl (UNKNOWN_LOCATION, PARM_DECL,
+		       get_identifier ("b"),
+		       integer_type_node);
+  /* "x" and "y" in a child frame.  */
+  tree x = build_decl (UNKNOWN_LOCATION, PARM_DECL,
+		       get_identifier ("x"),
+		       integer_type_node);
+  tree y = build_decl (UNKNOWN_LOCATION, PARM_DECL,
+		       get_identifier ("y"),
+		       integer_type_node);
+
+  /* "p" global.  */
+  tree p = build_global_decl ("p", ptr_type_node);
+
+  /* "q" global.  */
+  tree q = build_global_decl ("q", ptr_type_node);
+
+  region_model2_manager mgr;
+  test_region_model2_context ctxt;
+  region_model2 model (&mgr);
+
+  /* Push stack frame for "parent_fn".  */
+  const region2 *parent_frame_reg
+    = model.push_frame (DECL_STRUCT_FUNCTION (parent_fndecl),
+			NULL, &ctxt);
+  ASSERT_EQ (model.get_current_frame (), parent_frame_reg);
+  ASSERT_TRUE (model.region_exists_p (parent_frame_reg));
+  const region2 *a_in_parent_reg = model.get_lvalue (a, &ctxt);
+  model.set_value (a_in_parent_reg,
+		   model.get_rvalue (int_42, &ctxt),
+		   &ctxt);
+  ASSERT_EQ (a_in_parent_reg->maybe_get_frame_region (), parent_frame_reg);
+
+#if 0
+  model.set_to_new_unknown_value (model.get_lvalue (b, &ctxt),
+				  integer_type_node, &ctxt);
+#endif
+  model.add_constraint (b, LT_EXPR, int_10, &ctxt);
+  ASSERT_EQ (model.eval_condition (b, LT_EXPR, int_10, &ctxt),
+	     tristate (tristate::TS_TRUE));
+
+  /* Push stack frame for "child_fn".  */
+  const region2 *child_frame_reg
+    = model.push_frame (DECL_STRUCT_FUNCTION (child_fndecl), NULL, &ctxt);
+  ASSERT_EQ (model.get_current_frame (), child_frame_reg);
+  ASSERT_TRUE (model.region_exists_p (child_frame_reg));
+  const region2 *x_in_child_reg = model.get_lvalue (x, &ctxt);
+  model.set_value (x_in_child_reg,
+		   model.get_rvalue (int_0, &ctxt),
+		   &ctxt);
+  ASSERT_EQ (x_in_child_reg->maybe_get_frame_region (), child_frame_reg);
+#if 0
+  model.set_to_new_unknown_value (model.get_lvalue (y, &ctxt),
+				  integer_type_node, &ctxt);
+#endif
+  model.add_constraint (y, NE_EXPR, int_5, &ctxt);
+  ASSERT_EQ (model.eval_condition (y, NE_EXPR, int_5, &ctxt),
+	     tristate (tristate::TS_TRUE));
+
+  /* Point a global pointer at a local in the child frame:  p = &x.  */
+  const region2 *p_in_globals_reg = model.get_lvalue (p, &ctxt);
+  model.set_value (p_in_globals_reg,
+		   mgr.get_ptr_svalue2 (ptr_type_node, x_in_child_reg),
+		   &ctxt);
+  ASSERT_EQ (p_in_globals_reg->maybe_get_frame_region (), NULL);
+
+  /* Point another global pointer at p: q = &p.  */
+  const region2 *q_in_globals_reg = model.get_lvalue (q, &ctxt);
+  model.set_value (q_in_globals_reg,
+		   mgr.get_ptr_svalue2 (ptr_type_node, p_in_globals_reg),
+		   &ctxt);
+
+  /* Test region2::descendent_of_p.  */
+  ASSERT_TRUE (child_frame_reg->descendent_of_p (child_frame_reg));
+  ASSERT_TRUE (x_in_child_reg->descendent_of_p (child_frame_reg));
+  ASSERT_FALSE (a_in_parent_reg->descendent_of_p (child_frame_reg));
+#if 0
+  auto_vec<region2_id> test_vec;
+  for (region2_id_set::iterator_t iter = descendents.begin ();
+       iter != descendents.end ();
+       ++iter)
+    test_vec.safe_push (*iter);
+  gcc_unreachable (); // TODO
+  //ASSERT_EQ ();
+#endif
+
+#if 0
+  ASSERT_DUMP_EQ (model, true,
+		  "a: 42, x: 0, p: &x, q: &p, {b, y}: unknown, b < 10, y != 5");
+#endif
+
+  /* Pop the "child_fn" frame from the stack.  */
+  purge_stats2 purged;
+  model.pop_frame (NULL, true, &purged, NULL, &ctxt);
+  ASSERT_FALSE (model.region_exists_p (child_frame_reg));
+  ASSERT_TRUE (model.region_exists_p (parent_frame_reg));
+
+#if 0
+  /* We should have purged the unknown values for x and y. */
+  ASSERT_EQ (purged.m_num_svalue2s, 2);
+
+  /* We should have purged the frame region2 and the region2s for x and y. */
+  ASSERT_EQ (purged.m_num_region2s, 3);
+
+  /* We should have purged the constraint on y.  */
+  ASSERT_EQ (purged.m_num_equiv_classes, 1);
+  ASSERT_EQ (purged.m_num_constraints, 1);
+#endif
+
+  /* Verify that p (which was pointing at the local "x" in the popped
+     frame) has been poisoned.  */
+  const svalue2 *new_p_sval = model.get_rvalue (p, &ctxt);
+  ASSERT_EQ (new_p_sval->get_kind (), svalue2::SK_POISONED);
+  ASSERT_EQ (new_p_sval->dyn_cast_poisoned_svalue2 ()->get_poison_kind (),
+	     POISON_KIND_POPPED_STACK);
+
+  /* Verify that q still points to p, in spite of the region2
+     renumbering.  */
+  const svalue2 *new_q_sval = model.get_rvalue (q, &ctxt);
+  ASSERT_EQ (new_q_sval->get_kind (), svalue2::SK_REGION);
+  ASSERT_EQ (new_q_sval->dyn_cast_region_svalue2 ()->get_pointee (),
+	     model.get_lvalue (p, &ctxt));
+
+  /* Verify that top of stack has been updated.  */
+  ASSERT_EQ (model.get_current_frame (), parent_frame_reg);
+
+  /* Verify locals in parent frame.  */
+  /* Verify "a" still has its value.  */
+  const svalue2 *new_a_sval = model.get_rvalue (a, &ctxt);
+  ASSERT_EQ (new_a_sval->get_kind (), svalue2::SK_CONSTANT);
+  ASSERT_EQ (new_a_sval->dyn_cast_constant_svalue2 ()->get_constant (),
+	     int_42);
+  /* Verify "b" still has its constraint.  */
+  ASSERT_EQ (model.eval_condition (b, LT_EXPR, int_10, &ctxt),
+	     tristate (tristate::TS_TRUE));
+}
+
+/* Verify that get_representative_path_var works as expected, that
+   we can map from region2s to parms and back within a recursive call
+   stack.  */
+
+static void
+test_get_representative_path_var ()
+{
+  auto_vec <tree> param_types;
+  tree fndecl = make_fndecl (integer_type_node,
+			     "factorial",
+			     param_types);
+  allocate_struct_function (fndecl, true);
+
+  /* Parm "n".  */
+  tree n = build_decl (UNKNOWN_LOCATION, PARM_DECL,
+		       get_identifier ("n"),
+		       integer_type_node);
+
+  region_model2_manager mgr;
+  test_region_model2_context ctxt;
+  region_model2 model (&mgr);
+
+  /* Push 5 stack frames for "factorial", each with a param  */
+  auto_vec<const region2 *> parm_regs;
+  auto_vec<const svalue2 *> parm_svals;
+  for (int depth = 0; depth < 5; depth++)
+    {
+      const region2 *frame_n_reg
+	= model.push_frame (DECL_STRUCT_FUNCTION (fndecl), NULL, &ctxt);
+      const region2 *parm_n_reg = model.get_lvalue (path_var (n, depth), &ctxt);
+      parm_regs.safe_push (parm_n_reg);
+
+      ASSERT_EQ (parm_n_reg->get_parent_region (), frame_n_reg);
+      const svalue2 *sval_n = mgr.get_or_create_initial_value (parm_n_reg);
+      parm_svals.safe_push (sval_n);
+    }
+
+  /* Verify that we can recognize that the region2s are the parms,
+     at every depth.  */
+  for (int depth = 0; depth < 5; depth++)
+    {
+      {
+	hash_set <const svalue2 *> visited;
+	ASSERT_EQ (model.get_representative_path_var (parm_regs[depth],
+						      &visited),
+		   path_var (n, depth + 1));
+      }
+      /* ...and that we can lookup lvalues for locals for all frames,
+	 not just the top.  */
+      ASSERT_EQ (model.get_lvalue (path_var (n, depth), NULL),
+		 parm_regs[depth]);
+      /* ...and that we can locate the svalue2s.  */
+      {
+	hash_set <const svalue2 *> visited;
+	ASSERT_EQ (model.get_representative_path_var (parm_svals[depth],
+						      &visited),
+		   path_var (n, depth + 1));
+      }
+    }
+}
+
+/* Verify that "empty" region_model2 instances are equal to each other.  */
+
+static void
+test_model_equality_1 ()
+{
+  tree int_42 = build_int_cst (integer_type_node, 42);
+  tree int_17 = build_int_cst (integer_type_node, 17);
+
+  region_model2_manager mgr;
+  region_model2 model0 (&mgr);
+  region_model2 model1 (&mgr);
+  ASSERT_EQ (model0, model1);
+
+  /* Verify that setting state in model1 makes the models non-equal.  */
+  tree x = build_global_decl ("x", integer_type_node);
+  model0.set_value (x, int_42, NULL);
+  ASSERT_EQ (model0.get_rvalue (x, NULL)->maybe_get_constant (), int_42);
+  ASSERT_NE (model0, model1);
+
+  /* Verify the copy-ctor.  */
+  region_model2 model2 (model0);
+  ASSERT_EQ (model0, model2);
+  ASSERT_EQ (model2.get_rvalue (x, NULL)->maybe_get_constant (), int_42);
+  ASSERT_NE (model1, model2);
+
+  /* Verify that models obtained from copy-ctor are independently editable
+     w/o affecting the original model.  */
+  model2.set_value (x, int_17, NULL);
+  ASSERT_NE (model0, model2);
+  ASSERT_EQ (model2.get_rvalue (x, NULL)->maybe_get_constant (), int_17);
+  ASSERT_EQ (model0.get_rvalue (x, NULL)->maybe_get_constant (), int_42);
+}
+
+/* Verify that region2 models for
+      x = 42; y = 113;
+   and
+      y = 113; x = 42;
+   are equal.  */
+
+static void
+test_model_equality_2 ()
+{
+  tree int_42 = build_int_cst (integer_type_node, 42);
+  tree int_113 = build_int_cst (integer_type_node, 113);
+  tree x = build_global_decl ("x", integer_type_node);
+  tree y = build_global_decl ("y", integer_type_node);
+
+  region_model2_manager mgr;
+  region_model2 model0 (&mgr);
+  model0.set_value (model0.get_lvalue (x, NULL),
+		    model0.get_rvalue (int_42, NULL),
+		    NULL);
+  model0.set_value (model0.get_lvalue (y, NULL),
+		    model0.get_rvalue (int_113, NULL),
+		    NULL);
+
+  region_model2 model1 (&mgr);
+  model1.set_value (model1.get_lvalue (y, NULL),
+		    model1.get_rvalue (int_113, NULL),
+		    NULL);
+  model1.set_value (model1.get_lvalue (x, NULL),
+		    model1.get_rvalue (int_42, NULL),
+		    NULL);
+
+  ASSERT_EQ (model0, model1);
+}
+
+/* Verify that constraints for
+     x > 3 && y > 42
+   and
+     y > 42 && x > 3
+   are equal (after canonicalization).  */
+
+static void
+test_model_equality_3 ()
+{
+  tree int_3 = build_int_cst (integer_type_node, 3);
+  tree int_42 = build_int_cst (integer_type_node, 42);
+  tree x = build_global_decl ("x", integer_type_node);
+  tree y = build_global_decl ("y", integer_type_node);
+
+  region_model2_manager mgr;
+  region_model2 model0 (&mgr);
+  model0.add_constraint (x, GT_EXPR, int_3, NULL);
+  model0.add_constraint (y, GT_EXPR, int_42, NULL);
+
+  region_model2 model1 (&mgr);
+  model1.add_constraint (y, GT_EXPR, int_42, NULL);
+  model1.add_constraint (x, GT_EXPR, int_3, NULL);
+
+  model0.canonicalize ();
+  model1.canonicalize ();
+  ASSERT_EQ (model0, model1);
+}
+
+#if 0
+/* Verify that we can canonicalize a model containing NaN and other real
+   constants.  */
+
+static void
+test_model_equality_4 ()
+{
+  auto_vec<tree> csts;
+  append_interesting_constants (&csts);
+
+  region_model2 model;
+
+  unsigned i;
+  tree cst;
+  FOR_EACH_VEC_ELT (csts, i, cst)
+    model.get_rvalue (cst, NULL);
+
+  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
+   mergable.  Write the merged model to *OUT_MERGED_MODEL,
+   and the merged svalue2 ptr to *OUT_MERGED_SVALUE2.
+   If VAL_A or VAL_B are NULL_TREE, don't populate EXPR
+   for that region_model2.  */
+
+static void
+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);
+  region_model2 model1 (mgr);
+  if (val_a)
+    model0.set_value (model0.get_lvalue (expr, &ctxt),
+		      model0.get_rvalue (val_a, &ctxt),
+		      &ctxt);
+  if (val_b)
+    model1.set_value (model1.get_lvalue (expr, &ctxt),
+		      model1.get_rvalue (val_b, &ctxt),
+		      &ctxt);
+
+  /* They should be mergeable.  */
+  ASSERT_TRUE (model0.can_merge_with_p (model1, point, out_merged_model));
+  *out_merged_svalue2 = out_merged_model->get_rvalue (expr, &ctxt);
+}
+
+/* Verify that we can merge region_model2 instances.  */
+
+static void
+test_state_merging ()
+{
+  tree int_42 = build_int_cst (integer_type_node, 42);
+  tree int_113 = build_int_cst (integer_type_node, 113);
+  tree x = build_global_decl ("x", integer_type_node);
+  tree y = build_global_decl ("y", integer_type_node);
+  tree z = build_global_decl ("z", integer_type_node);
+  tree p = build_global_decl ("p", ptr_type_node);
+
+  tree addr_of_y = build1 (ADDR_EXPR, ptr_type_node, y);
+  tree addr_of_z = build1 (ADDR_EXPR, ptr_type_node, z);
+
+  auto_vec <tree> param_types;
+  tree test_fndecl = make_fndecl (integer_type_node, "test_fn", param_types);
+  allocate_struct_function (test_fndecl, true);
+
+  /* Param "a".  */
+  tree a = build_decl (UNKNOWN_LOCATION, PARM_DECL,
+		       get_identifier ("a"),
+		       integer_type_node);
+  tree addr_of_a = build1 (ADDR_EXPR, ptr_type_node, a);
+
+  /* Param "q", a pointer.  */
+  tree q = build_decl (UNKNOWN_LOCATION, PARM_DECL,
+		       get_identifier ("q"),
+		       ptr_type_node);
+
+  program_point point (program_point::origin ());
+  region_model2_manager mgr;
+
+  {
+    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, point, &merged));
+    ASSERT_EQ (model0, merged);
+  }
+
+  /* Verify that we can merge two contradictory constraints on the
+     value for a global.  */
+  /* TODO: verify that the merged model doesn't have a value for
+     the global  */
+  {
+    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);
+    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &merged));
+    ASSERT_NE (model0, merged);
+    ASSERT_NE (model1, merged);
+  }
+
+  /* Verify handling of a PARM_DECL.  */
+  {
+    test_region_model2_context ctxt;
+    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);
+
+    placeholder_svalue2 test_sval (integer_type_node, "test sval");
+    model0.set_value (model0.get_lvalue (a, &ctxt), &test_sval, &ctxt);
+    model1.set_value (model1.get_lvalue (a, &ctxt), &test_sval, &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 (&mgr);
+    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);
+  }
+
+  // FIXME: uses "set_to_new_unknown_value"
+  /* Verify handling of a global.  */
+  {
+    test_region_model2_context ctxt;
+    region_model2 model0 (&mgr);
+    region_model2 model1 (&mgr);
+
+    placeholder_svalue2 test_sval (integer_type_node, "test sval");
+    model0.set_value (model0.get_lvalue (x, &ctxt), &test_sval, &ctxt);
+    model1.set_value (model1.get_lvalue (x, &ctxt), &test_sval, &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 (&mgr);
+    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);
+  }
+
+  /* Use global-handling to verify various combinations of values.  */
+
+  /* Two equal constant values.  */
+  {
+    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 (), svalue2::SK_CONSTANT);
+    ASSERT_EQ (merged_x_sval->dyn_cast_constant_svalue2 ()->get_constant (),
+	       int_42);
+  }
+
+  /* Two non-equal constant values.  */
+  {
+    region_model2 merged (&mgr);
+    const svalue2 *merged_x_sval;
+    assert_region_model2s_merge (x, int_42, int_113, &merged, &merged_x_sval);
+
+    /* In particular, there should be an unknown value for "x".  */
+    // FIXME
+#if WIDENING
+    ASSERT_EQ (merged_x_sval->get_kind (), svalue2::SK_WIDENING);
+#else
+    ASSERT_EQ (merged_x_sval->get_kind (), svalue2::SK_UNKNOWN);
+#endif
+  }
+
+  /* Uninit and constant.  */
+  {
+    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 (), svalue2::SK_UNKNOWN);
+#endif
+  }
+
+  /* Constant and uninit.  */
+  {
+    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 (), svalue2::SK_UNKNOWN);
+#endif
+  }
+
+  /* Unknown and constant.  */
+  // TODO
+
+  /* Pointers: NULL and NULL.  */
+  // TODO
+
+  /* Pointers: NULL and non-NULL.  */
+  // TODO
+
+  /* Pointers: non-NULL and non-NULL: ptr to a local.  */
+  {
+    region_model2 model0 (&mgr);
+    model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL);
+    model0.set_value (model0.get_lvalue (p, NULL),
+		      model0.get_rvalue (addr_of_a, NULL), NULL);
+
+    region_model2 model1 (model0);
+    ASSERT_EQ (model0, model1);
+
+    /* They should be mergeable, and the result should be the same.  */
+    region_model2 merged (&mgr);
+    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &merged));
+    ASSERT_EQ (model0, merged);
+  }
+
+  /* Pointers: non-NULL and non-NULL: ptr to a global.  */
+  {
+    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 (), 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 (&mgr);
+    /* x == &y vs x == &z in the input models; these are actually casts
+       of the ptrs to "int".  */
+    const svalue2 *merged_x_sval;
+    // TODO:
+    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 (), svalue2::SK_UNKNOWN);
+  }
+
+  /* Pointers: non-NULL and non-NULL: ptr to a heap region2.  */
+  {
+    test_region_model2_context ctxt;
+    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);
+
+    region_model2 model1 (model0);
+
+    ASSERT_EQ (model0, model1);
+
+    region_model2 merged (&mgr);
+    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &merged));
+
+    /* 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 (&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), sval, &ctxt);
+    region_model2 model1 (model0);
+
+    /* They should be mergeable, and the result should be the same.  */
+    region_model2 merged (&mgr);
+    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &merged));
+    ASSERT_EQ (model0, merged);
+
+    /* In particular, we should have x == y.  */
+    ASSERT_EQ (merged.eval_condition (x, EQ_EXPR, y, &ctxt),
+	       tristate (tristate::TS_TRUE));
+  }
+#endif
+
+  {
+    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);
+    region_model2 merged (&mgr);
+    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &merged));
+  }
+
+  {
+    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);
+    model1.add_constraint (x, EQ_EXPR, int_113, &ctxt);
+    region_model2 merged (&mgr);
+    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &merged));
+  }
+
+  // TODO: what can't we merge? need at least one such test
+
+  /* TODO: various things
+     - heap region2s
+     - value merging:
+       - every combination, but in particular
+          - pairs of region2s
+   */
+
+  /* Views.  */
+  {
+#if 0
+    test_region_model2_context ctxt;
+    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,
+							 &ctxt);
+    model0.set_value (x_as_ptr, model0.get_rvalue (addr_of_y, &ctxt), &ctxt);
+
+    region_model2 model1 (model0);
+    ASSERT_EQ (model1, model0);
+
+    /* They should be mergeable, and the result should be the same.  */
+    region_model2 merged (&mgr);
+    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &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.  */
+  {
+#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);
+
+    /* Push a second frame.  */
+    const region2 *reg_2nd_frame
+      = model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL);
+
+    /* Have a pointer in the older frame point to a local in the
+       more recent frame.  */
+    const svalue2 *sval_ptr = model0.get_rvalue (addr_of_a, NULL);
+    model0.set_value (q_in_first_frame, sid_ptr, NULL);
+
+    /* Verify that it's pointing at the newer frame.  */
+    const region2 *reg_pointee
+      = model0.get_svalue2 (sid_ptr)->dyn_cast_region_svalue2 ()->get_pointee ();
+    ASSERT_EQ (model0.get_region2 (rid_pointee)->get_parent (), rid_2nd_frame);
+
+    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 (&mgr);
+    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &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 (&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);
+
+    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 (&mgr);
+    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &merged));
+    ASSERT_EQ (model0, merged);
+  }
+}
+
+/* Verify that constraints are correctly merged when merging region_model2
+   instances.  */
+
+static void
+test_constraint_merging ()
+{
+  tree int_0 = build_int_cst (integer_type_node, 0);
+  tree int_5 = build_int_cst (integer_type_node, 5);
+  tree x = build_global_decl ("x", integer_type_node);
+  tree y = build_global_decl ("y", integer_type_node);
+  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 (&mgr);
+  model0.add_constraint (x, EQ_EXPR, y, &ctxt);
+  model0.add_constraint (x, GE_EXPR, int_0, NULL);
+  model0.add_constraint (x, LT_EXPR, n, NULL);
+
+  /* model1: z != 5 && (0 <= x < n).  */
+  region_model2 model1 (&mgr);
+  model1.add_constraint (z, NE_EXPR, int_5, NULL);
+  model1.add_constraint (x, GE_EXPR, int_0, NULL);
+  model1.add_constraint (x, LT_EXPR, n, NULL);
+
+  /* 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, point, &merged));
+
+  ASSERT_EQ (merged.eval_condition (x, GE_EXPR, int_0, &ctxt),
+	     tristate (tristate::TS_TRUE));
+  ASSERT_EQ (merged.eval_condition (x, LT_EXPR, n, &ctxt),
+	     tristate (tristate::TS_TRUE));
+
+  ASSERT_EQ (merged.eval_condition (z, NE_EXPR, int_5, &ctxt),
+	     tristate (tristate::TS_UNKNOWN));
+  ASSERT_EQ (merged.eval_condition (x, LT_EXPR, y, &ctxt),
+	     tristate (tristate::TS_UNKNOWN));
+}
+
+/* FIXME.  */
+
+static void
+test_widening_constraints ()
+{
+  program_point point (program_point::origin ());
+  tree int_0 = build_int_cst (integer_type_node, 0);
+  tree int_m1 = build_int_cst (integer_type_node, -1);
+  tree int_1 = build_int_cst (integer_type_node, 1);
+  tree int_256 = build_int_cst (integer_type_node, 256);
+  region_model2_manager mgr;
+  test_region_model2_context ctxt;
+  const svalue2 *int_0_sval = mgr.get_or_create_constant_svalue2 (int_0);
+  const svalue2 *int_1_sval = mgr.get_or_create_constant_svalue2 (int_1);
+  const svalue2 *w_zero_then_one_sval
+    = mgr.get_or_create_widening_svalue2 (integer_type_node, point,
+					  int_0_sval, int_1_sval);
+  const widening_svalue2 *w_zero_then_one
+    = w_zero_then_one_sval->dyn_cast_widening_svalue2 ();
+  ASSERT_EQ (w_zero_then_one->get_direction (),
+	     widening_svalue2::DIR_ASCENDING);
+  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (LT_EXPR, int_m1),
+	     tristate::TS_FALSE);
+  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (LT_EXPR, int_0),
+	     tristate::TS_FALSE);
+  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (LT_EXPR, int_1),
+	     tristate::TS_UNKNOWN);
+  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (LT_EXPR, int_256),
+	     tristate::TS_UNKNOWN);
+
+  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (LE_EXPR, int_m1),
+	     tristate::TS_FALSE);
+  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (LE_EXPR, int_0),
+	     tristate::TS_UNKNOWN);
+  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (LE_EXPR, int_1),
+	     tristate::TS_UNKNOWN);
+  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (LE_EXPR, int_256),
+	     tristate::TS_UNKNOWN);
+
+  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (GT_EXPR, int_m1),
+	     tristate::TS_TRUE);
+  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (GT_EXPR, int_0),
+	     tristate::TS_UNKNOWN);
+  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (GT_EXPR, int_1),
+	     tristate::TS_UNKNOWN);
+  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (GT_EXPR, int_256),
+	     tristate::TS_UNKNOWN);
+
+  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (GE_EXPR, int_m1),
+	     tristate::TS_TRUE);
+  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (GE_EXPR, int_0),
+	     tristate::TS_TRUE);
+  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (GE_EXPR, int_1),
+	     tristate::TS_UNKNOWN);
+  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (GE_EXPR, int_256),
+	     tristate::TS_UNKNOWN);
+
+  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (EQ_EXPR, int_m1),
+	     tristate::TS_FALSE);
+  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (EQ_EXPR, int_0),
+	     tristate::TS_UNKNOWN);
+  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (EQ_EXPR, int_1),
+	     tristate::TS_UNKNOWN);
+  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (EQ_EXPR, int_256),
+	     tristate::TS_UNKNOWN);
+
+  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (NE_EXPR, int_m1),
+	     tristate::TS_TRUE);
+  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (NE_EXPR, int_0),
+	     tristate::TS_UNKNOWN);
+  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (NE_EXPR, int_1),
+	     tristate::TS_UNKNOWN);
+  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (NE_EXPR, int_256),
+	     tristate::TS_UNKNOWN);
+
+  // TODO: a descending value
+}
+
+/* Verify merging constraints for states simulating successive iterations
+   of a loop.
+   Simulate:
+     for (i = 0; i < 256; i++)
+       [...body...]
+   i.e. this gimple:.
+     i_15 = 0;
+     goto <bb 4>;
+
+   <bb 4> :
+     i_11 = PHI <i_15(2), i_23(3)>
+     if (i_11 <= 255)
+       goto <bb 3>;
+     else
+       goto [AFTER LOOP]
+
+   <bb 3> :
+     [LOOP BODY]
+     i_23 = i_11 + 1;
+
+   and thus these ops (and resultant states):
+     i_11 = PHI()
+       {i_11: 0}
+     add_constraint (i_11 <= 255) [for the true edge]
+       {i_11: 0}  [constraint was a no-op]
+     i_23 = i_11 + 1;
+       {i_22: 1}
+     i_11 = PHI()
+       {i_11: WIDENED (at phi, 0, 1)}
+     add_constraint (i_11 <= 255) [for the true edge]
+       {i_11: WIDENED (at phi, 0, 1); WIDENED <= 255}
+     i_23 = i_11 + 1;
+       {i_23: (WIDENED (at phi, 0, 1) + 1); WIDENED <= 255}
+     i_11 = PHI(); merge with state at phi above
+       {i_11: WIDENED (at phi, 0, 1); WIDENED <= 256}
+         [changing meaning of "WIDENED" here]
+     if (i_11 <= 255)
+        T: {i_11: WIDENED (at phi, 0, 1); WIDENED <= 255}; cache hit
+        F: {i_11: 256}
+
+	FIXME.
+	TODO: rename?
+ */
+
+static void
+test_iteration_1 ()
+{
+#if WIDENING
+  program_point point (program_point::origin ());
+
+  tree int_0 = build_int_cst (integer_type_node, 0);
+  tree int_1 = build_int_cst (integer_type_node, 1);
+  tree int_256 = build_int_cst (integer_type_node, 256);
+  tree int_257 = build_int_cst (integer_type_node, 257);
+  tree i = build_global_decl ("i", integer_type_node);
+
+  region_model2_manager mgr;
+  test_region_model2_context ctxt;
+
+  /* model0: i: 0.  */
+  region_model2 model0 (&mgr);
+  model0.set_value (i, int_0, &ctxt);
+
+  /* model1: i: 1.  */
+  region_model2 model1 (&mgr);
+  model1.set_value (i, int_1, &ctxt);
+
+  /* FIXME: should merge to a widened value.  */
+  region_model2 model2 (&mgr);
+  ASSERT_TRUE (model1.can_merge_with_p (model0, point, &model2));
+  const svalue2 *merged_i = model2.get_rvalue (i, &ctxt);
+  ASSERT_EQ (merged_i->get_kind (), svalue2::SK_WIDENING);
+  const widening_svalue2 *w = merged_i->dyn_cast_widening_svalue2 ();
+  ASSERT_EQ (w->get_direction (), widening_svalue2::DIR_ASCENDING);
+
+  /* Add constraint: i < 256  */
+  model2.add_constraint (i, LT_EXPR, int_256, &ctxt);
+  ASSERT_EQ (model2.eval_condition (i, LT_EXPR, int_256, &ctxt),
+	     tristate (tristate::TS_TRUE));
+  ASSERT_EQ (model2.eval_condition (i, GE_EXPR, int_0, &ctxt),
+	     tristate (tristate::TS_TRUE));
+
+  /* Try merging with the initial state.  */
+  region_model2 model3 (&mgr);
+  ASSERT_TRUE (model2.can_merge_with_p (model0, point, &model3));
+  /* Merging the merged value with the initial value should be idempotent,
+     so that the analysis converges.  */
+  ASSERT_EQ (model3.get_rvalue (i, &ctxt), merged_i);
+  /* Merger of 0 and a widening value with constraint < CST
+     should retain the constraint, even though it was implicit
+     for the 0 case.  */
+  ASSERT_EQ (model3.eval_condition (i, LT_EXPR, int_256, &ctxt),
+	     tristate (tristate::TS_TRUE));
+  /* ...and we should have equality: the analysis should have converged.  */
+  ASSERT_EQ (model3, model2);
+
+  /* "i_23 = i_11 + 1;"  */
+  region_model2 model4 (model3);
+  ASSERT_EQ (model4, model2);
+  model4.set_value (i, build2 (PLUS_EXPR, integer_type_node, i, int_1), &ctxt);
+  const svalue2 *plus_one = model4.get_rvalue (i, &ctxt);
+  /* TODO: what should this be?
+     Currently it's the widening(0, +ve) plus one.
+     Should it be a new widening(1, +ve), and apply a reworked version
+     of the constraints on the other widening value?  */
+  ASSERT_EQ (plus_one->get_kind (), svalue2::SK_BINOP);
+
+  /* Try merging with the "i: 1" state.  */
+  region_model2 model5 (&mgr);
+  ASSERT_TRUE (model4.can_merge_with_p (model1, point, &model5));
+  ASSERT_EQ (model5.get_rvalue (i, &ctxt), plus_one);
+  ASSERT_EQ (model5, model4);
+
+  /*  "i_11 = PHI();" merge with state at phi above.
+      For i, we should have a merger of WIDENING with WIDENING + 1,
+      and this should be WIDENING again.  */
+  region_model2 model6 (&mgr);
+  ASSERT_TRUE (model5.can_merge_with_p (model2, point, &model6));
+  const svalue2 *merged_widening = model6.get_rvalue (i, &ctxt);
+  ASSERT_EQ (merged_widening->get_kind (), svalue2::SK_WIDENING);
+  // FIXME: what constraints should we have here?
+
+  ASSERT_CONDITION_TRUE2 (model6, i, LT_EXPR, int_257);
+
+  /* This is a cache miss (we have i < 257 rather than i < 256).
+     However, on applying the loop-bound condition...
+     FIXME.  */
+
+  region_model2 model7_t (model6);
+  ADD_SAT_CONSTRAINT (model7_t, i, LT_EXPR, int_256);
+
+  region_model2 model7_f (model6);
+#if ITERATED_BINOPS
+  ADD_SAT_CONSTRAINT (model7_f, i, GE_EXPR, int_256);
+  ASSERT_CONDITION_TRUE2 (model7_f, i, EQ_EXPR, int_256);
+#endif /* ITERATED_BINOPS */
+
+  //ASSERT_EQ (model6, model3);
+
+  // TODO: constraints at end of loop (with i >= 256; should get i == 256)
+  /* TODO: unittests for widening_svalue2::eval_condition_without_cm
+     (separately).  */
+#endif
+}
+
+// TODO: as above, but with comparison against "n", rather than a constant
+
+static void
+test_iteration_2 ()
+{
+  // TODO
+}
+
+#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.  */
+
+static void
+test_malloc_constraints ()
+{
+  region_model2 model;
+  tree p = build_global_decl ("p", ptr_type_node);
+  tree char_star = build_pointer_type (char_type_node);
+  tree q = build_global_decl ("q", char_star);
+  tree null_ptr = build_int_cst (ptr_type_node, 0);
+
+  const region2 *reg = model.add_new_malloc_region2 ();
+  const svalue2 *sval = mgr.get_ptr_svalue2 (ptr_type_node, rid);
+  model.set_value (model.get_lvalue (p, NULL), sid, NULL);
+  model.set_value (q, p, NULL);
+
+  /* We should have a symbolic_region2 with m_possibly_null: true.  */
+  const region2 *pointee = model.get_region2 (rid);
+  const symbolic_region2 *sym_reg = pointee->dyn_cast_symbolic_region2 ();
+  ASSERT_NE (sym_reg, NULL);
+  ASSERT_TRUE (sym_reg->m_possibly_null);
+
+  ASSERT_CONDITION_UNKNOWN (model, p, NE_EXPR, null_ptr);
+  ASSERT_CONDITION_UNKNOWN (model, p, EQ_EXPR, null_ptr);
+  ASSERT_CONDITION_UNKNOWN (model, q, NE_EXPR, null_ptr);
+  ASSERT_CONDITION_UNKNOWN (model, q, EQ_EXPR, null_ptr);
+
+  model.add_constraint (p, NE_EXPR, null_ptr, NULL);
+
+  /* Adding the constraint should have cleared m_possibly_null.  */
+  ASSERT_FALSE (sym_reg->m_possibly_null);
+
+  ASSERT_CONDITION_TRUE (model, p, NE_EXPR, null_ptr);
+  ASSERT_CONDITION_FALSE (model, p, EQ_EXPR, null_ptr);
+  ASSERT_CONDITION_TRUE (model, q, NE_EXPR, null_ptr);
+  ASSERT_CONDITION_FALSE (model, q, EQ_EXPR, null_ptr);
+}
+#endif
+
+static void
+test_var ()
+{
+  /* "int i;"  */
+  tree i = build_global_decl ("i", integer_type_node);
+
+  tree int_17 = build_int_cst (integer_type_node, 17);
+  tree int_m3 = build_int_cst (integer_type_node, -3);
+
+  region_model2_manager mgr;
+  region_model2 model (&mgr);
+
+  const region2 *i_reg = model.get_lvalue (i, NULL);
+  ASSERT_EQ (i_reg->get_kind (), region2::RK_DECL);
+
+  /* Reading "i" should give a symbolic "initial value".  */
+  const svalue2 *sval_init = model.get_rvalue (i, NULL);
+  ASSERT_EQ (sval_init->get_kind (), svalue2::SK_INITIAL);
+  ASSERT_EQ (sval_init->dyn_cast_initial_svalue2 ()->get_region (), i_reg);
+  /* ..and doing it again should give the same "initial value".  */
+  ASSERT_EQ (model.get_rvalue (i, NULL), sval_init);
+
+  /* "i = 17;".  */
+  model.set_value (i, int_17, NULL);
+  ASSERT_EQ (model.get_rvalue (i, NULL),
+	     model.get_rvalue (int_17, NULL));
+
+  /* "i = -3;".  */
+  model.set_value (i, int_m3, NULL);
+  ASSERT_EQ (model.get_rvalue (i, NULL),
+	     model.get_rvalue (int_m3, NULL));
+
+  /* Verify get_offset for "i".  */
+  {
+    region_offset offset = i_reg->get_offset ();
+    ASSERT_EQ (offset.get_base_region (), i_reg);
+    ASSERT_EQ (offset.get_bit_offset (), 0);
+  }
+
+  /* Simplified dump.  */
+#if 0
+  ASSERT_DUMP_EQ (model, true,
+		  "base region: {c} has cluster: {"
+		  "binding key: {region: c.x, kind: direct, offset: 0},"
+		  " value: {17},"
+		  " binding key: {region: c.y, kind: direct, offset: 0},"
+		  " value: {-3}"
+		  "}");
+#endif
+
+}
+
+static void
+test_array ()
+{
+  /* "int arr[10];"  */
+  tree tlen = size_int (10);
+  tree arr_type
+    = build_array_type (integer_type_node, build_index_type (tlen));
+  tree arr = build_global_decl ("arr", arr_type);
+
+  /* "int i;"  */
+  tree i = build_global_decl ("i", integer_type_node);
+
+  tree int_0 = build_int_cst (integer_type_node, 0);
+  tree int_1 = build_int_cst (integer_type_node, 1);
+
+  tree arr_0 = build4 (ARRAY_REF, integer_type_node,
+		       arr, int_0, NULL_TREE, NULL_TREE);
+  tree arr_1 = build4 (ARRAY_REF, integer_type_node,
+		       arr, int_1, NULL_TREE, NULL_TREE);
+  tree arr_i = build4 (ARRAY_REF, integer_type_node,
+		       arr, i, NULL_TREE, NULL_TREE);
+
+  tree int_17 = build_int_cst (integer_type_node, 17);
+  tree int_42 = build_int_cst (integer_type_node, 42);
+  tree int_m3 = build_int_cst (integer_type_node, -3);
+
+  region_model2_manager mgr;
+  region_model2 model (&mgr);
+  /* "arr[0] = 17;".  */
+  model.set_value (arr_0, int_17, NULL);
+  /* "arr[1] = -3;".  */
+  model.set_value (arr_1, int_m3, NULL);
+
+  ASSERT_EQ (model.get_rvalue (arr_0, NULL), model.get_rvalue (int_17, NULL));
+  ASSERT_EQ (model.get_rvalue (arr_1, NULL), model.get_rvalue (int_m3, NULL));
+
+  /* Overwrite a pre-existing binding: "arr[1] = 42;".  */
+  model.set_value (arr_1, int_42, NULL);
+  ASSERT_EQ (model.get_rvalue (arr_1, NULL), model.get_rvalue (int_42, NULL));
+
+  /* Verify get_offset for "arr[0]".  */
+  {
+    const region2 *arr_0_reg = model.get_lvalue (arr_0, NULL);
+    region_offset offset = arr_0_reg->get_offset ();
+    ASSERT_EQ (offset.get_base_region (), model.get_lvalue (arr, NULL));
+    ASSERT_EQ (offset.get_bit_offset (), 0);
+  }
+
+  /* Verify get_offset for "arr[1]".  */
+  {
+    const region2 *arr_1_reg = model.get_lvalue (arr_1, NULL);
+    region_offset offset = arr_1_reg->get_offset ();
+    ASSERT_EQ (offset.get_base_region (), model.get_lvalue (arr, NULL));
+    ASSERT_EQ (offset.get_bit_offset (), INT_TYPE_SIZE);
+  }
+
+  /* "arr[i] = i;" - this should remove the earlier bindings.  */
+  model.set_value (arr_i, i, NULL);
+  ASSERT_EQ (model.get_rvalue (arr_i, NULL), model.get_rvalue (i, NULL));
+  ASSERT_EQ (model.get_rvalue (arr_0, NULL)->get_kind (), svalue2::SK_UNKNOWN);
+
+  /* "arr[0] = 17;" - this should remove the arr[i] binding.  */
+  model.set_value (arr_0, int_17, NULL);
+  ASSERT_EQ (model.get_rvalue (arr_0, NULL), model.get_rvalue (int_17, NULL));
+  ASSERT_EQ (model.get_rvalue (arr_i, NULL)->get_kind (), svalue2::SK_UNKNOWN);
+}
+
+/* FIXME.  */
+
+static void
+test_mem_ref ()
+{
+  /*
+    x = 17;
+    p = &x;
+    *p;
+   */
+  tree x = build_global_decl ("x", integer_type_node);
+  tree int_star = build_pointer_type (integer_type_node);
+  tree p = build_global_decl ("p", int_star);
+
+  tree int_17 = build_int_cst (integer_type_node, 17);
+  tree addr_of_x = build1 (ADDR_EXPR, int_star, x);
+  tree offset_0 = build_int_cst (integer_type_node, 0);
+  tree star_p = build2 (MEM_REF, integer_type_node, p, offset_0);
+
+  region_model2_manager mgr;
+  region_model2 model (&mgr);
+
+  /* "x = 17;".  */
+  model.set_value (x, int_17, NULL);
+
+  /* "p = &x;".  */
+  model.set_value (p, addr_of_x, NULL);
+
+  const svalue2 *sval = model.get_rvalue (star_p, NULL);
+  ASSERT_EQ (sval->maybe_get_constant (), int_17);
+}
+
+/* Test for a POINTER_PLUS_EXPR followed by a MEM_REF.
+   Analogous to this code:
+     void test_6 (int a[10])
+     {
+       __analyzer_eval (a[3] == 42); [should be UNKNOWN]
+       a[3] = 42;
+       __analyzer_eval (a[3] == 42); [should be TRUE]
+     }
+   from data-model-1.c, which looks like this at the gimple level:
+       # __analyzer_eval (a[3] == 42); [should be UNKNOWN]
+       int *_1 = a_10(D) + 12;   # POINTER_PLUS_EXPR
+       int _2 = *_1;             # MEM_REF
+       _Bool _3 = _2 == 42;
+       int _4 = (int) _3;
+       __analyzer_eval (_4);
+
+       # a[3] = 42;
+       int *_5 = a_10(D) + 12;   # POINTER_PLUS_EXPR
+       *_5 = 42;                 # MEM_REF
+
+       # __analyzer_eval (a[3] == 42); [should be TRUE]
+       int *_6 = a_10(D) + 12;   # POINTER_PLUS_EXPR
+       int _7 = *_6;             # MEM_REF
+       _Bool _8 = _7 == 42;
+       int _9 = (int) _8;
+       __analyzer_eval (_9);  */
+
+static void
+test_POINTER_PLUS_EXPR_then_MEM_REF ()
+{
+  tree int_star = build_pointer_type (integer_type_node);
+  tree a = build_global_decl ("a", int_star);
+  tree offset_12 = build_int_cst (size_type_node, 12);
+  tree pointer_plus_expr = build2 (POINTER_PLUS_EXPR, int_star, a, offset_12);
+  tree offset_0 = build_int_cst (integer_type_node, 0);
+  tree mem_ref = build2 (MEM_REF, integer_type_node,
+			 pointer_plus_expr, offset_0);
+  region_model2_manager mgr;
+  region_model2 m (&mgr);
+  const svalue2 *tmp_2 = m.get_rvalue (mem_ref, NULL);
+  const region2 *tmp_5 = m.get_lvalue (mem_ref, NULL);
+
+  tree int_42 = build_int_cst (integer_type_node, 42);
+  m.set_value (mem_ref, int_42, NULL);
+  ASSERT_EQ (m.get_rvalue (mem_ref, NULL)->maybe_get_constant (), int_42);
+  //ASSERT_EQ (m.get_store_value (tmp_5)->maybe_get_constant (), int_42);
+  // TODO: verify value of tmp_2 and tmp_5
+}
+
+/* FIXME.  */
+
+static void
+test_malloc ()
+{
+  tree int_star = build_pointer_type (integer_type_node);
+  tree p = build_global_decl ("p", int_star);
+  tree n = build_global_decl ("n", integer_type_node);
+  tree n_times_4 = build2 (MULT_EXPR, size_type_node,
+			   n, build_int_cst (size_type_node, 4));
+
+  region_model2_manager mgr;
+  test_region_model2_context ctxt;
+  region_model2 model (&mgr);
+
+  /* "p = malloc (n * 4);".  */
+  const svalue2 *size_sval = model.get_rvalue (n_times_4, &ctxt);
+  const region2 *reg = model.create_region_for_heap_alloc (size_sval);
+  const svalue2 *ptr = mgr.get_ptr_svalue2 (int_star, reg);
+  model.set_value (model.get_lvalue (p, &ctxt), ptr, &ctxt);
+  // TODO: verify dynamic extents
+
+}
+
+/* FIXME.  */
+
+static void
+test_alloca ()
+{
+  auto_vec <tree> param_types;
+  tree fndecl = make_fndecl (integer_type_node,
+			     "test_fn",
+			     param_types);
+  allocate_struct_function (fndecl, true);
+
+
+  tree int_star = build_pointer_type (integer_type_node);
+  tree p = build_global_decl ("p", int_star);
+  tree n = build_global_decl ("n", integer_type_node);
+  tree n_times_4 = build2 (MULT_EXPR, size_type_node,
+			   n, build_int_cst (size_type_node, 4));
+
+  region_model2_manager mgr;
+  test_region_model2_context ctxt;
+  region_model2 model (&mgr);
+
+  /* Push stack frame.  */
+  const region2 *frame_reg
+    = model.push_frame (DECL_STRUCT_FUNCTION (fndecl),
+			NULL, &ctxt);
+  /* "p = alloca (n * 4);".  */
+  const svalue2 *size_sval = model.get_rvalue (n_times_4, &ctxt);
+  const region2 *reg = model.create_region_for_alloca (size_sval);
+  const svalue2 *ptr = mgr.get_ptr_svalue2 (int_star, reg);
+  model.set_value (model.get_lvalue (p, &ctxt), ptr, &ctxt);
+  // TODO: verify dynamic extents
+
+  /* Verify that the pointers to the alloca region are replaced by
+     poisoned values when the frame is popped.  */
+  purge_stats2 purged;
+  model.pop_frame (NULL, true, &purged, NULL, &ctxt);
+  ASSERT_EQ (model.get_rvalue (p, &ctxt)->get_kind (), svalue2::SK_POISONED);
+}
 
 /* Run all of the selftests within this file.  */
 
 void
 analyzer_region_model_cc_tests ()
 {
+  test_tree_cmp_on_constants ();
+  test_dump ();
+  test_dump_2 ();
+#if 0
+  test_dump_3 ();
+#endif
+  test_get_representative_tree ();
+  test_unique_constants ();
+  test_unique_unknowns ();
+#if 0
+  test_svalue2_equality ();
+  test_region2_equality ();
+#endif
+  test_initial_svalue2_folding ();
+  test_unaryop_svalue2_folding ();
+  test_binop_svalue2_folding ();
+  test_sub_svalue2_folding ();
+  test_descendent_of_p ();
+#if 0
+  test_purging_by_criteria ();
+  test_purge_unused_svalue2s ();
+#endif
+  test_assignment ();
+  test_compound_assignment ();
+  test_stack_frames ();
+  test_get_representative_path_var ();
+  test_model_equality_1 ();
+  test_model_equality_2 ();
+  test_model_equality_3 ();
+#if 0
+  test_model_equality_4 ();
+#endif
+  test_state_merging ();
+  test_constraint_merging ();
+  test_widening_constraints ();
+  test_iteration_1 ();
+  test_iteration_2 ();
+#if 0
+  test_malloc_constraints ();
+#endif
+  test_var ();
+  test_array ();
+  test_mem_ref ();
+  test_POINTER_PLUS_EXPR_then_MEM_REF ();
+  test_malloc ();
+  test_alloca ();
 }
 
 } // namespace selftest
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index d0ab2c12829..c066cb507ae 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -18,270 +18,3035 @@ You should have received a copy of the GNU General Public License
 along with GCC; see the file COPYING3.  If not see
 <http://www.gnu.org/licenses/>.  */
 
-#ifndef GCC_ANALYZER_REGION_MODEL_H
-#define GCC_ANALYZER_REGION_MODEL_H
+#ifndef GCC_ANALYZER_REGION_MODEL_2_H
+#define GCC_ANALYZER_REGION_MODEL_2_H
+
+#include "analyzer/uniq-manager.h" // FIXME
+#include "analyzer/store2.h" // FIXME
+#include "analyzer/program-point.h" // FIXME
+
+#define WIDENING 1
 
 /* Implementation of the region-based ternary model described in:
      "A Memory Model for Static Analysis of C Programs"
       (Zhongxing Xu, Ted Kremenek, and Jian Zhang)
      http://lcs.ios.ac.cn/~xuzb/canalyze/memmodel.pdf  */
 
-using namespace ana;
 
-namespace inchash
+/* FIXME:
+   This prototype has an alternate implementation of region_model.
+   Idea:
+   - no region_id or svalue_id, just pointers.
+   - regions are shared throughout the analysis, via a region_manager.
+   - should svalues also be shared?  Probably
+   - regions and values don't have state; region_model2 instances do
+     (though potentially we want to use the regions to store information
+     about the shared region structure e.g. subregions of map_region)
+   - no canonicalization or ID remapping should be needed
+   - uniqueness of regions: comparison by pointer rather than by fields
+     (but maybe we need to keep the compare_fields for implementing the
+     uniquifying?)
+   - not yet sure if we need validation
+   - the concept of what the "current stack" is needs to move from the
+     regions to the region_model2 (maybe a region *m_current_frame)
+   - do we still need region cloning?
+   - should there still be a root_region?  or should those things go in the
+     ext_state (or something owned by ext_state: region_model2_manager)
+   Hopefully this is a huge simplification, and (I hope) avoids lots of heap
+   churn as states are copied.
+
+   Other ideas:
+   - region subclasses no longer represent internal structure, but instead
+     represent how the regions are reached.  So e.g. a global
+     "struct coord c;" is now a decl_region, rather than a struct_region.
+
+   TODO:
+   - some selftests depend on bit sizes of types, hopefully via the TYPE_SIZE
+     macros (see e.g. INT_TYPE_SIZE in defaults.h).  Remember to test on
+     some other targets.  Similarly, big-endian vs little-endian.  */
+
+using namespace ana;
+
+namespace inchash
+{
+  extern void add_path_var (path_var pv, hash &hstate);
+} // namespace inchash
+
+
+namespace ana {
+
+/* A hash traits class that uses member functions to implement
+   the various required ops.  */
+
+template <typename Type>
+struct member_function_hash_traits : public typed_noop_remove<Type>
+{
+  typedef Type value_type;
+  typedef Type compare_type;
+  static inline hashval_t hash (value_type v) { return v.hash (); }
+  static inline bool equal (const value_type &existing,
+			    const value_type &candidate)
+  {
+    return existing == candidate;
+  }
+  static inline void mark_deleted (Type &t) { t.mark_deleted (); }
+  static inline void mark_empty (Type &t) { t.mark_empty (); }
+  static inline bool is_deleted (Type t) { return t.is_deleted (); }
+  static inline bool is_empty (Type t) { return t.is_empty (); }
+};
+
+/* A region_model2 is effectively a graph of regions and symbolic values.
+   FIXME: we're eliminating IDs in this prototype.
+   We store per-model IDs rather than pointers to make it easier to clone
+   and to compare graphs.  */
+
+/* Various operations delete information from a region_model2.
+
+   This struct tracks how many of each kind of entity were purged (e.g.
+   for selftests, and for debugging).  */
+
+struct purge_stats2
+{
+  purge_stats2 ()
+  : m_num_svalues (0),
+    m_num_regions (0),
+    m_num_equiv_classes (0),
+    m_num_constraints (0),
+    m_num_client_items (0)
+  {}
+
+  int m_num_svalues;
+  int m_num_regions;
+  int m_num_equiv_classes;
+  int m_num_constraints;
+  int m_num_client_items;
+};
+
+/* A measurement of the complexity of an svalue2 or region2, so that
+   we can avoid these tree-like structures growing without bounds and
+   thus leading to infinite chains of analysis.  */
+
+struct complexity
+{
+  complexity (unsigned num_nodes, unsigned max_depth)
+  : m_num_nodes (num_nodes), m_max_depth (max_depth)
+  {}
+
+  complexity (const region2 *reg);
+  complexity (const svalue2 *sval);
+  static complexity from_pair (const complexity &c1, const complexity &c2);
+
+  /* The total number of svalue2s and region2s in the tree of this
+     entity, including the entity itself.  */
+  unsigned m_num_nodes;
+
+  /* The maximum depth of the tree of this entity, including the
+     entity itself.  */
+  unsigned m_max_depth;
+};
+
+class visitor
+{
+public:
+  virtual void visit_region_svalue2 (const region_svalue2 *) {}
+  virtual void visit_constant_svalue2 (const constant_svalue2 *) {}
+  virtual void visit_unknown_svalue2 (const unknown_svalue2 *) {}
+  virtual void visit_poisoned_svalue2 (const poisoned_svalue2 *) {}
+  virtual void visit_setjmp_svalue2 (const setjmp_svalue2 *) {}
+  virtual void visit_initial_svalue2 (const initial_svalue2 *) {}
+  virtual void visit_unaryop_svalue2 (const unaryop_svalue2 *) {}
+  virtual void visit_binop_svalue2 (const binop_svalue2 *) {}
+  virtual void visit_sub_svalue2 (const sub_svalue2 *) {}
+  virtual void visit_unmergeable_svalue2 (const unmergeable_svalue2 *) {}
+  virtual void visit_placeholder_svalue2 (const placeholder_svalue2 *) {}
+  virtual void visit_widening_svalue2 (const widening_svalue2 *) {}
+  virtual void visit_compound_svalue2 (const compound_svalue2 *) {}
+  virtual void visit_conjured_svalue2 (const conjured_svalue2 *) {}
+
+  virtual void visit_region (const region2 *) {}
+};
+
+/* svalue and its subclasses.
+
+   The class hierarchy looks like this (using indentation to show
+   inheritance, and with kinds shown for the concrete subclasses):
+
+   svalue2
+     region_svalue2 (SK_REGION): a pointer to a region2
+     constant_svalue2 (SK_CONSTANT): a constant
+     unknown_svalue2 (SK_UNKNOWN): an unknowable value
+     poisoned_svalue2 (SK_POISONED): a unusable value (undefined)
+     setjmp_svalue2 (SK_SETJMP): a setjmp/longjmp buffer
+     initial2_svalue2 (SK_INITIAL): the initial value of a region2
+     unaryop_svalue2 (SK_UNARYOP): unary operation on another svalue2
+     binop_svalue2 (SK_BINOP): binary operation on two svalue2s
+     sub_svalue2 (SK_SUB): the result of accessing a subregion
+     unmergeable_svalue2 (SK_UNMERGEABLE): a value that is so interesting
+       from a control-flow perspective that it can inhibit state-merging
+     placeholder_svalue2 (SK_PLACEHOLDER): for use in selftests.
+     widening_svalue2 (SK_WIDENING): a merger of two svalues (possibly
+       in an iteration).
+     compound_svalue2 (SK_COMPOUND): a mapping of bit-ranges to svalues
+     conjured_svalue2 (SK_CONJURED): a value arising from a stmt.  */
+
+/* An abstract base class representing a value held by a region of memory.  */
+
+class svalue2
+{
+public:
+  /* An enum for discriminating between the different concrete subclasses
+   of svalue.  */
+  enum kind
+  {
+   SK_REGION,
+   SK_CONSTANT,
+   SK_UNKNOWN,
+   SK_POISONED,
+   SK_SETJMP,
+   SK_INITIAL,
+   SK_UNARYOP,
+   SK_BINOP,
+   SK_SUB,
+   SK_UNMERGEABLE,
+   SK_PLACEHOLDER,
+   SK_WIDENING,
+   SK_COMPOUND,
+   SK_CONJURED
+  };
+
+  virtual ~svalue2 () {}
+
+#if 0
+  bool operator== (const svalue2 &other) const;
+  bool operator!= (const svalue2 &other) const { return !(*this == other); }
+#endif
+
+  tree get_type () const { return m_type; }
+
+  virtual enum kind get_kind () const = 0;
+
+  void print (const region_model2 &model,
+	      pretty_printer *pp) const;
+
+#if 0
+  virtual void dump_dot_to_pp (const region_model2 &model,
+			       pretty_printer *pp) const;
+#endif
+
+  virtual void dump_to_pp (pretty_printer *pp, bool simple) const = 0;
+  void dump (bool simple=true) const;
+  label_text get_desc (bool simple=true) const;
+
+  virtual const region_svalue2 *
+  dyn_cast_region_svalue2 () const { return NULL; }
+  virtual const constant_svalue2 *
+  dyn_cast_constant_svalue2 () const { return NULL; }
+  virtual const poisoned_svalue2 *
+  dyn_cast_poisoned_svalue2 () const { return NULL; }
+  virtual const setjmp_svalue2 *
+  dyn_cast_setjmp_svalue2 () const { return NULL; }
+  virtual const initial_svalue2 *
+  dyn_cast_initial_svalue2 () const { return NULL; }
+  virtual const unaryop_svalue2 *
+  dyn_cast_unaryop_svalue2 () const { return NULL; }
+  virtual const binop_svalue2 *
+  dyn_cast_binop_svalue2 () const { return NULL; }
+  virtual const sub_svalue2 *
+  dyn_cast_sub_svalue2 () const { return NULL; }
+  virtual const unmergeable_svalue2 *
+  dyn_cast_unmergeable_svalue2 () const { return NULL; }
+  virtual const widening_svalue2 *
+  dyn_cast_widening_svalue2 () const { return NULL; }
+  virtual const compound_svalue2 *
+  dyn_cast_compound_svalue2 () const { return NULL; }
+  virtual const conjured_svalue2 *
+  dyn_cast_conjured_svalue2 () const { return NULL; }
+
+  tree maybe_get_constant () const;
+  const svalue2 *maybe_undo_cast () const;
+  const svalue2 *unwrap_any_unmergeable () const;
+
+  const svalue2 *can_merge_p (const svalue2 *other,
+			      region_model2_manager *mgr,
+			      model2_merger *merger) const;
+
+  const complexity &get_complexity () const { return m_complexity; }
+
+  virtual void accept (visitor *v) const  = 0;
+
+  bool live_p (const svalue2_set &live_svalues,
+	       const region_model2 *model) const;
+  virtual bool implicitly_live_p (const svalue2_set &live_svalues,
+				  const region_model2 *model) const;
+
+ protected:
+  svalue2 (complexity c, tree type)
+  : m_complexity (c), m_type (type)
+  {}
+
+ private:
+  complexity m_complexity;
+  tree m_type;
+};
+
+/* Concrete subclass of svalue2 representing a pointer value that points to
+   a known region  */
+
+class region_svalue2 : public svalue2
+{
+public:
+  region_svalue2 (tree type, const region2 *reg)
+  : svalue2 (complexity (reg), type),
+    m_reg (reg)
+  {
+    gcc_assert (m_reg != NULL);
+  }
+
+  enum kind get_kind () const FINAL OVERRIDE { return SK_REGION; }
+
+#if 0
+  void dump_dot_to_pp (const region_model2 &model,
+		       pretty_printer *pp) const
+    FINAL OVERRIDE;
+#endif
+
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+
+  const region_svalue2 *
+  dyn_cast_region_svalue2 () const FINAL OVERRIDE { return this; }
+
+  void accept (visitor *v) const FINAL OVERRIDE;
+
+  const region2 * get_pointee () const { return m_reg; }
+
+#if 0
+  static void merge_values (const region_svalue2 &region_sval_a,
+			    const region_svalue2 &region_sval_b,
+			    svalue2_id *merged_sid,
+			    tree type,
+			    model_merger *merger);
+#endif
+
+  static tristate eval_condition (const region_svalue2 *lhs_ptr,
+				  enum tree_code op,
+				  const region_svalue2 *rhs_ptr);
+
+ private:
+  const region2 *m_reg;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const region_svalue2 *>::test (const svalue2 *sval)
+{
+  return sval->get_kind () == svalue2::SK_REGION;
+}
+
+namespace ana {
+
+/* Concrete subclass of svalue2 representing a specific constant value.  */
+
+class constant_svalue2 : public svalue2
+{
+public:
+  constant_svalue2 (tree cst_expr)
+  : svalue2 (complexity (1, 1), TREE_TYPE (cst_expr)), m_cst_expr (cst_expr)
+  {
+    gcc_assert (cst_expr);
+    gcc_assert (CONSTANT_CLASS_P (cst_expr));
+  }
+
+  enum kind get_kind () const FINAL OVERRIDE { return SK_CONSTANT; }
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+
+  const constant_svalue2 *
+  dyn_cast_constant_svalue2 () const FINAL OVERRIDE { return this; }
+
+  void accept (visitor *v) const FINAL OVERRIDE;
+
+  bool implicitly_live_p (const svalue2_set &,
+			  const region_model2 *) const FINAL OVERRIDE;
+
+  tree get_constant () const { return m_cst_expr; }
+
+#if 0
+  static void merge_values (const constant_svalue2 &cst_sval_a,
+			    const constant_svalue2 &cst_sval_b,
+			    svalue2_id *merged_sid,
+			    model_merger *merger);
+#endif
+
+  static tristate eval_condition (const constant_svalue2 *lhs,
+				  enum tree_code op,
+				  const constant_svalue2 *rhs);
+
+ private:
+  tree m_cst_expr;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const constant_svalue2 *>::test (const svalue2 *sval)
+{
+  return sval->get_kind () == svalue2::SK_CONSTANT;
+}
+
+namespace ana {
+
+/* Concrete subclass of svalue2 representing an unknowable value, the bottom
+   value when thinking of svalue2s as a lattice.
+   This is a singleton (w.r.t. its manager): there is a single unknown_svalue2
+   per type.  Self-comparisons of such instances yield "unknown".  */
+
+class unknown_svalue2 : public svalue2
+{
+public:
+  unknown_svalue2 (tree type)
+  : svalue2 (complexity (1, 1), type)
+  {}
+
+  enum kind get_kind () const FINAL OVERRIDE { return SK_UNKNOWN; }
+  void accept (visitor *v) const FINAL OVERRIDE;
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+};
+
+#if 0
+/* An enum describing a particular kind of "poisoned" value.  */
+
+enum poison_kind
+{
+  /* For use to describe uninitialized memory.  */
+  POISON_KIND_UNINIT,
+
+  /* For use to describe freed memory.  */
+  POISON_KIND_FREED,
+
+  /* For use on pointers to regions within popped stack frames.  */
+  POISON_KIND_POPPED_STACK
+};
+#endif
+
+extern const char *poison_kind_to_str (enum poison_kind);
+
+/* Concrete subclass of svalue2 representing a value that should not
+   be used (e.g. uninitialized memory, freed memory).  */
+
+class poisoned_svalue2 : public svalue2
+{
+public:
+  /* A support class for uniquifying instances of poisoned_svalue2.  */
+  struct key_t
+  {
+    key_t (enum poison_kind kind, tree type)
+    : m_kind (kind), m_type (type)
+    {}
+
+    hashval_t hash () const
+    {
+      inchash::hash hstate;
+      hstate.add_int (m_kind);
+      hstate.add_ptr (m_type);
+      return hstate.end ();
+    }
+
+    bool operator== (const key_t &other) const
+    {
+      return (m_kind == other.m_kind && m_type == other.m_type);
+    }
+
+    void mark_deleted () { m_type = reinterpret_cast<tree> (1); }
+    void mark_empty () { m_type = NULL_TREE; }
+    bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); }
+    bool is_empty () const { return m_type == NULL_TREE; }
+
+    enum poison_kind m_kind;
+    tree m_type;
+  };
+
+  poisoned_svalue2 (enum poison_kind kind, tree type)
+  : svalue2 (complexity (1, 1), type), m_kind (kind) {}
+
+  enum kind get_kind () const FINAL OVERRIDE { return SK_POISONED; }
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+
+  const poisoned_svalue2 *
+  dyn_cast_poisoned_svalue2 () const FINAL OVERRIDE { return this; }
+
+  void accept (visitor *v) const FINAL OVERRIDE;
+
+  enum poison_kind get_poison_kind () const { return m_kind; }
+
+ private:
+  enum poison_kind m_kind;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const poisoned_svalue2 *>::test (const svalue2 *sval)
+{
+  return sval->get_kind () == svalue2::SK_POISONED;
+}
+
+template <> struct default_hash_traits<poisoned_svalue2::key_t>
+: public member_function_hash_traits<poisoned_svalue2::key_t>
+{
+  static const bool empty_zero_p = true;
+};
+
+namespace ana {
+
+/* Concrete subclass of svalue2 representing buffers for setjmp/sigsetjmp,
+   so that longjmp/siglongjmp can potentially "return" to an entirely
+   different function.  */
+
+class setjmp_svalue2 : public svalue2
+{
+public:
+  /* A support class for uniquifying instances of poisoned_svalue2.  */
+  struct key_t
+  {
+    key_t (const setjmp_record &record, tree type)
+    : m_record (record), m_type (type)
+    {}
+
+    hashval_t hash () const
+    {
+      inchash::hash hstate;
+      m_record.add_to_hash (&hstate);
+      hstate.add_ptr (m_type);
+      return hstate.end ();
+    }
+
+    bool operator== (const key_t &other) const
+    {
+      return (m_record == other.m_record && m_type == other.m_type);
+    }
+
+    void mark_deleted () { m_type = reinterpret_cast<tree> (1); }
+    void mark_empty () { m_type = NULL_TREE; }
+    bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); }
+    bool is_empty () const { return m_type == NULL_TREE; }
+
+    setjmp_record m_record;
+    tree m_type;
+  };
+
+  setjmp_svalue2 (const setjmp_record &setjmp_record,
+		  tree type)
+  : svalue2 (complexity (1, 1), type), m_setjmp_record (setjmp_record)
+  {}
+
+  enum kind get_kind () const FINAL OVERRIDE { return SK_SETJMP; }
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+
+  const setjmp_svalue2 *
+  dyn_cast_setjmp_svalue2 () const FINAL OVERRIDE { return this; }
+
+  void accept (visitor *v) const FINAL OVERRIDE;
+
+  int get_enode_index () const;
+
+  const setjmp_record &get_setjmp_record () const { return m_setjmp_record; }
+
+ private:
+  setjmp_record m_setjmp_record;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const setjmp_svalue2 *>::test (const svalue2 *sval)
+{
+  return sval->get_kind () == svalue2::SK_SETJMP;
+}
+
+template <> struct default_hash_traits<setjmp_svalue2::key_t>
+: public member_function_hash_traits<setjmp_svalue2::key_t>
+{
+  static const bool empty_zero_p = true;
+};
+
+namespace ana {
+
+/* Concrete subclass of svalue2 representing the initial value of a
+   specific region.
+
+   FIXME: trying this approach:
+   This represents the initial value at the start of the analysis path,
+   as opposed to the first time the region is accessed during the path.
+   Hence as soon as we have a call to an unknown function, all previously
+   unmodelled globals become implicitly "unknown" rathen than "initial".  */
+
+class initial_svalue2 : public svalue2
+{
+public:
+  initial_svalue2 (tree type, const region2 *reg)
+  : svalue2 (complexity (reg), type), m_reg (reg)
+  {
+    gcc_assert (m_reg != NULL);
+  }
+
+  enum kind get_kind () const FINAL OVERRIDE { return SK_INITIAL; }
+
+#if 0
+  void dump_dot_to_pp (const region_model2 &model,
+		       pretty_printer *pp) const
+    FINAL OVERRIDE;
+#endif
+
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+
+  const initial_svalue2 *
+  dyn_cast_initial_svalue2 () const FINAL OVERRIDE { return this; }
+
+  void accept (visitor *v) const FINAL OVERRIDE;
+
+  bool implicitly_live_p (const svalue2_set &,
+			  const region_model2 *) const FINAL OVERRIDE;
+
+  const region2 * get_region () const { return m_reg; }
+
+#if 0
+  static void merge_values (const initial_svalue2 &region_sval_a,
+			    const initial_svalue2 &region_sval_b,
+			    svalue2_id *merged_sid,
+			    tree type,
+			    model_merger *merger);
+#endif
+
+ private:
+  const region2 *m_reg;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const initial_svalue2 *>::test (const svalue2 *sval)
+{
+  return sval->get_kind () == svalue2::SK_INITIAL;
+}
+
+namespace ana {
+
+/* Concrete subclass of svalue2 representing a unary operation on
+   another svalue2s (e.g. a cast).  */
+
+class unaryop_svalue2 : public svalue2
+{
+public:
+  /* A support class for uniquifying instances of unaryop_svalue2.  */
+  struct key_t
+  {
+    key_t (tree type, enum tree_code op, const svalue2 *arg)
+    : m_type (type), m_op (op), m_arg (arg)
+    {}
+
+    hashval_t hash () const
+    {
+      inchash::hash hstate;
+      hstate.add_ptr (m_type);
+      hstate.add_int (m_op);
+      hstate.add_ptr (m_arg);
+      return hstate.end ();
+    }
+
+    bool operator== (const key_t &other) const
+    {
+      return (m_type == other.m_type
+	      && m_op == other.m_op
+	      && m_arg == other.m_arg);
+    }
+
+    void mark_deleted () { m_type = reinterpret_cast<tree> (1); }
+    void mark_empty () { m_type = NULL_TREE; }
+    bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); }
+    bool is_empty () const { return m_type == NULL_TREE; }
+
+    tree m_type;
+    enum tree_code m_op;
+    const svalue2 *m_arg;
+  };
+
+  unaryop_svalue2 (tree type, enum tree_code op, const svalue2 *arg)
+  : svalue2 (complexity (arg), type), m_op (op), m_arg (arg)
+  {
+  }
+
+  enum kind get_kind () const FINAL OVERRIDE { return SK_UNARYOP; }
+  const unaryop_svalue2 *
+  dyn_cast_unaryop_svalue2 () const FINAL OVERRIDE { return this; }
+
+  void accept (visitor *v) const FINAL OVERRIDE;
+
+  bool implicitly_live_p (const svalue2_set &,
+			  const region_model2 *) const FINAL OVERRIDE;
+
+#if 0
+  void dump_dot_to_pp (const region_model2 &model,
+		       pretty_printer *pp) const
+    FINAL OVERRIDE;
+#endif
+
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+
+
+  enum tree_code get_op () const { return m_op; }
+  const svalue2 *get_arg () const { return m_arg; }
+
+ private:
+  enum tree_code m_op;
+  const svalue2 *m_arg;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const unaryop_svalue2 *>::test (const svalue2 *sval)
+{
+  return sval->get_kind () == svalue2::SK_UNARYOP;
+}
+
+template <> struct default_hash_traits<unaryop_svalue2::key_t>
+: public member_function_hash_traits<unaryop_svalue2::key_t>
+{
+  static const bool empty_zero_p = true;
+};
+
+namespace ana {
+
+/* Concrete subclass of svalue2 representing a binary operation of
+   two svalue2s.  */
+
+class binop_svalue2 : public svalue2
+{
+public:
+  /* A support class for uniquifying instances of binop_svalue2.  */
+  struct key_t
+  {
+    key_t (tree type, enum tree_code op,
+	   const svalue2 *arg0, const svalue2 *arg1)
+    : m_type (type), m_op (op), m_arg0 (arg0), m_arg1 (arg1)
+    {}
+
+    hashval_t hash () const
+    {
+      inchash::hash hstate;
+      hstate.add_ptr (m_type);
+      hstate.add_int (m_op);
+      hstate.add_ptr (m_arg0);
+      hstate.add_ptr (m_arg1);
+      return hstate.end ();
+    }
+
+    bool operator== (const key_t &other) const
+    {
+      return (m_type == other.m_type
+	      && m_op == other.m_op
+	      && m_arg0 == other.m_arg0
+	      && m_arg1 == other.m_arg1);
+    }
+
+    void mark_deleted () { m_type = reinterpret_cast<tree> (1); }
+    void mark_empty () { m_type = NULL_TREE; }
+    bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); }
+    bool is_empty () const { return m_type == NULL_TREE; }
+
+    tree m_type;
+    enum tree_code m_op;
+    const svalue2 *m_arg0;
+    const svalue2 *m_arg1;
+  };
+
+  binop_svalue2 (tree type, enum tree_code op,
+		 const svalue2 *arg0, const svalue2 *arg1)
+  : svalue2 (complexity::from_pair (arg0->get_complexity (),
+				    arg1->get_complexity ()),
+	     type),
+    m_op (op), m_arg0 (arg0), m_arg1 (arg1)
+  {
+  }
+
+  enum kind get_kind () const FINAL OVERRIDE { return SK_BINOP; }
+  virtual const binop_svalue2 *dyn_cast_binop_svalue2 () const { return this; }
+
+  void accept (visitor *v) const FINAL OVERRIDE;
+
+  bool implicitly_live_p (const svalue2_set &,
+			  const region_model2 *) const FINAL OVERRIDE;
+
+  enum tree_code get_op () const { return m_op; }
+  const svalue2 *get_arg0 () const { return m_arg0; }
+  const svalue2 *get_arg1 () const { return m_arg1; }
+
+#if 0
+  void dump_dot_to_pp (const region_model2 &model,
+		       pretty_printer *pp) const
+    FINAL OVERRIDE;
+#endif
+
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+
+#if 0
+  const binop_svalue2 *
+  dyn_cast_binop_svalue2 () const FINAL OVERRIDE { return this; }
+#endif
+
+ private:
+  enum tree_code m_op;
+  const svalue2 *m_arg0;
+  const svalue2 *m_arg1;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const binop_svalue2 *>::test (const svalue2 *sval)
+{
+  return sval->get_kind () == svalue2::SK_BINOP;
+}
+
+template <> struct default_hash_traits<binop_svalue2::key_t>
+: public member_function_hash_traits<binop_svalue2::key_t>
+{
+  static const bool empty_zero_p = true;
+};
+
+namespace ana {
+
+/* Concrete subclass of svalue2 representing the result of accessing a subregion
+   of another svalue2 (the value of a component/field of a struct, or an element
+   from an array).
+   FIXME: do we want to split this into two different classes for that?  */
+
+class sub_svalue2 : public svalue2
+{
+public:
+  /* A support class for uniquifying instances of sub_svalue2.  */
+  struct key_t
+  {
+    key_t (tree type, const svalue2 *parent_svalue, const region2 *subregion)
+    : m_type (type), m_parent_svalue (parent_svalue), m_subregion (subregion)
+    {}
+
+    hashval_t hash () const
+    {
+      inchash::hash hstate;
+      hstate.add_ptr (m_type);
+      hstate.add_ptr (m_parent_svalue);
+      hstate.add_ptr (m_subregion);
+      return hstate.end ();
+    }
+
+    bool operator== (const key_t &other) const
+    {
+      return (m_type == other.m_type
+	      && m_parent_svalue == other.m_parent_svalue
+	      && m_subregion == other.m_subregion);
+    }
+
+    void mark_deleted () { m_type = reinterpret_cast<tree> (1); }
+    void mark_empty () { m_type = NULL_TREE; }
+    bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); }
+    bool is_empty () const { return m_type == NULL_TREE; }
+
+    tree m_type;
+    const svalue2 *m_parent_svalue;
+    const region2 *m_subregion;
+  };
+  sub_svalue2 (tree type, const svalue2 *parent_svalue,
+	       const region2 *subregion);
+
+  enum kind get_kind () const FINAL OVERRIDE { return SK_SUB; }
+  const sub_svalue2 *dyn_cast_sub_svalue2 () const FINAL OVERRIDE
+  {
+    return this;
+  }
+  void accept (visitor *v) const FINAL OVERRIDE;
+  bool implicitly_live_p (const svalue2_set &,
+			  const region_model2 *) const FINAL OVERRIDE;
+
+#if 0
+  void dump_dot_to_pp (const region_model2 &model,
+		       pretty_printer *pp) const
+    FINAL OVERRIDE;
+#endif
+
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+
+  const svalue2 *get_parent () const { return m_parent_svalue; }
+  const region2 *get_subregion () const { return m_subregion; }
+
+ private:
+  const svalue2 *m_parent_svalue;
+  const region2 *m_subregion;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const sub_svalue2 *>::test (const svalue2 *sval)
+{
+  return sval->get_kind () == svalue2::SK_SUB;
+}
+
+template <> struct default_hash_traits<sub_svalue2::key_t>
+: public member_function_hash_traits<sub_svalue2::key_t>
+{
+  static const bool empty_zero_p = true;
+};
+
+namespace ana {
+
+/* Concrete subclass of svalue2: decorate another svalue2,
+   so that the resulting svalue2 can be identified as being
+   "interesting to control flow".
+   For example, consider the return value from setjmp.  We
+   don't want to merge states in which the result is 0 with
+   those in which the result is non-zero.  By using an
+   unmergeable_svalue2 for the result, we can inhibit such merges
+   and have separate exploded nodes for those states, keeping
+   the first and second returns from setjmp distinct in the exploded
+   graph.
+   FIXME: possibly rename to "significant_svalue2"?
+   FIXME: possibly use this to capture "significant" conditions in
+   control flow, and to use this to tweak descriptions of conditions
+   in paths.  Specifically, could this be used by
+     start_cfg_edge_event::maybe_describe_condition
+   to emit things like
+     following 'true' branch (when value returned from 'setjmp' is non-zero)
+   or somesuch.  */
+
+class unmergeable_svalue2 : public svalue2
+{
+public:
+  unmergeable_svalue2 (const svalue2 *arg)
+  : svalue2 (complexity (arg), arg->get_type ()), m_arg (arg)
+  {
+  }
+
+  enum kind get_kind () const FINAL OVERRIDE { return SK_UNMERGEABLE; }
+  const unmergeable_svalue2 *
+  dyn_cast_unmergeable_svalue2 () const FINAL OVERRIDE { return this; }
+  void accept (visitor *v) const FINAL OVERRIDE;
+  bool implicitly_live_p (const svalue2_set &,
+			  const region_model2 *) const FINAL OVERRIDE;
+
+#if 0
+  void dump_dot_to_pp (const region_model2 &model,
+		       pretty_printer *pp) const
+    FINAL OVERRIDE;
+#endif
+
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+
+  const svalue2 *get_arg () const { return m_arg; }
+
+ private:
+  const svalue2 *m_arg;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const unmergeable_svalue2 *>::test (const svalue2 *sval)
+{
+  return sval->get_kind () == svalue2::SK_UNMERGEABLE;
+}
+
+namespace ana {
+
+/* Concrete subclass of svalue2 for use in selftests, where
+   we want a specific but unknown svalue2.
+   Unlike other svalue2 subclasses these aren't managed by
+   region_model2_manager.  */
+
+class placeholder_svalue2 : public svalue2
+{
+public:
+  placeholder_svalue2 (tree type, const char *name)
+  : svalue2 (complexity (1, 1), type), m_name (name)
+  {
+  }
+
+  enum kind get_kind () const FINAL OVERRIDE { return SK_PLACEHOLDER; }
+  void accept (visitor *v) const FINAL OVERRIDE;
+
+#if 0
+  void dump_dot_to_pp (const region_model2 &model,
+		       pretty_printer *pp) const
+    FINAL OVERRIDE;
+#endif
+
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+
+#if 0
+  binop_svalue2 *dyn_cast_placeholder_svalue2 () FINAL OVERRIDE { return this; }
+#endif
+
+ private:
+  const char *m_name;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <placeholder_svalue2 *>::test (svalue2 *sval)
+{
+  return sval->get_kind () == svalue2::SK_PLACEHOLDER;
+}
+
+namespace ana {
+
+/* Concrete subclass of svalue2 representing a "widening" seen when merging
+   states, widening from a base value to {base value, iter value} and thus
+   representing a possible fixed point in an iteration from the base to
+   +ve infinity, or -ve infinity, and thus useful for representing a value
+   within a loop.
+   We also need to capture the program_point at which the merger happens,
+   so that distinguish between different iterators, and thus handle
+   nested loops.
+
+   FIXME: firm this up; clarify the above.  */
+
+class widening_svalue2 : public svalue2
+{
+public:
+  /* A support class for uniquifying instances of widening_svalue2.  */
+  struct key_t
+  {
+#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
+    {
+      inchash::hash hstate;
+      hstate.add_ptr (m_base_sval);
+      hstate.add_ptr (m_iter_sval);
+      return hstate.end ();
+    }
+
+    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);
+    }
+
+    void mark_deleted () { m_type = reinterpret_cast<tree> (1); }
+    void mark_empty () { m_type = NULL_TREE; }
+    bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); }
+    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;
+  };
+
+  enum direction_t
+    {
+     DIR_ASCENDING,
+     DIR_DESCENDING,
+     DIR_UNKNOWN
+    };
+  widening_svalue2 (tree type, const program_point &point,
+		    const svalue2 *base_sval, const svalue2 *iter_sval)
+  : svalue2 (complexity::from_pair (base_sval->get_complexity (),
+				    iter_sval->get_complexity ()),
+	     type),
+    m_point (point.get_function_point ()),
+    m_base_sval (base_sval), m_iter_sval (iter_sval)
+  {
+  }
+
+  enum kind get_kind () const FINAL OVERRIDE { return SK_WIDENING; }
+  const widening_svalue2 *dyn_cast_widening_svalue2 () const { return this; }
+  void accept (visitor *v) const FINAL OVERRIDE;
+
+#if 0
+  void dump_dot_to_pp (const region_model2 &model,
+		       pretty_printer *pp) const
+    FINAL OVERRIDE;
+#endif
+
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+
+  const svalue2 *get_base_svalue2 () const { return m_base_sval; }
+  const svalue2 *get_iter_svalue2 () const { return m_iter_sval; }
+
+  enum direction_t get_direction () const;
+
+  tristate eval_condition_without_cm (enum tree_code op,
+				      tree rhs_cst) const;
+
+ private:
+#if 0
+  program_point m_point;
+#else
+  function_point m_point;
+#endif
+  const svalue2 *m_base_sval;
+  const svalue2 *m_iter_sval;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <widening_svalue2 *>::test (svalue2 *sval)
+{
+  return sval->get_kind () == svalue2::SK_WIDENING;
+}
+
+#if 1
+template <> struct default_hash_traits<widening_svalue2::key_t>
+: public member_function_hash_traits<widening_svalue2::key_t>
+{
+  static const bool empty_zero_p = true;
+};
+#endif
+
+namespace ana {
+
+/* Concrete subclass of svalue2 representing a mapping of bit-ranges
+   to svalues, analogous to a cluster within the store.
+
+   This is for use in places where we want to represent a store-like
+   mapping, but are required to use an svalue2, such as when handling
+   compound assignments and compound return values.
+
+   Instances of this class shouldn't be bound as-is into the store;
+   instead they should be unpacked.  Similarly, they should not be
+   nested.  */
+
+class compound_svalue2 : public svalue2
+{
+public:
+  typedef binding_map::iterator_t iterator_t;
+
+  /* A support class for uniquifying instances of compound_svalue2.
+     Note that to avoid copies, keys store pointers to binding_maps,
+     rather than the maps themselves.  */
+  struct key_t
+  {
+    key_t (tree type, const binding_map *map_ptr)
+    : m_type (type), m_map_ptr (map_ptr)
+    {}
+
+    hashval_t hash () const
+    {
+      inchash::hash hstate;
+      hstate.add_ptr (m_type);
+      //hstate.add_ptr (m_map_ptr); // TODO
+      return hstate.end ();
+    }
+
+    bool operator== (const key_t &other) const
+    {
+      return (m_type == other.m_type
+	      && *m_map_ptr == *other.m_map_ptr);
+    }
+
+    void mark_deleted () { m_type = reinterpret_cast<tree> (1); }
+    void mark_empty () { m_type = NULL_TREE; }
+    bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); }
+    bool is_empty () const { return m_type == NULL_TREE; }
+
+    tree m_type;
+    const binding_map *m_map_ptr;
+  };
+
+  compound_svalue2 (tree type, const binding_map &map)
+  : svalue2 (calc_complexity (map), type),
+    m_map (map)
+  {
+  }
+
+  enum kind get_kind () const FINAL OVERRIDE { return SK_COMPOUND; }
+  const compound_svalue2 *dyn_cast_compound_svalue2 () const { return this; }
+  void accept (visitor *v) const FINAL OVERRIDE;
+
+  iterator_t begin () const { return m_map.begin (); }
+  iterator_t end () const { return m_map.end (); }
+
+#if 0
+  void dump_dot_to_pp (const region_model2 &model,
+		       pretty_printer *pp) const
+    FINAL OVERRIDE;
+#endif
+
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+
+  struct key_t make_key () const
+  {
+    return key_t (get_type (), &m_map);
+  }
+
+ private:
+  static complexity calc_complexity (const binding_map &map);
+
+  binding_map m_map;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <compound_svalue2 *>::test (svalue2 *sval)
+{
+  return sval->get_kind () == svalue2::SK_COMPOUND;
+}
+
+template <> struct default_hash_traits<compound_svalue2::key_t>
+: public member_function_hash_traits<compound_svalue2::key_t>
+{
+  static const bool empty_zero_p = true;
+};
+
+namespace ana {
+
+/* A defined value arising from a statement, where we want to identify a
+   particular unknown value, rather than resorting to the unknown_value
+   singleton, so that the value can have sm-state.
+
+   Comparisons of variables that share the same conjured_svalue2 are known
+   to be equal, even if we don't know what the value is.
+
+   For example, this is used for the values of regions that may have been
+   touched when calling an unknown function.
+
+   The value captures a region as well as a stmt in order to avoid falsely
+   aliasing the various values that could arise in one statement.  For
+   example, after:
+      unknown_fn (&a, &b);
+   we want values to clobber a and b with, but we don't want to use the
+   same value, or it would falsely implicitly assume that a == b.  */
+
+class conjured_svalue2 : public svalue2
+{
+public:
+  typedef binding_map::iterator_t iterator_t;
+
+  /* A support class for uniquifying instances of conjured_svalue2.  */
+  struct key_t
+  {
+    key_t (tree type, const gimple *stmt, const region2 *id_reg)
+    : m_type (type), m_stmt (stmt), m_id_reg (id_reg)
+    {}
+
+    hashval_t hash () const
+    {
+      inchash::hash hstate;
+      hstate.add_ptr (m_type);
+      hstate.add_ptr (m_stmt);
+      hstate.add_ptr (m_id_reg);
+      return hstate.end ();
+    }
+
+    bool operator== (const key_t &other) const
+    {
+      return (m_type == other.m_type
+	      && m_stmt == other.m_stmt
+	      && m_id_reg == other.m_id_reg);
+    }
+
+    /* Use m_stmt to mark empty/deleted, as m_type can be NULL for
+       legitimate instances.  */
+    void mark_deleted () { m_stmt = reinterpret_cast<const gimple *> (1); }
+    void mark_empty () { m_stmt = NULL; }
+    bool is_deleted () const
+    {
+      return m_stmt == reinterpret_cast<const gimple *> (1);
+    }
+    bool is_empty () const { return m_stmt == NULL; }
+
+    tree m_type;
+    const gimple *m_stmt;
+    const region2 *m_id_reg;
+  };
+
+  conjured_svalue2 (tree type, const gimple *stmt, const region2 *id_reg)
+  : svalue2 (complexity (id_reg), type),
+    m_stmt (stmt), m_id_reg (id_reg)
+  {
+    gcc_assert (m_stmt != NULL);
+  }
+
+  enum kind get_kind () const FINAL OVERRIDE { return SK_CONJURED; }
+  const conjured_svalue2 *dyn_cast_conjured_svalue2 () const { return this; }
+  void accept (visitor *v) const FINAL OVERRIDE;
+
+#if 0
+  void dump_dot_to_pp (const region_model2 &model,
+		       pretty_printer *pp) const
+    FINAL OVERRIDE;
+#endif
+
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+
+ private:
+  const gimple *m_stmt;
+  const region2 *m_id_reg;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <conjured_svalue2 *>::test (svalue2 *sval)
+{
+  return sval->get_kind () == svalue2::SK_CONJURED;
+}
+
+#if 1
+template <> struct default_hash_traits<conjured_svalue2::key_t>
+: public member_function_hash_traits<conjured_svalue2::key_t>
+{
+  static const bool empty_zero_p = true;
+};
+#endif
+
+namespace ana {
+
+/* Region and its subclasses.
+
+   The class hierarchy looks like this (using indentation to show
+   inheritance, and with region_kinds shown for the concrete subclasses):
+
+   FIXME: this needs a rewrite; reorganizing regions by identity, rather than
+   structure.  So e.g. a global "struct coord c;" is now a decl_region, rather
+   than a struct_region.
+
+   region
+     space_region
+       frame_region (RK_FRAME)
+       globals_region (RK_GLOBALS)
+       code_region (RK_CODE)
+       stack_region (RK_STACK)
+       heap_region (RK_HEAP)
+       root_region (RK_ROOT)
+     function_region (RK_FUNCTION)
+     label_region2 (RK_LABEL)
+     symbolic_region (RK_SYMBOLIC)
+     field_region2 (RK_FIELD)
+     element_region2 (RK_ELEMENT)
+     offset_region2 (RK_OFFSET)
+     heap_allocated_region2 (RK_HEAP_ALLOCATED)
+     alloca_region2 (RK_ALLOCA)
+     string_region2 (RK_STRING)
+     unknown_region2 (RK_UNKNOWN).  */
+
+/* Abstract base class representing a chunk of memory.
+
+   Regions form a tree-like hierarchy, with a root region at the base,
+   with memory space regions within it, representing the stack and
+   globals, with frames within the stack, and regions for variables
+   within the frames and the "globals" region.  Regions for structs
+   can have subregions for fields.
+
+   FIXME:
+   A region can optionally have a value, or inherit its value from
+   the first ancestor with a value.  For example, the stack region
+   has a "uninitialized" poison value which is inherited by all
+   descendent regions that don't themselves have a value.  */
+
+class region2
+{
+public:
+  /* An enum for discriminating between the different concrete subclasses
+     of region2.  */
+  enum kind
+  {
+   RK_FRAME,
+   RK_GLOBALS,
+   RK_CODE,
+   RK_FUNCTION,
+   RK_LABEL,
+   RK_STACK,
+   RK_HEAP,
+   RK_ROOT,
+   RK_SYMBOLIC,
+   RK_DECL,
+   RK_FIELD,
+   RK_ELEMENT,
+   RK_OFFSET,
+   RK_CAST,
+   RK_HEAP_ALLOCATED,
+   RK_ALLOCA,
+   RK_STRING,
+   RK_UNKNOWN
+  };
+  static const char *kind_to_str (enum kind);
+
+  virtual ~region2 ();
+
+  bool operator== (const region2 &other) const;
+  bool operator!= (const region2 &other) const { return !(*this == other); }
+
+  unsigned get_id () const { return m_id; }
+  static int cmp_ids (const region2 *reg1, const region2 *reg2);
+
+  virtual enum kind get_kind () const = 0;
+  virtual const frame_region2 *
+  dyn_cast_frame_region2 () const { return NULL; }
+  virtual const function_region2 *
+  dyn_cast_function_region2 () const { return NULL; }
+  virtual const symbolic_region2 *
+  dyn_cast_symbolic_region2 () const { return NULL; }
+  virtual const decl_region2 *
+  dyn_cast_decl_region2 () const { return NULL; }
+  virtual const field_region2 *
+  dyn_cast_field_region2 () const { return NULL; }
+  virtual const element_region2 *
+  dyn_cast_element_region2 () const { return NULL; }
+  virtual const offset_region2 *
+  dyn_cast_offset_region2 () const { return NULL; }
+  virtual const cast_region2 *
+  dyn_cast_cast_region2 () const { return NULL; }
+  virtual const string_region2 *
+  dyn_cast_string_region2 () const { return NULL; }
+
+  virtual void accept (visitor *v) const;
+
+  const region2 *get_parent_region () const { return m_parent; }
+  const region2 *get_base_region () const;
+  bool base_region_p () const;
+  bool descendent_of_p (const region2 *elder) const;
+  const frame_region2 *maybe_get_frame_region () const;
+
+  tree maybe_get_decl () const;
+
+#if 0
+  void set_value (region_model2 &model, region_id this_rid, svalue2_id rhs_sid,
+		  region_model2_context *ctxt);
+  svalue2_id get_value (region_model2 &model, bool non_null,
+		       region_model2_context *ctxt);
+  svalue2_id get_value_direct () const { return m_sval_id; }
+
+  svalue2_id get_inherited_child_sid (region *child,
+				     region_model2 &model,
+				     region_model2_context *ctxt);
+#endif
+
+  tree get_type () const { return m_type; }
+
+  hashval_t hash () const;
+
+  void print (const region_model2 &model,
+	      pretty_printer *pp) const;
+  label_text get_desc (bool simple=true) const;
+
+#if 0
+  virtual void dump_dot_to_pp (const region_model2 &model,
+			       pretty_printer *pp) const;
+#endif
+  void dump_to_pp (const region_model2 &model,
+		   pretty_printer *pp,
+		   const char *prefix,
+		   bool is_last_child) const;
+
+  virtual void dump_to_pp (pretty_printer *pp, bool simple) const = 0;
+  void dump (bool simple) const;
+
+#if 0
+  void add_view (region_id view_rid, region_model2 *model);
+  region_id get_view (tree type, region_model2 *model) const;
+  region_id get_active_view () const { return m_active_view_rid; }
+  bool is_view_p () const { return m_is_view; }
+#endif
+#if 0
+  virtual void validate (const region_model2 &model) const;
+#endif
+
+  bool non_null_p (const region_model2 &model) const;
+
+  static int cmp_ptrs (const void *, const void *);
+
+  region_offset get_offset () const;
+  bool get_byte_size (byte_size_t *out) const;
+  bool get_bit_size (bit_size_t *out) const;
+
+  void
+  get_subregions_for_binding (region_model2_manager *mgr,
+			      bit_offset_t start_bit_offset,
+			      bit_size_t size_in_bits,
+			      tree type,
+			      auto_vec <const region2 *> *out) const;
+
+  bool symbolic_for_unknown_ptr_p () const;
+
+  const complexity &get_complexity () const { return m_complexity; }
+
+ protected:
+  region2 (complexity c, unsigned id, const region2 *parent, tree type);
+
+  virtual void add_to_hash (inchash::hash &hstate) const;
+
+ private:
+#if 0
+  void become_active_view (region_model2 &model, region_id this_rid);
+  void deactivate_any_active_view (region_model2 &model);
+  void deactivate_view (region_model2 &model, region_id this_view_rid);
+#endif
+  region_offset calc_offset () const;
+
+  complexity m_complexity;
+  unsigned m_id; // purely for deterministic sorting at this stage, for dumps
+  const region2 *m_parent;
+  tree m_type;
+
+  mutable region_offset *m_cached_offset;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const region2 *>::test (const region2 *)
+{
+  return true;
+}
+
+namespace ana {
+
+/* Abstract subclass of region2, for regions that represent an untyped
+   space within memory, such as the stack or the heap.  */
+
+class space_region2 : public region2
+{
+protected:
+  space_region2 (unsigned id, const region2 *parent)
+  : region2 (complexity (parent), id, parent, NULL_TREE)
+  {}
+};
+
+/* Concrete space_region2 subclass, representing a function frame on the stack,
+   to contain the locals.
+   The parent is the stack region; there's also a hierarchy of call-stack
+   prefixes expressed via m_calling_frame.
+   For example, given "oldest" calling "middle" called "newest" we would have
+   - a stack depth of 3
+   - frame (A) for "oldest" with index 0 for depth 1, calling_frame == NULL
+   - frame (B) for "middle" with index 1 for depth 2, calling_frame == (A)
+   - frame (C) for "newest" with index 2 for depth 3, calling_frame == (B)
+   where the parent region for each of the frames is the "stack" region.
+   The index is the count of frames earlier than this in the stack.  */
+
+class frame_region2 : public space_region2
+{
+public:
+  frame_region2 (unsigned id, const region2 *parent,
+		 const frame_region2 *calling_frame,
+		 function *fun, int index)
+  : space_region2 (id, parent), m_calling_frame (calling_frame),
+    m_fun (fun), m_index (index)
+  {}
+  ~frame_region2 ();
+
+  /* region2 vfuncs.  */
+  enum kind get_kind () const FINAL OVERRIDE { return RK_FRAME; }
+  const frame_region2 * dyn_cast_frame_region2 () const FINAL OVERRIDE
+  {
+    return this;
+  }
+  void accept (visitor *v) const FINAL OVERRIDE;
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+#if 0
+  void add_to_hash (inchash::hash &hstate) const FINAL OVERRIDE;
+#endif
+
+  /* Accessors.  */
+  const frame_region2 *get_calling_frame () const { return m_calling_frame; }
+  function *get_function () const { return m_fun; }
+  int get_index () const { return m_index; }
+  int get_stack_depth () const { return m_index + 1; }
+
+  bool compare_fields (const frame_region2 &other) const;
+
+  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;
+  int m_index;
+  typedef hash_map<tree, region2 *> map_t;
+  map_t m_locals;
+  /* TODO: do we want a distinction between params (assumed initialized)
+     vs temporaries?  */
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const frame_region2 *>::test (const region2 *reg)
+{
+  return reg->get_kind () == region2::RK_FRAME;
+}
+
+namespace ana {
+
+/* Concrete space_region2 subclass, to hold global variables (data and bss).  */
+
+class globals_region2 : public space_region2
+{
+ public:
+  globals_region2 (unsigned id, const region2 *parent)
+  : space_region2 (id, parent)
+  {}
+
+  /* region2 vfuncs.  */
+  enum kind get_kind () const FINAL OVERRIDE { return RK_GLOBALS; }
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+
+  bool compare_fields (const globals_region2 &other) const;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const globals_region2 *>::test (const region2 *reg)
 {
-  extern void add_path_var (path_var pv, hash &hstate);
-} // namespace inchash
-
+  return reg->get_kind () == region2::RK_GLOBALS;
+}
 
 namespace ana {
 
-/* A class for renumbering IDs within a region_model, mapping old IDs
-   to new IDs (e.g. when removing one or more elements, thus needing to
-   renumber).  */
-// TODO: could this be useful for equiv_class_ids?
+/* Concrete region2 subclass.  A map_region2 representing the code, using
+   FUNCTION_DECLs for its keys.  */
 
-template <typename T>
-class id_map
+class code_region2 : public space_region2
 {
- public:
-  id_map (int num_ids);
-  void put (T src, T dst);
-  T get_dst_for_src (T src) const;
-  T get_src_for_dst (T dst) const;
-  void dump_to_pp (pretty_printer *pp) const;
-  void dump () const;
-  void update (T *) const;
+public:
+  code_region2 (unsigned id, const region2 *parent)
+  : space_region2 (id, parent)
+  {}
 
- private:
-  auto_vec<T> m_src_to_dst;
-  auto_vec<T> m_dst_to_src;
-};
+  /* region2 vfuncs.  */
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+  enum kind get_kind () const FINAL OVERRIDE { return RK_CODE; }
+
+  const region2 *get_element (region_model2 *model,
+			const svalue2 *index,
+			region_model2_context *ctxt);
 
-/* class id_map.  */
+  bool compare_fields (const code_region2 &other) const;
+};
 
-/* id_map's ctor, which populates the map with dummy null values.  */
+} // namespace ana
 
-template <typename T>
-inline id_map<T>::id_map (int num_svalues)
-: m_src_to_dst (num_svalues),
-  m_dst_to_src (num_svalues)
+template <>
+template <>
+inline bool
+is_a_helper <const code_region2 *>::test (const region2 *reg)
 {
-  for (int i = 0; i < num_svalues; i++)
-    {
-      m_src_to_dst.quick_push (T::null ());
-      m_dst_to_src.quick_push (T::null ());
-    }
+  return reg->get_kind () == region2::RK_CODE;
 }
 
-/* Record that SRC is to be mapped to DST.  */
+namespace ana {
+
+// FIXME:
+/* Concrete region2 subclass.  A region2 representing the code for
+   a particular function, using LABEL_DECLs for its keys.  */
 
-template <typename T>
-inline void
-id_map<T>::put (T src, T dst)
+class function_region2 : public region2
 {
-  m_src_to_dst[src.as_int ()] = dst;
-  m_dst_to_src[dst.as_int ()] = src;
-}
+public:
+  function_region2 (unsigned id, const code_region2 *parent, tree fndecl)
+  : region2 (complexity (parent), id, parent, TREE_TYPE (fndecl)),
+    m_fndecl (fndecl)
+  {
+    gcc_assert (FUNC_OR_METHOD_TYPE_P (TREE_TYPE (fndecl)));
+  }
+
+  /* region2 vfuncs.  */
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+  enum kind get_kind () const FINAL OVERRIDE { return RK_FUNCTION; }
+  const function_region2 *
+  dyn_cast_function_region2 () const FINAL OVERRIDE{ return this; }
+
+  tree get_fndecl () const { return m_fndecl; }
+
+  region2 *get_element (region_model2 *model,
+			const svalue2 *index_sid,
+			region_model2_context *ctxt);
+
+  bool compare_fields (const function_region2 &other) const;
+
+private:
+  tree m_fndecl;
+};
 
-/* Get the new value for SRC within the map.  */
+} // namespace ana
 
-template <typename T>
-inline T
-id_map<T>::get_dst_for_src (T src) const
+template <>
+template <>
+inline bool
+is_a_helper <const function_region2 *>::test (const region2 *reg)
 {
-  if (src.null_p ())
-    return src;
-  return m_src_to_dst[src.as_int ()];
+  return reg->get_kind () == region2::RK_FUNCTION;
 }
 
-/* Given DST, a new value, determine which old value will be mapped to it
-   (the inverse of the map).  */
+namespace ana {
+
+/* Concrete region2 subclass.  A region2 representing a particular label
+   within a function.  */
+
+class label_region2 : public region2
+{
+public:
+  label_region2 (unsigned id, const function_region2 *parent, tree label)
+  : region2 (complexity (parent), id, parent, NULL_TREE), m_label (label)
+  {
+    gcc_assert (TREE_CODE (label) == LABEL_DECL);
+  }
+
+  /* region2 vfuncs.  */
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+  enum kind get_kind () const FINAL OVERRIDE { return RK_LABEL; }
+
+  //bool compare_fields (const function_region2 &other) const;
+
+private:
+  tree m_label;
+};
+
+} // namespace ana
 
-template <typename T>
-inline T
-id_map<T>::get_src_for_dst (T dst) const
+template <>
+template <>
+inline bool
+is_a_helper <const label_region2 *>::test (const region2 *reg)
 {
-  if (dst.null_p ())
-    return dst;
-  return m_dst_to_src[dst.as_int ()];
+  return reg->get_kind () == region2::RK_LABEL;
 }
 
-/* Dump this id_map to PP.  */
+namespace ana {
+
+/* Concrete region2 subclass representing a stack, containing all stack
+   frames, and implicitly providing a POISON_KIND_UNINIT value to all
+   child region2s by default.  */
 
-template <typename T>
-inline void
-id_map<T>::dump_to_pp (pretty_printer *pp) const
+class stack_region2 : public region2
 {
-  pp_string (pp, "src to dst: {");
-  unsigned i;
-  T *dst;
-  FOR_EACH_VEC_ELT (m_src_to_dst, i, dst)
-    {
-      if (i > 0)
-	pp_string (pp, ", ");
-      T src (T::from_int (i));
-      src.print (pp);
-      pp_string (pp, " -> ");
-      dst->print (pp);
-    }
-  pp_string (pp, "}");
-  pp_newline (pp);
+public:
+  stack_region2 (unsigned id, region2 *parent)
+  : region2 (complexity (parent), id, parent, NULL_TREE)
+  {}
 
-  pp_string (pp, "dst to src: {");
-  T *src;
-  FOR_EACH_VEC_ELT (m_dst_to_src, i, src)
-    {
-      if (i > 0)
-	pp_string (pp, ", ");
-      T dst (T::from_int (i));
-      dst.print (pp);
-      pp_string (pp, " <- ");
-      src->print (pp);
-    }
-  pp_string (pp, "}");
-  pp_newline (pp);
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+
+  bool compare_fields (const stack_region2 &other) const;
+
+  enum kind get_kind () const FINAL OVERRIDE { return RK_STACK; }
+
+#if 0
+  static bool can_merge_p (const stack_region2 *stack_region2_a,
+			   const stack_region2 *stack_region2_b,
+			   model_merger *merger);
+  const svalue2 *get_value_by_name (tree identifier,
+			      const region_model2 &model) const;
+#endif
+
+#if 0
+  void validate (const region_model2 &model) const FINAL OVERRIDE;
+#endif
+ private:
+#if 0
+  void add_to_hash (inchash::hash &hstate) const FINAL OVERRIDE;
+#endif
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const stack_region2 *>::test (const region2 *reg)
+{
+  return reg->get_kind () == region2::RK_STACK;
 }
 
-/* Dump this id_map to stderr.  */
+namespace ana {
+
+/* Concrete region2 subclass: a region2 within which region2s can be
+   dynamically allocated.  */
+
+class heap_region2 : public region2
+{
+public:
+  heap_region2 (unsigned id, region2 *parent)
+  : region2 (complexity (parent), id, parent, NULL_TREE)
+  {}
+
+#if 0
+  bool compare_fields (const heap_region2 &other) const;
+#endif
+
+  enum kind get_kind () const FINAL OVERRIDE { return RK_HEAP; }
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+#if 0
+  static bool can_merge_p (const heap_region2 *heap_a, region2_id heap_a_rid,
+			   const heap_region2 *heap_b, region2_id heap_b_rid,
+			   heap_region2 *merged_heap, region2_id merged_heap_rid,
+			   model_merger *merger);
+#endif
+};
+
+} // namespace ana
 
-template <typename T>
-DEBUG_FUNCTION inline void
-id_map<T>::dump () const
+template <>
+template <>
+inline bool
+is_a_helper <const heap_region2 *>::test (const region2 *reg)
 {
-  pretty_printer pp;
-  pp.buffer->stream = stderr;
-  dump_to_pp (&pp);
-  pp_flush (&pp);
+  return reg->get_kind () == region2::RK_HEAP;
 }
 
-/* Update *ID from the old value to its new value in this map.  */
+namespace ana {
+
+/* Concrete region2 subclass.  The root region2, containing all region2s
+   (either directly, or as descendents).
+   Unique within a region_model2.  */
+
+class root_region2 : public region2
+{
+public:
+  root_region2 (unsigned id);
+
+  bool compare_fields (const root_region2 &other) const;
+
+  enum kind get_kind () const FINAL OVERRIDE { return RK_ROOT; }
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+
+#if 0
+  static bool can_merge_p (const root_region2 *root_region2_a,
+			   const root_region2 *root_region2_b,
+			   root_region2 *merged_root_region2,
+			   model_merger *merger);
+#endif
 
-template <typename T>
-inline void
-id_map<T>::update (T *id) const
+  const svalue2 *get_value_by_name (tree identifier,
+			      const region_model2 &model) const;
+
+#if 0
+  void validate (const region_model2 &model) const FINAL OVERRIDE;
+#endif
+
+private:
+#if 0
+  void add_to_hash (inchash::hash &hstate) const FINAL OVERRIDE;
+#endif
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const root_region2 *>::test (const region2 *reg)
 {
-  *id = get_dst_for_src (*id);
+  return reg->get_kind () == region2::RK_ROOT;
 }
 
-/* Variant of the above, which only stores things in one direction.
-   (e.g. for merging, when the number of destination regions is not
-   the same of the src regions, and can grow).  */
+namespace ana {
+
+/* Concrete region2 subclass: a region2 to use when dereferencing an unknown
+   pointer.  */
 
-template <typename T>
-class one_way_id_map
+class symbolic_region2 : public region2
 {
- public:
-  one_way_id_map (int num_ids);
-  void put (T src, T dst);
-  T get_dst_for_src (T src) const;
-  void dump_to_pp (pretty_printer *pp) const;
-  void dump () const;
-  void update (T *) const;
+public:
+  symbolic_region2 (unsigned id, region2 *parent, const svalue2 *sval_ptr)
+  : region2 (complexity::from_pair (parent, sval_ptr), id, parent,
+	     TREE_TYPE (sval_ptr->get_type ())),
+    m_sval_ptr (sval_ptr)
+  {}
 
- private:
-  auto_vec<T> m_src_to_dst;
+  const symbolic_region2 *
+  dyn_cast_symbolic_region2 () const FINAL OVERRIDE { return this; }
+
+  bool compare_fields (const symbolic_region2 &other) const;
+
+  enum kind get_kind () const FINAL OVERRIDE { return region2::RK_SYMBOLIC; }
+  void accept (visitor *v) const FINAL OVERRIDE;
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+
+  const svalue2 *get_pointer () const { return m_sval_ptr; }
+
+private:
+  const svalue2 *m_sval_ptr;
 };
 
-/* class one_way_id_map.  */
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const symbolic_region2 *>::test (const region2 *reg)
+{
+  return reg->get_kind () == region2::RK_SYMBOLIC;
+}
+
+namespace ana {
+
+/* Concrete region2 subclass representing the memory occupied by a
+   variable (whether for a global or a local).  */
+
+class decl_region2 : public region2
+{
+public:
+  decl_region2 (unsigned id, const region2 *parent, tree decl)
+  : region2 (complexity (parent), id, parent, TREE_TYPE (decl)), m_decl (decl)
+  {}
+
+  bool compare_fields (const decl_region2 &other) const;
+
+  enum region2::kind get_kind () const FINAL OVERRIDE { return RK_DECL; }
+  const decl_region2 *
+  dyn_cast_decl_region2 () const FINAL OVERRIDE { return this; }
+
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+
+  tree get_decl () const { return m_decl; }
+  int get_stack_depth () const;
+
+protected:
+  void add_to_hash (inchash::hash &hstate) const OVERRIDE;
+
+private:
+  tree m_decl;
+};
 
-/* one_way_id_map's ctor, which populates the map with dummy null values.  */
+} // namespace ana
 
-template <typename T>
-inline one_way_id_map<T>::one_way_id_map (int num_svalues)
-: m_src_to_dst (num_svalues)
+template <>
+template <>
+inline bool
+is_a_helper <const decl_region2 *>::test (const region2 *reg)
 {
-  for (int i = 0; i < num_svalues; i++)
-    m_src_to_dst.quick_push (T::null ());
+  return reg->get_kind () == region2::RK_DECL;
 }
 
-/* Record that SRC is to be mapped to DST.  */
+namespace ana {
+
+/* Concrete region2 subclass representing the memory occupied by a
+   field within a struct or union.  */
+
+class field_region2 : public region2
+{
+public:
+  field_region2 (unsigned id, const region2 *parent, tree field)
+  : region2 (complexity (parent), id, parent, TREE_TYPE (field)),
+    m_field (field)
+  {}
+
+  bool compare_fields (const field_region2 &other) const;
+
+  enum region2::kind get_kind () const FINAL OVERRIDE { return RK_FIELD; }
 
-template <typename T>
-inline void
-one_way_id_map<T>::put (T src, T dst)
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+  const field_region2 *
+  dyn_cast_field_region2 () const FINAL OVERRIDE { return this; }
+
+  tree get_field () const { return m_field; }
+
+protected:
+  void add_to_hash (inchash::hash &hstate) const OVERRIDE;
+
+private:
+  tree m_field;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const field_region2 *>::test (const region2 *reg)
 {
-  m_src_to_dst[src.as_int ()] = dst;
+  return reg->get_kind () == region2::RK_FIELD;
 }
 
-/* Get the new value for SRC within the map.  */
+namespace ana {
+
+/* An element within an array.  */
+
+class element_region2 : public region2
+{
+public:
+  element_region2 (unsigned id, const region2 *parent, tree element_type,
+		   const svalue2 *index)
+  : region2 (complexity::from_pair (parent, index), id, parent, element_type),
+    m_index (index)
+  {}
+
+  bool compare_fields (const element_region2 &other) const;
+
+  enum region2::kind get_kind () const FINAL OVERRIDE { return RK_ELEMENT; }
+  const element_region2 *
+  dyn_cast_element_region2 () const FINAL OVERRIDE { return this; }
+
+  void accept (visitor *v) const FINAL OVERRIDE;
+
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
 
-template <typename T>
-inline T
-one_way_id_map<T>::get_dst_for_src (T src) const
+  const svalue2 *get_index () const { return m_index; }
+
+protected:
+  void add_to_hash (inchash::hash &hstate) const OVERRIDE;
+
+private:
+  const svalue2 *m_index;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const element_region2 *>::test (const region2 *reg)
 {
-  if (src.null_p ())
-    return src;
-  return m_src_to_dst[src.as_int ()];
+  return reg->get_kind () == region2::RK_ELEMENT;
 }
 
-/* Dump this map to PP.  */
+namespace ana {
+
+/* A byte-offset within another region, for handling pointer arithmetic
+   as a region.  */
 
-template <typename T>
-inline void
-one_way_id_map<T>::dump_to_pp (pretty_printer *pp) const
+class offset_region2 : public region2
 {
-  pp_string (pp, "src to dst: {");
-  unsigned i;
-  T *dst;
-  FOR_EACH_VEC_ELT (m_src_to_dst, i, dst)
-    {
-      if (i > 0)
-	pp_string (pp, ", ");
-      T src (T::from_int (i));
-      src.print (pp);
-      pp_string (pp, " -> ");
-      dst->print (pp);
-    }
-  pp_string (pp, "}");
-  pp_newline (pp);
+public:
+  offset_region2 (unsigned id, const region2 *parent, tree type,
+		  const svalue2 *byte_offset)
+  : region2 (complexity::from_pair (parent, byte_offset), id, parent, type),
+    m_byte_offset (byte_offset)
+  {}
+
+  bool compare_fields (const offset_region2 &other) const;
+
+  enum region2::kind get_kind () const FINAL OVERRIDE { return RK_OFFSET; }
+  const offset_region2 *
+  dyn_cast_offset_region2 () const FINAL OVERRIDE { return this; }
+
+  void accept (visitor *v) const FINAL OVERRIDE;
+
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+
+  const svalue2 *get_byte_offset () const { return m_byte_offset; }
+
+protected:
+  void add_to_hash (inchash::hash &hstate) const OVERRIDE;
+
+private:
+  const svalue2 *m_byte_offset;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const offset_region2 *>::test (const region2 *reg)
+{
+  return reg->get_kind () == region2::RK_OFFSET;
 }
 
-/* Dump this map to stderr.  */
+namespace ana {
+
+/* A region that views another region using a different type.  */
+
+class cast_region2 : public region2
+{
+public:
+  cast_region2 (unsigned id, const region2 *original_region, tree type)
+  : region2 (complexity (original_region), id,
+	     original_region->get_parent_region (), type),
+    m_original_region (original_region)
+  {}
+
+  bool compare_fields (const cast_region2 &other) const;
+
+  enum region2::kind get_kind () const FINAL OVERRIDE { return RK_CAST; }
+  const cast_region2 *
+  dyn_cast_cast_region2 () const FINAL OVERRIDE { return this; }
+  void accept (visitor *v) const FINAL OVERRIDE;
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+
+  /* FIXME: should this just be the parent region? */
+  const region2 *get_original_region () const { return m_original_region; }
+
+protected:
+  void add_to_hash (inchash::hash &hstate) const OVERRIDE;
 
-template <typename T>
-DEBUG_FUNCTION inline void
-one_way_id_map<T>::dump () const
+private:
+  const region2 *m_original_region;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const cast_region2 *>::test (const region2 *reg)
 {
-  pretty_printer pp;
-  pp.buffer->stream = stderr;
-  dump_to_pp (&pp);
-  pp_flush (&pp);
+  return reg->get_kind () == region2::RK_CAST;
 }
 
-/* Update *ID from the old value to its new value in this map.  */
+namespace ana {
+
+/* An untyped region dynamically allocated on the heap via "malloc"
+   or similar.  */
+// FIXME: ...but could be NULL
+
+class heap_allocated_region2 : public region2
+{
+public:
+  heap_allocated_region2 (unsigned id, const region2 *parent)
+  : region2 (complexity (parent), id, parent, NULL_TREE)
+  {}
+
+#if 0
+  bool compare_fields (const element_region2 &other) const;
+#endif
+  enum region2::kind get_kind () const FINAL OVERRIDE { return RK_HEAP_ALLOCATED; }
+
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+
+protected:
+#if 0
+  void add_to_hash (inchash::hash &hstate) const OVERRIDE;
+#endif
+};
+
+/* An untyped region dynamically allocated on the stack via "alloca".  */
+
+class alloca_region2 : public region2
+{
+public:
+  alloca_region2 (unsigned id, const frame_region2 *parent)
+  : region2 (complexity (parent), id, parent, NULL_TREE)
+  {}
+
+#if 0
+  bool compare_fields (const element_region2 &other) const;
+#endif
+  enum region2::kind get_kind () const FINAL OVERRIDE { return RK_ALLOCA; }
+
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+
+protected:
+#if 0
+  void add_to_hash (inchash::hash &hstate) const OVERRIDE;
+#endif
+};
+
+/* A region for a STRING_CST.  */
+
+class string_region2 : public region2
+{
+public:
+  string_region2 (unsigned id, const region2 *parent, tree string_cst)
+  : region2 (complexity (parent), id, parent, TREE_TYPE (string_cst)),
+    m_string_cst (string_cst)
+  {}
+
+  const string_region2 *
+  dyn_cast_string_region2 () const FINAL OVERRIDE { return this; }
+
+#if 0
+  bool compare_fields (const element_region2 &other) const;
+#endif
+  enum region2::kind get_kind () const FINAL OVERRIDE { return RK_STRING; }
+
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+
+  tree get_string_cst () const { return m_string_cst; }
+
+protected:
+#if 0
+  void add_to_hash (inchash::hash &hstate) const OVERRIDE;
+#endif
+private:
+  tree m_string_cst;
+};
+
+} // namespace ana
 
-template <typename T>
-inline void
-one_way_id_map<T>::update (T *id) const
+template <>
+template <>
+inline bool
+is_a_helper <const string_region2 *>::test (const region2 *reg)
 {
-  *id = get_dst_for_src (*id);
+  return reg->get_kind () == region2::RK_STRING;
 }
 
-/* Various operations delete information from a region_model.
+namespace ana {
 
-   This struct tracks how many of each kind of entity were purged (e.g.
-   for selftests, and for debugging).  */
+/* An unknown region, for handling unimplemented tree codes.  */
 
-struct purge_stats
+class unknown_region2 : public region2
 {
-  purge_stats ()
-  : m_num_svalues (0),
-    m_num_regions (0),
-    m_num_equiv_classes (0),
-    m_num_constraints (0),
-    m_num_client_items (0)
+public:
+  unknown_region2 (unsigned id, const region2 *parent, tree type)
+  : region2 (complexity (parent), id, parent, type)
   {}
 
-  int m_num_svalues;
-  int m_num_regions;
-  int m_num_equiv_classes;
-  int m_num_constraints;
-  int m_num_client_items;
+#if 0
+  bool compare_fields (const element_region2 &other) const;
+#endif
+  enum region2::kind get_kind () const FINAL OVERRIDE { return RK_UNKNOWN; }
+
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+
+protected:
+#if 0
+  void add_to_hash (inchash::hash &hstate) const OVERRIDE;
+#endif
+};
+
+/* A class responsible for owning and consolidating region2 and svalue2
+   instances.
+   region2 and svalue2 instances are immutable as far as clients are
+   concerned, so they are provided as "const" ptrs.  */
+
+class region_model2_manager
+{
+public:
+  region_model2_manager ();
+  ~region_model2_manager ();
+
+  /* svalue2 consolidation.  */
+  const svalue2 *get_or_create_constant_svalue2 (tree cst_expr);
+  const svalue2 *get_or_create_unknown_svalue2 (tree type);
+  const svalue2 *get_or_create_setjmp_svalue2 (const setjmp_record &r,
+					       tree type);
+  const svalue2 *get_or_create_poisoned_svalue2 (enum poison_kind kind,
+						 tree type);
+  const svalue2 *get_or_create_initial_value (const region2 *reg);
+  const svalue2 *get_ptr_svalue2 (tree ptr_type, const region2 *pointee);
+  const svalue2 *get_or_create_unaryop (tree type, enum tree_code op,
+					const svalue2 *arg);
+  const svalue2 *get_or_create_cast (tree type, const svalue2 *arg);
+  const svalue2 *get_or_create_binop (tree type,
+				      enum tree_code op,
+				      const svalue2 *arg0, const svalue2 *arg1);
+  const svalue2 *get_or_create_sub_svalue2 (tree type,
+					    const svalue2 *parent_svalue,
+					    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);
+  const svalue2 *get_or_create_compound_svalue2 (tree type,
+						 const binding_map &map);
+  const svalue2 *get_or_create_conjured_svalue2 (tree type, const gimple *stmt,
+						 const region2 *id_reg);
+
+  /* region2 consolidation.  */
+  const stack_region2 * get_stack_region () const { return &m_stack_region; }
+  const heap_region2 *get_heap_region () const { return &m_heap_region; }
+  const code_region2 *get_code_region () const { return &m_code_region; }
+  const globals_region2 *get_globals_region () const
+  {
+    return &m_globals_region;
+  }
+  const function_region2 *get_region_for_fndecl (tree fndecl);
+  const label_region2 *get_region_for_label (tree label);
+  const decl_region2 *get_region_for_global (tree expr);
+  const region2 *get_field_region (const region2 *parent, tree field);
+  const region2 *get_element_region (const region2 *parent,
+				     tree element_type,
+				     const svalue2 *index);
+  const region2 *get_offset_region (const region2 *parent,
+				    tree type,
+				    const svalue2 *byte_offset);
+  const region2 *get_cast_region (const region2 *original_region,
+				  tree type);
+  const frame_region2 *get_frame_region (const frame_region2 *calling_frame,
+					 function *fun);
+  const region2 *get_symbolic_region (const svalue2 *sval);
+  const string_region2 *get_region_for_string (tree string_cst);
+
+  const region2 *
+  get_region2_for_unexpected_tree_code (region_model2_context *ctxt,
+					tree t,
+					const dump_location_t &loc);
+
+  unsigned alloc_region_id () { return m_next_region_id++; }
+
+  store2_manager *get_store2_manager () { return &m_store2_mgr; }
+
+  /* Dynamically-allocated region2 instances.
+     FIXME: for now, the number of these within the analysis can grow
+     arbitrarily.  They are still owned by the manager.  */
+  const region2 *create_region_for_heap_alloc ();
+  const region2 *create_region_for_alloca (const frame_region2 *frame);
+
+  void log_stats (logger *logger, bool show_objs) const;
+
+private:
+  bool too_complex_p (const complexity &c) const;
+  bool reject_if_too_complex (svalue2 *sval);
+
+  const svalue2 *maybe_fold_unaryop (tree type, enum tree_code op,
+				     const svalue2 *arg);
+  const svalue2 *maybe_fold_binop (tree type, enum tree_code op,
+				   const svalue2 *arg0, const svalue2 *arg1);
+  const svalue2 *maybe_fold_sub_svalue2 (tree type,
+					 const svalue2 *parent_svalue,
+					 const region2 *subregion);
+
+  unsigned m_next_region_id;
+  root_region2 m_root_region;
+  stack_region2 m_stack_region;
+  heap_region2 m_heap_region;
+
+  /* svalue2 consolidation.  */
+  typedef hash_map<tree, constant_svalue2 *> constants_map_t;
+  constants_map_t m_constants_map;
+
+  typedef hash_map<tree, unknown_svalue2 *> unknowns_map_t;
+  unknowns_map_t m_unknowns_map;
+  const unknown_svalue2 *m_unknown_NULL;
+
+  typedef hash_map<poisoned_svalue2::key_t,
+		   poisoned_svalue2 *> poisoned_values_map_t;
+  poisoned_values_map_t m_poisoned_values_map;
+
+  typedef hash_map<setjmp_svalue2::key_t,
+		   setjmp_svalue2 *> setjmp_values_map_t;
+  setjmp_values_map_t m_setjmp_values_map;
+
+  typedef hash_map<const region2 *, initial_svalue2 *> initial_values_map_t;
+  initial_values_map_t m_initial_values_map;
+
+  typedef hash_map<const region2 *, region_svalue2 *> pointer_values_map_t;
+  pointer_values_map_t m_pointer_values_map;
+
+  typedef hash_map<unaryop_svalue2::key_t,
+		   unaryop_svalue2 *> unaryop_values_map_t;
+  unaryop_values_map_t m_unaryop_values_map;
+
+  typedef hash_map<binop_svalue2::key_t, binop_svalue2 *> binop_values_map_t;
+  binop_values_map_t m_binop_values_map;
+
+  typedef hash_map<sub_svalue2::key_t, sub_svalue2 *> sub_values_map_t;
+  sub_values_map_t m_sub_values_map;
+
+  typedef hash_map<const svalue2 *,
+		   unmergeable_svalue2 *> unmergeable_values_map_t;
+  unmergeable_values_map_t m_unmergeable_values_map;
+
+  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;
+
+  typedef hash_map<compound_svalue2::key_t,
+		   compound_svalue2 *> compound_values_map_t;
+  compound_values_map_t m_compound_values_map;
+
+  typedef hash_map<conjured_svalue2::key_t,
+		   conjured_svalue2 *> conjured_values_map_t;
+  conjured_values_map_t m_conjured_values_map;
+
+  /* Maximum complexity of svalues that weren't rejected.  */
+  complexity m_max_complexity;
+
+  /* region2 consolidation.  */
+
+  code_region2 m_code_region;
+  typedef hash_map<tree, function_region2 *> fndecls_map_t;
+  typedef fndecls_map_t::iterator fndecls_iterator_t;
+  fndecls_map_t m_fndecls_map;
+
+  typedef hash_map<tree, label_region2 *> labels_map_t;
+  typedef labels_map_t::iterator labels_iterator_t;
+  labels_map_t m_labels_map;
+
+  globals_region2 m_globals_region;
+  typedef hash_map<tree, decl_region2 *> globals_map_t;
+  typedef globals_map_t::iterator globals_iterator_t;
+  globals_map_t m_globals_map;
+
+  uniq_manager<region2> m_fields;
+#if 0
+  struct field_key_t { region2 *m_parent; tree m_field; };
+  typedef hash_map<field_key_t, region2 *> fields_map_t;
+  fields_map_t m_fields_map;
+#endif
+  uniq_manager<region2> m_elements;
+  uniq_manager<region2> m_offset_regions;
+  uniq_manager<region2> m_cast_regions;
+  uniq_manager<frame_region2> m_frames;
+  uniq_manager<region2> m_symbolic_regions;
+
+  typedef hash_map<tree, string_region2 *> string_map_t;
+  string_map_t m_string_map;
+
+  store2_manager m_store2_mgr;
+
+  /* "Dynamically-allocated" region2 instances.
+     FIXME: for now, the number of these within the analysis can grow
+     arbitrarily.  They are still owned by the manager.  */
+  auto_delete_vec<region2> m_managed_dynamic_regions;
+};
+
+struct append_ssa_names_cb_data;
+
+/* Helper class for handling calls to functions with known behavior.
+   Implemented in region-model2-impl-calls.c.  */
+
+class call_details
+{
+public:
+  call_details (const gcall *call, region_model2 *model,
+		region_model2_context *ctxt);
+
+  region_model2_context *get_ctxt () const { return m_ctxt; }
+  tree get_lhs_type () const { return m_lhs_type; }
+  const region2 *get_lhs_region () const { return m_lhs_region; }
+
+  bool maybe_set_lhs (const svalue2 *result) const;
+
+  tree get_arg_tree (unsigned idx) const;
+  const svalue2 *get_arg_svalue2 (unsigned idx) const;
+
+  void dump_to_pp (pretty_printer *pp, bool simple) const;
+  void dump (bool simple) const;
+
+private:
+  const gcall *m_call;
+  region_model2 *m_model;
+  region_model2_context *m_ctxt;
+  tree m_lhs_type;
+  const region2 *m_lhs_region;
+};
+
+/* A region_model2 encapsulates a representation of the state of memory, with
+   a tree of region2s, along with their associated values.
+   The representation is graph-like because values can be pointers to
+   region2s.
+   It also stores a constraint_manager, capturing relationships between
+   the values.  */
+
+class region_model2
+{
+ public:
+  region_model2 (region_model2_manager *mgr);
+  region_model2 (const region_model2 &other);
+  ~region_model2 ();
+
+#if 0//__cplusplus >= 201103
+  region_model2 (region_model2 &&other);
+#endif
+
+  region_model2 &operator= (const region_model2 &other);
+
+  bool operator== (const region_model2 &other) const;
+  bool operator!= (const region_model2 &other) const
+  {
+    return !(*this == other);
+  }
+
+  hashval_t hash () const;
+
+  void print (pretty_printer *pp) const;
+
+  void dump_dot_to_pp (pretty_printer *pp) const;
+  void dump_dot_to_file (FILE *fp) const;
+  void dump_dot (const char *path) const;
+
+  void dump_to_pp (pretty_printer *pp, bool simple, bool multiline) const;
+  void dump (FILE *fp, bool simple, bool multiline) const;
+  void dump (bool simple) const;
+  void summarize_to_pp (pretty_printer *pp, bool simple) const;
+  void summarize (bool simple) const;
+
+  void debug () const;
+
+  void validate () const;
+
+  bool on_stmt (const gimple *stmt,
+		region_model2_context *ctxt);
+  bool on_gassign (const gassign *stmt,
+		   region_model2_context *ctxt);
+  const svalue2 * get_gassign_result (const gassign *assign,
+				      region_model2_context *ctxt);
+#if 0
+  bool on_gcall (const supernode *snode,
+		 const gcall *stmt);
+#else
+  bool on_call_pre (const gcall *stmt, region_model2_context *ctxt);
+  void on_call_post (const gcall *stmt,
+		     bool unknown_side_effects,
+		     region_model2_context *ctxt);
+
+  /* Specific handling for on_call_pre.  */
+  bool impl_call_alloca (const call_details &cd);
+  void impl_call_analyzer_describe (const gcall *call,
+				    region_model2_context *ctxt);
+  void impl_call_analyzer_eval (const gcall *call,
+				region_model2_context *ctxt);
+  bool impl_call_builtin_expect (const call_details &cd);
+  bool impl_call_calloc (const call_details &cd);
+  void impl_call_free (const call_details &cd);
+  bool impl_call_malloc (const call_details &cd);
+  bool impl_call_memset (const call_details &cd);
+  bool impl_call_strlen (const call_details &cd);
+#endif
+  void handle_unrecognized_call (const gcall *call,
+				 region_model2_context *ctxt);
+  void get_reachable_svalues (svalue2_set *out,
+			      const svalue2 *extra_sval);
+  bool on_greturn (const greturn *stmt,
+		   region_model2_context *ctxt);
+  void on_setjmp (const gcall *stmt, const exploded_node *enode,
+		  region_model2_context *ctxt);
+  void on_longjmp (const gcall *longjmp_call, const gcall *setjmp_call,
+		   int setjmp_stack_depth, region_model2_context *ctxt);
+
+  void update_for_phis (const supernode *snode,
+			const cfg_superedge *last_cfg_superedge,
+			region_model2_context *ctxt);
+
+  void handle_phi (const gphi *phi,
+		   tree lhs, tree rhs, bool is_back_edge,
+		   region_model2_context *ctxt);
+
+  bool maybe_update_for_edge (const superedge &edge,
+			      const gimple *last_stmt,
+			      region_model2_context *ctxt);
+
+  const region2 *push_frame (function *fun, vec<const svalue2 *> *arg_sids,
+			     region_model2_context *ctxt);
+  const frame_region2 *get_current_frame () const { return m_current_frame; }
+  function * get_current_function () const;
+  void pop_frame (const region2 *result_dst,
+		  bool purge, purge_stats2 *stats,
+		  const svalue2 **out_result,
+		  region_model2_context *ctxt);
+  int get_stack_depth () const;
+  const frame_region2 *get_frame_at_index (int index) const;
+
+#if 0
+  void replace_svalue2 (svalue2_id sid, const svalue2 *new_sval);
+#endif
+
+  const region2 *get_lvalue (path_var pv, region_model2_context *ctxt);
+  const region2 *get_lvalue (tree expr, region_model2_context *ctxt);
+  const svalue2 *get_rvalue (path_var pv, region_model2_context *ctxt);
+  const svalue2 *get_rvalue (tree expr, region_model2_context *ctxt);
+
+#if 0
+  svalue2_id maybe_cast (tree type, svalue2_id sid, region_model2_context *ctxt);
+  svalue2_id maybe_cast_1 (tree type, svalue2_id sid);
+
+  region2_id get_field_region2 (region2_id rid, tree field,
+			      region_model2_context *ctxt);
+#endif
+
+  const region2 *deref_rvalue (const svalue2 *ptr_sval, tree ptr_tree,
+			       region_model2_context *ctxt);
+#if 0
+  const region2 *deref_rvalue (tree ptr, region_model2_context *ctxt);
+#endif
+
+  void set_value (const region2 *lhs_reg, const svalue2 *rhs_sval,
+		  region_model2_context *ctxt);
+  void set_value (tree lhs, tree rhs, region_model2_context *ctxt);
+  void clobber_region (const region2 *reg);
+  void purge_region (const region2 *reg);
+  void zero_fill_region (const region2 *reg);
+  void mark_region_as_unknown (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,
+			   enum tree_code op,
+			   const svalue2 *rhs) const;
+  tristate eval_condition_without_cm (const svalue2 *lhs,
+				      enum tree_code op,
+				      const svalue2 *rhs) const;
+  tristate compare_initial_and_pointer (const initial_svalue2 *init,
+					const region_svalue2 *ptr) const;
+  tristate eval_condition (tree lhs,
+			   enum tree_code op,
+			   tree rhs,
+			   region_model2_context *ctxt);
+  bool add_constraint (tree lhs, enum tree_code op, tree rhs,
+		       region_model2_context *ctxt);
+#if 0
+  region2_id add_new_malloc_region2 ();
+#endif
+  const region2 *create_region_for_heap_alloc (const svalue2 *size_in_bytes);
+  const region2 *create_region_for_alloca (const svalue2 *size_in_bytes);
+
+  tree get_representative_tree (const svalue2 *sval) const;
+#if 0
+  void get_path_vars_for_svalue2 (const svalue2 *sval,
+				  auto_vec<path_var> *out) const;
+#endif
+  path_var
+  get_representative_path_var (const svalue2 *sval,
+			       hash_set <const svalue2 *> *visited) const;
+  path_var
+  get_representative_path_var (const region2 *reg,
+			       hash_set <const svalue2 *> *visited) const;
+
+#if 0
+  void purge_unused_svalue2s (purge_stats *out,
+			     region_model2_context *ctxt,
+			     svalue2_id_set *known_used_sids = NULL);
+  void purge_region2s (const region2_id_set &set,
+		      purge_stats *stats,
+		      logger *logger);
+#endif
+  /* For selftests.  */
+  constraint_manager2 *get_constraints2 ()
+  {
+    return m_constraints2;
+  }
+
+  store2 *get_store () { return &m_store; }
+  const store2 *get_store () const { return &m_store; }
+
+  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;
+#endif
+
+  void unbind_region2_and_descendents (const region2 *reg,
+				       enum poison_kind pkind,
+				       purge_stats2 *stats,
+				       logger *logger);
+
+  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;
+
+  svalue2_id convert_byte_offset_to_array_index (tree ptr_type,
+						svalue2_id offset_sid);
+
+  region2_id get_or_create_mem_ref (tree type,
+				   svalue2_id ptr_sid,
+				   svalue2_id offset_sid,
+				   region_model2_context *ctxt);
+  region2_id get_or_create_pointer_plus_expr (tree type,
+					     svalue2_id ptr_sid,
+					     svalue2_id offset_sid,
+					     region_model2_context *ctxt);
+  region2_id get_or_create_view (region2_id raw_rid, tree type,
+				region_model2_context *ctxt);
+#endif
+  tree get_fndecl_for_call (const gcall *call,
+			    region_model2_context *ctxt);
+
+  void get_ssa_name_regions_for_current_frame
+    (auto_vec<const decl_region2 *> *out) const;
+  static void append_ssa_names_cb (const region2 *base_reg,
+				   struct append_ssa_names_cb_data *data);
+
+  const svalue2 *get_store_value (const region2 *reg) const;
+
+  bool region_exists_p (const region2 *reg) const;
+
+  void canonicalize ();
+  bool canonicalized_p () const;
+
+  void loop_replay_hack (const region_model2 *dst_state);
+
+private:
+  const region2 *get_lvalue_1 (path_var pv,
+			       region_model2_context *ctxt);
+  const svalue2 *get_rvalue_1 (path_var pv,
+			       region_model2_context *ctxt);
+
+#if 0
+  void copy_struct_region2 (region2_id dst_rid, struct_region2 *dst_reg,
+			   struct_region2 *src_reg, region_model2_context *ctxt);
+  void copy_union_region2 (region2_id dst_rid, union_region2 *src_reg,
+			  region_model2_context *ctxt);
+  void copy_array_region2 (region2_id dst_rid, array_region2 *dst_reg,
+			  array_region2 *src_reg, region_model2_context *ctxt);
+#endif
+  void add_any_constraints_from_ssa_def_stmt (tree lhs,
+					      enum tree_code op,
+					      tree rhs,
+					      region_model2_context *ctxt);
+  void add_any_constraints_from_gassign (enum tree_code op,
+					 tree rhs,
+					 const gassign *assign,
+					 region_model2_context *ctxt);
+  void add_any_constraints_from_gcall (enum tree_code op,
+				       tree rhs,
+				       const gcall *call,
+				       region_model2_context *ctxt);
+
+  void update_for_call_superedge (const call_superedge &call_edge,
+				  region_model2_context *ctxt);
+  void update_for_return_superedge (const return_superedge &return_edge,
+				    region_model2_context *ctxt);
+  void update_for_call_summary (const callgraph_superedge &cg_sedge,
+				region_model2_context *ctxt);
+  bool apply_constraints_for_gcond (const cfg_superedge &edge,
+				    const gcond *cond_stmt,
+				    region_model2_context *ctxt);
+  bool apply_constraints_for_gswitch (const switch_cfg_superedge &edge,
+				      const gswitch *switch_stmt,
+				      region_model2_context *ctxt);
+
+  int poison_any_pointers_to_descendents (const region2 *reg,
+					  enum poison_kind pkind);
+
+#if 0
+  void dump_summary_of_rep_path_vars (pretty_printer *pp,
+				      auto_vec<path_var> *rep_path_vars,
+				      bool *is_first);
+#endif
+  void on_top_level_param (tree param, region_model2_context *ctxt);
+
+  void record_dynamic_extents (const region2 *reg,
+			       const svalue2 *size_in_bytes);
+
+  /* Storing this here to avoid passing it around everywhere.  */
+  region_model2_manager *const m_mgr;
+
+  store2 m_store;
+
+  constraint_manager2 *m_constraints2; // TODO: embed, rather than dynalloc?
+
+  const frame_region2 *m_current_frame;
+};
+
+/* Some region_model2 activity could lead to warnings (e.g. attempts to use an
+   uninitialized value).  This abstract base class encapsulates an interface
+   for the region2 model to use when emitting such warnings.
+
+   It also provides an interface for being notified about svalue2_ids being
+   remapped, and being deleted.
+
+   Having this as an abstract base class allows us to support the various
+   operations needed by program_state in the analyzer within region_model2,
+   whilst keeping them somewhat modularized.  */
+
+class region_model2_context
+{
+ public:
+  virtual void warn (pending_diagnostic *d) = 0;
+
+#if 0
+  /* Return true if if's OK to purge SID when simplifying state.
+     Subclasses can return false for values that have sm state,
+     to avoid generating "leak" false positives.  */
+  virtual bool can_purge_p (svalue2_id sid) = 0;
+#endif
+
+  // FIXME:
+  /* Hook for clients to be notified when a range of SIDs have
+     been purged, so that they can purge state relating to those
+     values (and potentially emit warnings about leaks).
+     All SIDs from FIRST_PURGED_SID numerically upwards are being
+     purged.
+     The return values is a count of how many items of data the client
+     has purged (potentially for use in selftests).
+     MAP has already been applied to the IDs, but is provided in case
+     the client needs to figure out the old IDs.  */
+  virtual void on_svalue2_leak (const svalue2 *sval) = 0;
+
+  /* Hook for clients to be notified when the set of explicitly live
+     svalue2s changes, so that they can purge state relating to dead
+     svalues.  */
+  virtual void
+  on_liveness_change (const svalue2_set &live_svalues,
+		      const region_model2 *model) = 0;
+
+  virtual logger *get_logger () = 0;
+
+#if 0
+  /* Hook for clients to be notified when CHILD_SID is created
+     from PARENT_SID, when "inheriting" a value for a region2 from a
+     parent region2.
+     This exists so that state machines that inherit state can
+     propagate the state from parent to child.  */
+  virtual void on_inherited_svalue2 (svalue2_id parent_sid,
+				    svalue2_id child_sid) = 0;
+
+  /* Hook for clients to be notified when DST_SID is created
+     (or reused) as a cast from SRC_SID.
+     This exists so that state machines can propagate the state
+     from SRC_SID to DST_SID.  */
+  virtual void on_cast (svalue2_id src_sid,
+			svalue2_id dst_sid) = 0;
+#endif
+  /* Hook for clients to be notified when the condition
+     "LHS OP RHS" is added to the region2 model.
+     This exists so that state machines can detect tests on edges,
+     and use them to trigger sm-state transitions (e.g. transitions due
+     to ptrs becoming known to be NULL or non-NULL, rather than just
+     "unchecked") */
+  virtual void on_condition (tree lhs, enum tree_code op, tree rhs) = 0;
+
+  // FIXME:
+  /* Hooks for clients to be notified when an unknown change happens
+     to BASE_REG (in response to a call to an unknown function).  */
+  virtual void on_unknown_change (const svalue2 *sval, bool is_mutable) = 0;
+
+  /* Hooks for clients to be notified when a phi node is handled,
+     where RHS is the pertinent argument.  */
+  virtual void on_phi (const gphi *phi, tree rhs) = 0;
+
+  /* Hooks for clients to be notified when the region2 model doesn't
+     know how to handle the tree code of T at LOC.  */
+  virtual void on_unexpected_tree_code (tree t,
+					const dump_location_t &loc) = 0;
+};
+
+/* A "do nothing" subclass of region_model2_context.  */
+
+class noop_region_model2_context : public region_model2_context
+{
+public:
+  void warn (pending_diagnostic *) OVERRIDE {}
+  void on_svalue2_leak (const svalue2 *) OVERRIDE {}
+  void on_liveness_change (const svalue2_set &,
+			   const region_model2 *) OVERRIDE {}
+  logger *get_logger () OVERRIDE { return NULL; }
+#if 0
+  void on_inherited_svalue2 (svalue2_id parent_sid ATTRIBUTE_UNUSED,
+			    svalue2_id child_sid  ATTRIBUTE_UNUSED)
+    OVERRIDE
+  {
+  }
+  void on_cast (svalue2_id src_sid ATTRIBUTE_UNUSED,
+		svalue2_id dst_sid ATTRIBUTE_UNUSED) OVERRIDE
+  {
+  }
+#endif
+  void on_condition (tree lhs ATTRIBUTE_UNUSED,
+		     enum tree_code op ATTRIBUTE_UNUSED,
+		     tree rhs ATTRIBUTE_UNUSED) OVERRIDE
+  {
+  }
+  void on_unknown_change (const svalue2 *sval ATTRIBUTE_UNUSED,
+			  bool is_mutable ATTRIBUTE_UNUSED) OVERRIDE
+  {
+  }
+  void on_phi (const gphi *phi ATTRIBUTE_UNUSED,
+	       tree rhs ATTRIBUTE_UNUSED) OVERRIDE
+  {
+  }
+  void on_unexpected_tree_code (tree, const dump_location_t &) OVERRIDE {}
+};
+
+/* A subclass of region_model2_context for determining if operations fail
+   e.g. "can we generate a region2 for the lvalue of EXPR?".  */
+
+class tentative_region_model2_context : public noop_region_model2_context
+{
+public:
+  tentative_region_model2_context () : m_num_unexpected_codes (0) {}
+
+  void on_unexpected_tree_code (tree, const dump_location_t &)
+    FINAL OVERRIDE
+  {
+    m_num_unexpected_codes++;
+  }
+
+  bool had_errors_p () const { return m_num_unexpected_codes > 0; }
+
+private:
+  int m_num_unexpected_codes;
+};
+
+/* A bundle of data for use when attempting to merge two region_model2
+   instances to make a third.  */
+
+struct model2_merger
+{
+  model2_merger (const region_model2 *model_a,
+		 const region_model2 *model_b,
+		 const program_point &point,
+		 region_model2 *merged_model)
+  : m_model_a (model_a), m_model_b (model_b),
+    m_point (point),
+    m_merged_model (merged_model)
+  {
+  }
+
+  void dump_to_pp (pretty_printer *pp, bool simple) const;
+  void dump (FILE *fp, bool simple) const;
+  void dump (bool simple) const;
+
+#if ITERATED_BINOPS
+  void add_iterating_widening_binop_thing (const binop_svalue2 *binop_sval);
+#endif
+  region_model2_manager *get_manager () const
+  {
+    return m_model_a->get_manager ();
+  }
+
+#if 0
+  template <typename Subclass>
+  Subclass *get_region2_a (region2_id rid_a) const
+  {
+    return m_model_a->get_region2 <Subclass> (rid_a);
+  }
+
+  template <typename Subclass>
+  Subclass *get_region2_b (region2_id rid_b) const
+  {
+    return m_model_b->get_region2 <Subclass> (rid_b);
+  }
+
+  bool can_merge_values_p (svalue2_id sid_a,
+			   svalue2_id sid_b,
+			   svalue2_id *merged_sid);
+
+  void record_region2s (region2_id a_rid,
+		       region2_id b_rid,
+		       region2_id merged_rid);
+
+  void record_svalue2s (svalue2_id a_sid,
+		       svalue2_id b_sid,
+		       svalue2_id merged_sid);
+#endif
+
+  const region_model2 *m_model_a;
+  const region_model2 *m_model_b;
+  const program_point &m_point;
+  region_model2 *m_merged_model;
+#if ITERATED_BINOPS
+  /* FIXME: comment for this.  */
+  auto_vec<const binop_svalue2 *> m_iterated_binops;
+#endif
+};
+
+/* FIXME.  */
+
+class engine2
+{
+public:
+  region_model2_manager *get_model_manager () { return &m_mgr; }
+
+  void log_stats (logger *logger) const;
+
+private:
+  region_model2_manager m_mgr;
+
 };
 
 } // namespace ana
 
+extern void debug (const region_model2 &rmodel);
+
 namespace ana {
 
 #if CHECKING_P
@@ -290,6 +3055,31 @@ namespace selftest {
 
 using namespace ::selftest;
 
+/* An implementation of region_model2_context for use in selftests, which
+   stores any pending_diagnostic instances passed to it.  */
+
+class test_region_model2_context : public noop_region_model2_context
+{
+public:
+  void warn (pending_diagnostic *d) FINAL OVERRIDE
+  {
+    m_diagnostics.safe_push (d);
+  }
+
+  unsigned get_num_diagnostics () const { return m_diagnostics.length (); }
+
+  void on_unexpected_tree_code (tree t, const dump_location_t &)
+    FINAL OVERRIDE
+  {
+    internal_error ("unhandled tree code: %qs",
+		    get_tree_code_name (TREE_CODE (t)));
+  }
+
+private:
+  /* Implicitly delete any diagnostics in the dtor.  */
+  auto_delete_vec<pending_diagnostic> m_diagnostics;
+};
+
 /* Attempt to add the constraint (LHS OP RHS) to MODEL.
    Verify that MODEL remains satisfiable.  */
 
@@ -308,31 +3098,43 @@ using namespace ::selftest;
     ASSERT_FALSE (sat);				\
   SELFTEST_END_STMT
 
-/* Assert that REGION_MODEL evaluates the condition "LHS OP RHS"
+/* Implementation detail of the ASSERT_CONDITION_*2 macros.  */
+
+void assert_condition2 (const location &loc,
+			region_model2 &model,
+			const svalue2 *lhs, tree_code op, const svalue2 *rhs,
+			tristate expected);
+
+void assert_condition2 (const location &loc,
+			region_model2 &model,
+			tree lhs, tree_code op, tree rhs,
+			tristate expected);
+
+/* Assert that REGION_MODEL2 evaluates the condition "LHS OP RHS"
    as "true".  */
 
-#define ASSERT_CONDITION_TRUE(REGION_MODEL, LHS, OP, RHS) \
+#define ASSERT_CONDITION_TRUE2(REGION_MODEL2, LHS, OP, RHS)		\
   SELFTEST_BEGIN_STMT							\
-  assert_condition (SELFTEST_LOCATION, REGION_MODEL, LHS, OP, RHS,	\
-		    tristate (tristate::TS_TRUE));		\
+  assert_condition2 (SELFTEST_LOCATION, REGION_MODEL2, LHS, OP, RHS,	\
+		     tristate (tristate::TS_TRUE));			\
   SELFTEST_END_STMT
 
-/* Assert that REGION_MODEL evaluates the condition "LHS OP RHS"
+/* Assert that REGION_MODEL2 evaluates the condition "LHS OP RHS"
    as "false".  */
 
-#define ASSERT_CONDITION_FALSE(REGION_MODEL, LHS, OP, RHS) \
+#define ASSERT_CONDITION_FALSE2(REGION_MODEL2, LHS, OP, RHS) \
   SELFTEST_BEGIN_STMT							\
-  assert_condition (SELFTEST_LOCATION, REGION_MODEL, LHS, OP, RHS,	\
-		    tristate (tristate::TS_FALSE));		\
+  assert_condition2 (SELFTEST_LOCATION, REGION_MODEL2, LHS, OP, RHS,	\
+		     tristate (tristate::TS_FALSE));			\
   SELFTEST_END_STMT
 
-/* Assert that REGION_MODEL evaluates the condition "LHS OP RHS"
+/* Assert that REGION_MODEL2 evaluates the condition "LHS OP RHS"
    as "unknown".  */
 
-#define ASSERT_CONDITION_UNKNOWN(REGION_MODEL, LHS, OP, RHS) \
+#define ASSERT_CONDITION_UNKNOWN2(REGION_MODEL2, LHS, OP, RHS) \
   SELFTEST_BEGIN_STMT							\
-  assert_condition (SELFTEST_LOCATION, REGION_MODEL, LHS, OP, RHS,	\
-		    tristate (tristate::TS_UNKNOWN));		\
+  assert_condition2 (SELFTEST_LOCATION, REGION_MODEL2, LHS, OP, RHS,	\
+		     tristate (tristate::TS_UNKNOWN));			\
   SELFTEST_END_STMT
 
 } /* end of namespace selftest.  */
@@ -341,4 +3143,4 @@ using namespace ::selftest;
 
 } // namespace ana
 
-#endif /* GCC_ANALYZER_REGION_MODEL_H */
+#endif /* GCC_ANALYZER_REGION_MODEL_2_H */
diff --git a/gcc/analyzer/region-model2-impl-calls.cc b/gcc/analyzer/region-model2-impl-calls.cc
index 252e9167b6f..3ef3dd21152 100644
--- a/gcc/analyzer/region-model2-impl-calls.cc
+++ b/gcc/analyzer/region-model2-impl-calls.cc
@@ -51,7 +51,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "digraph.h"
 #include "analyzer/supergraph.h"
 #include "sbitmap.h"
-#include "analyzer/region-model2.h"
+#include "analyzer/region-model.h"
 #include "gimple-pretty-print.h"
 
 #if ENABLE_ANALYZER
diff --git a/gcc/analyzer/region-model2-reachability.cc b/gcc/analyzer/region-model2-reachability.cc
index 337850f523b..6f6a7c4f9a5 100644
--- a/gcc/analyzer/region-model2-reachability.cc
+++ b/gcc/analyzer/region-model2-reachability.cc
@@ -53,8 +53,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "digraph.h"
 #include "analyzer/supergraph.h"
 #include "sbitmap.h"
-#include "analyzer/region-model2.h"
-#include "analyzer/constraint-manager2.h"
+#include "analyzer/region-model.h"
+#include "analyzer/constraint-manager.h"
 #include "diagnostic-event-id.h"
 #include "analyzer/sm.h"
 #include "diagnostic-event-id.h"
diff --git a/gcc/analyzer/region-model2.cc b/gcc/analyzer/region-model2.cc
deleted file mode 100644
index d6524ccce17..00000000000
--- a/gcc/analyzer/region-model2.cc
+++ /dev/null
@@ -1,11064 +0,0 @@
-/* Classes for modeling the state of memory.
-   Copyright (C) 2019-2020 Free Software Foundation, Inc.
-   Contributed by David Malcolm <dmalcolm@redhat.com>.
-
-This file is part of GCC.
-
-GCC is free software; you can redistribute it and/or modify it
-under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 3, or (at your option)
-any later version.
-
-GCC is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING3.  If not see
-<http://www.gnu.org/licenses/>.  */
-
-#include "config.h"
-#include "system.h"
-#include "coretypes.h"
-#include "tree.h"
-#include "gimple-pretty-print.h"
-#include "function.h"
-#include "basic-block.h"
-#include "gimple.h"
-#include "gimple-iterator.h"
-#include "diagnostic-core.h"
-#include "graphviz.h"
-#include "options.h"
-#include "cgraph.h"
-#include "tree-dfa.h"
-#include "stringpool.h"
-#include "convert.h"
-#include "target.h"
-#include "fold-const.h"
-#include "tree-pretty-print.h"
-#include "diagnostic-color.h"
-#include "diagnostic-metadata.h"
-#include "tristate.h"
-#include "bitmap.h"
-#include "selftest.h"
-#include "function.h"
-#include "analyzer/analyzer.h"
-#include "analyzer/analyzer-logging.h"
-#include "ordered-hash-map.h"
-#include "options.h"
-#include "cgraph.h"
-#include "cfg.h"
-#include "digraph.h"
-#include "analyzer/supergraph.h"
-#include "sbitmap.h"
-#include "analyzer/region-model2.h"
-#include "analyzer/constraint-manager2.h"
-#include "diagnostic-event-id.h"
-#include "analyzer/sm.h"
-#include "diagnostic-event-id.h"
-#include "analyzer/sm.h"
-#include "analyzer/pending-diagnostic.h"
-#include "analyzer/region-model2-reachability.h"
-#include "analyzer/analyzer-selftests.h"
-#include "stor-layout.h"
-#include "value-range.h" // for tree-ssanames.h
-#include "gimple-ssa.h" // for struct gimple_df in FOR_EACH_SSA_NAME
-#include "tree-ssanames.h" // for FOR_EACH_SSA_NAME
-
-#if ENABLE_ANALYZER
-
-namespace ana {
-
-/* Dump T to PP in language-independent form, for debugging/logging/dumping
-   purposes.  */
-
-static void
-dump_tree (pretty_printer *pp, tree t)
-{
-  dump_generic_node (pp, t, 0, TDF_SLIM, 0);
-}
-
-/* Equivalent to pp_printf (pp, "%qT", t), to avoid nesting pp_printf
-   calls within other pp_printf calls.
-
-   default_tree_printer handles 'T' and some other codes by calling
-     dump_generic_node (pp, t, 0, TDF_SLIM, 0);
-   dump_generic_node calls pp_printf in various places, leading to
-   garbled output.
-
-   Ideally pp_printf could be made to be reentrant, but in the meantime
-   this function provides a workaround.  */
-
-static void
-print_quoted_type (pretty_printer *pp, tree t)
-{
-  pp_begin_quote (pp, pp_show_color (pp));
-  dump_generic_node (pp, t, 0, TDF_SLIM, 0);
-  pp_end_quote (pp, pp_show_color (pp));
-}
-
-/* For use in printing a comma-separated list.  */
-
-static void
-dump_separator (pretty_printer *pp, bool *is_first)
-{
-  if (!*is_first)
-    pp_string (pp, ", ");
-  *is_first = false;
-}
-
-#if 0
-/* Concrete subclass of constraint_manager that wires it up to a region_model2
-   (whilst allowing the constraint_manager and region_model2 to be somewhat
-   at arms length).
-   TODO: revisit this; maybe put the region_model2 * into the constraint_manager
-   base class.  */
-
-class impl_constraint_manager : public constraint_manager
-{
- public:
-  impl_constraint_manager (region_model2 *model)
-  : constraint_manager (),
-    m_model (model)
-  {}
-
-  impl_constraint_manager (const impl_constraint_manager &other,
-			   region_model2 *model)
-  : constraint_manager (other),
-    m_model (model)
-  {}
-
-  constraint_manager *clone (region_model2 *model) const
-  {
-    return new impl_constraint_manager (*this, model);
-  }
-
-  tree maybe_get_constant (const svalue2 *sval) const FINAL OVERRIDE
-  {
-    const svalue2 *svalue2 = m_model->get_svalue2 (sid);
-    return svalue2->maybe_get_constant ();
-  }
-
-  svalue2_id get_sid_for_constant (tree cst) const FINAL OVERRIDE
-  {
-    gcc_assert (CONSTANT_CLASS_P (cst));
-    return m_model->get_rvalue (cst, NULL);
-  }
-
-  int get_num_svalue2s () const FINAL OVERRIDE
-  {
-    return m_model->get_num_svalue2s ();
-  }
-
- private:
-  region_model2 *m_model;
-};
-#endif
-
-/* struct complexity.  */
-
-/* Get complexity for a new node that references REG
-   (the complexity of REG, plus one for the new node).  */
-
-complexity::complexity (const region2 *reg)
-: m_num_nodes (reg->get_complexity ().m_num_nodes + 1),
-  m_max_depth (reg->get_complexity ().m_max_depth + 1)
-{
-}
-
-/* Get complexity for a new node that references SVAL.
-   (the complexity of SVAL, plus one for the new node).  */
-
-complexity::complexity (const svalue2 *sval)
-: m_num_nodes (sval->get_complexity ().m_num_nodes + 1),
-  m_max_depth (sval->get_complexity ().m_max_depth + 1)
-{
-}
-
-/* Get complexity for a new node that references nodes with complexity
-   C1 and C2.  */
-
-complexity
-complexity::from_pair (const complexity &c1, const complexity &c2)
-{
-  return complexity (c1.m_num_nodes + c2.m_num_nodes + 1,
-		     MAX (c1.m_max_depth, c2.m_max_depth) + 1);
-}
-
-/* class svalue2 and its various subclasses.  */
-
-/* class svalue2.  */
-
-/* Dump this svalue2 in the form of a .dot record to PP.  */
-
-#if 0
-void
-svalue2::dump_dot_to_pp (const region_model2 &model,
-			pretty_printer *pp) const
-{
-  this_sval.dump_node_name_to_pp (pp);
-  pp_printf (pp, " [label=\"");
-  pp_write_text_to_stream (pp);
-  this_sval.print (pp);
-  pp_string (pp, ": {");
-  print (model, this_sval, pp);
-  pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/false);
-  pp_string (pp, "}\"];");
-  pp_newline (pp);
-}
-#endif
-
-/* FIXME.  */
-
-DEBUG_FUNCTION void
-svalue2::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);
-  pp_newline (&pp);
-  pp_flush (&pp);
-}
-
-/* FIXME.  */
-
-label_text
-svalue2::get_desc (bool simple) const
-{
-  pretty_printer pp;
-  pp_format_decoder (&pp) = default_tree_printer;
-  dump_to_pp (&pp, simple);
-  return label_text::take (xstrdup (pp_formatted_text (&pp)));
-}
-
-/* If this svalue2 is a constant_svalue2, return the underlying tree constant.
-   Otherwise return NULL_TREE.  */
-
-tree
-svalue2::maybe_get_constant () const
-{
-  if (const constant_svalue2 *cst_sval = dyn_cast_constant_svalue2 ())
-    return cst_sval->get_constant ();
-  else
-    return NULL_TREE;
-}
-
-/* If this svalue2 is a cast (i.e a unaryop NOP_EXPR), return the underlying
-   svalue2.
-   Otherwise return NULL.  */
-
-const svalue2 *
-svalue2::maybe_undo_cast () const
-{
-  if (const unaryop_svalue2 *unaryop_sval = dyn_cast_unaryop_svalue2 ())
-    if (unaryop_sval->get_op () == NOP_EXPR)
-      return unaryop_sval->get_arg ();
-  return NULL;
-}
-
-const svalue2 *
-svalue2::unwrap_any_unmergeable () const
-{
-  if (const unmergeable_svalue2 *unmergeable = dyn_cast_unmergeable_svalue2 ())
-    return unmergeable->get_arg ();
-  return this;
-}
-
-/* Attempt to merge THIS with OTHER, returning the merged svalue2.
-   Return NULL if not mergeable.  */
-
-const svalue2 *
-svalue2::can_merge_p (const svalue2 *other,
-		      region_model2_manager *mgr,
-		      model2_merger *merger) const
-{
-  if (!(get_type () && other->get_type ()))
-    return NULL;
-
-  // FIXME: should we assert this instead?
-  if (!types_compatible_p (get_type (), other->get_type ()))
-    return NULL;
-
-  /* Reject attempts to merge unmergeable svalues.  */
-  if ((get_kind () == svalue2::SK_UNMERGEABLE)
-      || (other->get_kind () == svalue2::SK_UNMERGEABLE))
-    return NULL;
-
-  /* Reject attempts to merge NULL pointers with not-NULL-pointers.  */
-  if (POINTER_TYPE_P (get_type ()))
-    {
-      bool null0 = false;
-      bool null1 = false;
-      if (tree cst0 = maybe_get_constant ())
-	if (zerop (cst0))
-	  null0 = true;
-      if (tree cst1 = other->maybe_get_constant ())
-	if (zerop (cst1))
-	  null1 = true;
-      if (null0 != null1)
-	return NULL;
-    }
-
-#if WIDENING
-  /* Widening.  */
-  /* Merge: (new_cst, existing_cst) -> widen (existing, new).  */
-  if (tree cst0 = maybe_get_constant ())
-    if (tree cst1 = other->maybe_get_constant ())
-      {
-	return mgr->get_or_create_widening_svalue2 (other->get_type (),
-						    merger->m_point,
-						    other, this);
-      }
-  /* TODO: also detect widening symbolically via binary ops
-     e.g. for (i = start; i < end; i+= step).  */
-
-  /* Merge: (Widen(existing_val, V), existing_val) -> Widen (existing_val, V)
-     and thus get a fixed point.  */
-  if (const widening_svalue2 *widen_sval = dyn_cast_widening_svalue2 ())
-    {
-      /* FIXME: which direction??? */
-      if (other == widen_sval->get_base_svalue2 ())
-	return this;
-      if (other == widen_sval->get_iter_svalue2 ())
-	return this;
-    }
-
-  if (const binop_svalue2 *binop_sval = dyn_cast_binop_svalue2 ())
-    if (const widening_svalue2 *widen_arg0
-	= binop_sval->get_arg0 ()->dyn_cast_widening_svalue2 ())
-      {
-	if (other == binop_sval->get_arg1 ())
-	  {
-	    /* Merger of: (Widen(..., OTHER) BINOP X)
-	       and      : OTHER
-	       to       : (Widen(..., OTHER) BINOP X)
-	       e.g. merge of Widen(0, 1) + 1 with 1 to the Widen(0, 1) + 1.  */
-	    return this;
-	  }
-
-	/* Merger of : (Widen() BINOP X)
-	   and       : Widen()
-	   to        : Widen()
-	   e.g. merge of Widen(0, 1) + 1 and Widen(0, 1) to Widen(0, 1).
-	   However, we want to update constraints for this case, since we're
-	   considering another iteration.
-	   Presumably we also want to ensure that it converges; we don't want
-	   a descending chain of constraints.  */
-	if (other == widen_arg0)
-	  {
-#if ITERATED_BINOPS
-	    merger->add_iterating_widening_binop_thing (binop_sval);
-#endif
-	    return widen_arg0;
-	  }
-      }
-#endif
-
-  return mgr->get_or_create_unknown_svalue2 (get_type ());
-}
-
-/* FIXME.  */
-
-bool
-svalue2::live_p (const hash_set<const svalue2 *> &live_svalues,
-		 const region_model2 *model) const
-{
-  /* Determine if SVAL is explicitly live.  */
-  if (const_cast<hash_set<const svalue2 *> &> (live_svalues).contains (this))
-    return true;
-
-  /* Otherwise, determine if SVAL is implicitly live due to being made of
-     other live svalues.  */
-  return implicitly_live_p (live_svalues, model);
-}
-
-/* Base implementation of svalue2::implicitly_live_p.  */
-
-bool
-svalue2::implicitly_live_p (const svalue2_set &, const region_model2 *) const
-{
-  return false;
-}
-
-/* FIXME: group these here, or move them to their subclass groupings.  */
-
-/* Constants are implicitly live.  */
-
-bool
-constant_svalue2::implicitly_live_p (const svalue2_set &,
-				     const region_model2 *) const
-{
-  return true;
-}
-
-bool
-initial_svalue2::implicitly_live_p (const svalue2_set &live_svalues,
-				    const region_model2 *model) const
-{
-  /* This svalue2 may be implicitly live if the region still implicitly
-     has its initial value and is reachable.  */
-
-  /* It must be a region that exists; we don't want to consider
-     INIT_VAL(R) as still being implicitly reachable if R is in
-     a popped stack frame.  */
-  if (model->region_exists_p (m_reg))
-    {
-      const svalue2 *reg_sval = model->get_store_value (m_reg);
-      if (reg_sval == this)
-	return true;
-    }
-
-  return false;
-}
-
-bool
-unaryop_svalue2::implicitly_live_p (const svalue2_set &live_svalues,
-				    const region_model2 *model) const
-{
-  return get_arg ()->live_p (live_svalues, model);
-}
-
-bool
-binop_svalue2::implicitly_live_p (const svalue2_set &live_svalues,
-				  const region_model2 *model) const
-{
-  return (get_arg0 ()->live_p (live_svalues, model)
-	  && get_arg1 ()->live_p (live_svalues, model));
-}
-
-bool
-sub_svalue2::implicitly_live_p (const svalue2_set &live_svalues,
-				const region_model2 *model) const
-{
-  return get_parent ()->live_p (live_svalues, model);
-}
-
-bool
-unmergeable_svalue2::implicitly_live_p (const svalue2_set &live_svalues,
-					const region_model2 *model) const
-{
-  return get_arg ()->live_p (live_svalues, model);
-}
-
-/* class region_svalue2 : public svalue2.  */
-
-/* FIXME.  */
-
-void
-region_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  if (simple)
-    {
-      pp_string (pp, "&");
-      m_reg->dump_to_pp (pp, simple);
-    }
-  else
-    {
-      pp_string (pp, "region_svalue2(");
-      print_quoted_type (pp, get_type ());
-      pp_string (pp, ", ");
-      m_reg->dump_to_pp (pp, simple);
-      pp_string (pp, ")");
-    }
-}
-
-/* Compare the fields of this region_svalue2 with OTHER, returning true
-   if they are equal.
-   For use by svalue2::operator==.  */
-#if 0
-bool
-region_svalue2::compare_fields (const region_svalue2 &other) const
-{
-  return m_reg == other.m_reg;
-}
-#endif
-
-/* FIXME.  */
-
-void
-region_svalue2::accept (visitor *v) const
-{
-  v->visit_region_svalue2 (this);
-  m_reg->accept (v);
-}
-
-/* Implementation of svalue2::dump_dot_to_pp for region_svalue2.  */
-
-#if 0
-void
-region_svalue2::dump_dot_to_pp (const region_model2 &model,
-			       svalue2_id this_sval,
-			       pretty_printer *pp) const
-{
-  svalue2::dump_dot_to_pp (model, this_sval, pp);
-
-  /* If non-NULL, add an edge to the pointed-to region2.  */
-  if (!m_reg.null_p ())
-    {
-      this_sval.dump_node_name_to_pp (pp);
-      pp_string (pp, " -> ");
-      m_reg.dump_node_name_to_pp (pp);
-      pp_string (pp, ";");
-      pp_newline (pp);
-  }
-}
-#endif
-
-#if 0
-/* Merge REGION2_SVAL_A and REGION2_SVAL_B using MERGER, writing the result
-   into *MERGED_SVAL.  */
-
-void
-region_svalue2::merge_values (const region_svalue2 &region2_sval_a,
-			     const region_svalue2 &region2_sval_b,
-			     svalue2_id *merged_sval,
-			     tree type,
-			     model_merger *merger)
-{
-  region2_id a_reg = region2_sval_a.get_pointee ();
-  region2_id b_reg = region2_sval_b.get_pointee ();
-
-  /* Both are non-NULL. */
-  gcc_assert (!a_reg.null_p () && !b_reg.null_p ());
-
-  /* Have these ptr-values already been merged?  */
-
-  region2_id a_reg_in_m
-    = merger->m_map_region2s_from_a_to_m.get_dst_for_src (a_reg);
-  region2_id b_reg_in_m
-    = merger->m_map_region2s_from_b_to_m.get_dst_for_src (b_reg);
-
-  /* "null_p" here means "we haven't seen this ptr-value before".
-     If we've seen one but not the other, or we have different
-     region2s, then the merged ptr has to be "unknown".  */
-  if (a_reg_in_m != b_reg_in_m)
-    {
-      const svalue2 *merged_sval = new unknown_svalue2 (type);
-      *merged_sval = merger->m_merged_model->add_svalue2 (merged_sval);
-      return;
-    }
-
-  /* Have we seen this yet?  If so, reuse the value.  */
-  if (!a_reg_in_m.null_p ())
-    {
-      *merged_sval
-	= merger->m_merged_model->get_or_create_ptr_svalue2 (type, a_reg_in_m);
-      return;
-    }
-
-  /* Otherwise we have A/B region2s that haven't been referenced yet.  */
-
-  /* Are the region2s the "same", when seen from the tree point-of-view.
-     If so, create a merged pointer to it.  */
-  path_var pv_a = merger->m_model_a->get_representative_path_var (a_reg);
-  path_var pv_b = merger->m_model_b->get_representative_path_var (b_reg);
-  if (pv_a.m_tree
-      && pv_a == pv_b)
-    {
-      region2_id merged_pointee_reg
-	= merger->m_merged_model->get_lvalue (pv_a, NULL);
-      *merged_sval
-	= merger->m_merged_model->get_or_create_ptr_svalue2 (type,
-							    merged_pointee_reg);
-      merger->record_region2s (a_reg, b_reg, merged_pointee_reg);
-      return;
-    }
-
-  /* Handle an A/B pair of ptrs that both point at heap region2s.
-     If they both have a heap region2 in the merger model, merge them.  */
-  region2 *region2_a = merger->m_model_a->get_region2 (a_reg);
-  region2 *region2_b = merger->m_model_b->get_region2 (b_reg);
-  region2_id a_parent_reg = region2_a->get_parent ();
-  region2_id b_parent_reg = region2_b->get_parent ();
-  region2 *parent_region2_a = merger->m_model_a->get_region2 (a_parent_reg);
-  region2 *parent_region2_b = merger->m_model_b->get_region2 (b_parent_reg);
-  if (parent_region2_a
-      && parent_region2_b
-      && parent_region2_a->get_kind () == RK_HEAP
-      && parent_region2_b->get_kind () == RK_HEAP)
-    {
-	/* We have an A/B pair of ptrs that both point at heap region2s.  */
-	/* presumably we want to see if each A/B heap region2 already
-	   has a merged region2, and, if so, is it the same one.  */
-	// This check is above
-
-	region2_id merged_pointee_reg
-	  = merger->m_merged_model->add_new_malloc_region2 ();
-	*merged_sval
-	  = merger->m_merged_model->get_or_create_ptr_svalue2
-	     (type, merged_pointee_reg);
-	merger->record_region2s (a_reg, b_reg, merged_pointee_reg);
-	return;
-    }
-
-  /* Two different non-NULL pointers?  Merge to unknown.  */
-  const svalue2 *merged_sval = new unknown_svalue2 (type);
-  *merged_sval = merger->m_merged_model->add_svalue2 (merged_sval);
-  return;
-}
-#endif
-
-/* Evaluate the condition LHS OP RHS.
-   Subroutine of region_model2::eval_condition for when we have a pair of
-   pointers.  */
-
-tristate
-region_svalue2::eval_condition (const region_svalue2 *lhs,
-				enum tree_code op,
-				const region_svalue2 *rhs)
-{
-  /* See if they point to the same region2.  */
-  const region2 *lhs_reg = lhs->get_pointee ();
-  const region2 *rhs_reg = rhs->get_pointee ();
-  bool ptr_equality = lhs_reg == rhs_reg;
-  // FIXME: what about when we have unknown values?
-  /* TODO: what about child region2s where the child is the first child
-     (or descendent)?
-     Presumably we should look at base regions and compare offsets
-     within them.  */
-  switch (op)
-    {
-    default:
-      gcc_unreachable ();
-
-    case EQ_EXPR:
-      if (ptr_equality)
-	return tristate::TS_TRUE;
-      else
-	return tristate::TS_FALSE;
-      break;
-
-    case NE_EXPR:
-      if (ptr_equality)
-	return tristate::TS_FALSE;
-      else
-	return tristate::TS_TRUE;
-      break;
-
-    case GE_EXPR:
-    case LE_EXPR:
-      if (ptr_equality)
-	return tristate::TS_TRUE;
-      break;
-
-    case GT_EXPR:
-    case LT_EXPR:
-      if (ptr_equality)
-	return tristate::TS_FALSE;
-      break;
-    }
-
-  return tristate::TS_UNKNOWN;
-}
-
-/* class constant_svalue2 : public svalue2.  */
-
-/* FIXME.  */
-
-void
-constant_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  if (simple)
-    {
-      pp_string (pp, "(");
-      dump_tree (pp, get_type ());
-      pp_string (pp, ")");
-      dump_tree (pp, m_cst_expr);
-    }
-  else
-    {
-      pp_string (pp, "constant_svalue2(");
-      print_quoted_type (pp, get_type ());
-      pp_string (pp, ", ");
-      dump_tree (pp, m_cst_expr);
-      pp_string (pp, ")");
-    }
-}
-
-/* FIXME.  */
-
-void
-constant_svalue2::accept (visitor *v) const
-{
-  v->visit_constant_svalue2 (this);
-}
-
-/* Compare the fields of this constant_svalue2 with OTHER, returning true
-   if they are equal.
-   For use by svalue2::operator==.  */
-#if 0
-bool
-constant_svalue2::compare_fields (const constant_svalue2 &other) const
-{
-  return m_cst_expr == other.m_cst_expr;
-}
-#endif
-
-/* Merge the CST_SVAL_A and CST_SVAL_B using MERGER, writing the id of
-   the resulting svalue2 into *MERGED_SVAL.  */
-#if 0
-void
-constant_svalue2::merge_values (const constant_svalue2 &cst_sval_a,
-			       const constant_svalue2 &cst_sval_b,
-			       svalue2_id *merged_sval,
-			       model_merger *merger)
-{
-  tree cst_a = cst_sval_a.get_constant ();
-  tree cst_b = cst_sval_b.get_constant ();
-  const svalue2 *merged_sval;
-  if (cst_a == cst_b)
-    {
-      /* If they are the same constant, merge as that constant value.  */
-      merged_sval = new constant_svalue2 (cst_a);
-    }
-  else
-    {
-      /* Otherwise, we have two different constant values.
-	 Merge as an unknown value.
-	 TODO: impose constraints on the value?
-	 (maybe just based on A, to avoid infinite chains)  */
-      merged_sval = new unknown_svalue2 (TREE_TYPE (cst_a));
-    }
-  *merged_sval = merger->m_merged_model->add_svalue2 (merged_sval);
-}
-#endif
-
-/* Evaluate the condition LHS OP RHS.
-   Subroutine of region_model2::eval_condition for when we have a pair of
-   constants.  */
-
-tristate
-constant_svalue2::eval_condition (const constant_svalue2 *lhs,
-				  enum tree_code op,
-				  const constant_svalue2 *rhs)
-{
-  tree lhs_const = lhs->get_constant ();
-  tree rhs_const = rhs->get_constant ();
-
-  gcc_assert (CONSTANT_CLASS_P (lhs_const));
-  gcc_assert (CONSTANT_CLASS_P (rhs_const));
-
-  /* Check for comparable types.  */
-  if (types_compatible_p (TREE_TYPE (lhs_const), TREE_TYPE (rhs_const)))
-    {
-      tree comparison
-	= fold_binary (op, boolean_type_node, lhs_const, rhs_const);
-      if (comparison == boolean_true_node)
-	return tristate (tristate::TS_TRUE);
-      if (comparison == boolean_false_node)
-	return tristate (tristate::TS_FALSE);
-    }
-  return tristate::TS_UNKNOWN;
-}
-
-/* Implementation of svalue2::get_child_sval vfunc for constant_svalue2.  */
-#if 0
-svalue2_id
-constant_svalue2::get_child_sval (region2 *parent ATTRIBUTE_UNUSED,
-				region2 *child,
-				region_model2 &model,
-				region_model2_context *ctxt ATTRIBUTE_UNUSED)
-{
-  /* TODO: handle the all-zeroes case by returning an all-zeroes of the
-     child type.  */
-
-  /* Otherwise, we don't have a good way to get a child value out of a
-     constant.
-
-     Handle this case by using an unknown value.  */
-  const svalue2 *unknown_sval = new unknown_svalue2 (child->get_type ());
-  return model.add_svalue2 (unknown_sval);
-}
-#endif
-
-/* class unknown_svalue2 : public svalue2.  */
-
-/* FIXME.  */
-
-void
-unknown_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  if (simple)
-    {
-      pp_string (pp, "UNKNOWN(");
-      if (get_type ())
-	dump_tree (pp, get_type ());
-      pp_character (pp, ')');
-    }
-  else
-    {
-      pp_string (pp, "unknown_svalue2(");
-      if (get_type ())
-	dump_tree (pp, get_type ());
-      pp_character (pp, ')');
-    }
-}
-
-/* FIXME.  */
-
-void
-unknown_svalue2::accept (visitor *v) const
-{
-  v->visit_unknown_svalue2 (this);
-}
-
-/* class poisoned_svalue2 : public svalue2.  */
-
-/* Compare the fields of this poisoned_svalue2 with OTHER, returning true
-   if they are equal.
-   For use by svalue2::operator==.  */
-#if 0
-bool
-poisoned_svalue2::compare_fields (const poisoned_svalue2 &other) const
-{
-  return m_kind == other.m_kind;
-}
-#endif
-
-/* FIXME.  */
-
-void
-poisoned_svalue2::accept (visitor *v) const
-{
-  v->visit_poisoned_svalue2 (this);
-}
-
-/* FIXME.  */
-
-void
-poisoned_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  if (simple)
-    pp_printf (pp, "POISONED(%s)", poison_kind_to_str (m_kind));
-  else
-    pp_printf (pp, "poisoned_svalue2(%s)", poison_kind_to_str (m_kind));
-}
-
-/* class setjmp_svalue2's implementation is in engine.cc, so that it can use
-   the declaration of exploded_node.  */
-
-/* class initial_svalue2 : public svalue2.  */
-
-/* FIXME.  */
-
-void
-initial_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  if (simple)
-    {
-      pp_string (pp, "INIT_VAL(");
-      m_reg->dump_to_pp (pp, simple);
-      pp_string (pp, ")");
-    }
-  else
-    {
-      pp_string (pp, "initial_svalue2(");
-      print_quoted_type (pp, get_type ());
-      pp_string (pp, ", ");
-      m_reg->dump_to_pp (pp, simple);
-      pp_string (pp, ")");
-    }
-}
-
-/* FIXME.  */
-
-void
-initial_svalue2::accept (visitor *v) const
-{
-  v->visit_initial_svalue2 (this);
-  m_reg->accept (v);
-}
-
-/* Compare the fields of this initial_svalue2 with OTHER, returning true
-   if they are equal.
-   For use by svalue2::operator==.  */
-#if 0
-bool
-initial_svalue2::compare_fields (const initial_svalue2 &other) const
-{
-  return m_reg == other.m_reg;
-}
-#endif
-
-/* class unaryop_svalue2 : public svalue2.  */
-
-void
-unaryop_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  if (simple)
-    {
-      if (m_op == NOP_EXPR)
-	{
-	  pp_string (pp, "CAST(");
-	  dump_tree (pp, get_type ());
-	  pp_string (pp, ", ");
-	  m_arg->dump_to_pp (pp, simple);
-	  pp_character (pp, ')');
-	}
-      else
-	{
-	  pp_character (pp, '(');
-	  pp_string (pp, get_tree_code_name (m_op));
-	  //pp_string (pp, op_symbol_code (m_op));
-	  m_arg->dump_to_pp (pp, simple);
-	  pp_character (pp, ')');
-	}
-    }
-  else
-    {
-      pp_string (pp, "unaryop_svalue2 (");
-      // TODO: type?
-      pp_string (pp, get_tree_code_name (m_op));
-      pp_string (pp, ", ");
-      m_arg->dump_to_pp (pp, simple);
-      pp_character (pp, ')');
-    }
-}
-
-/* FIXME.  */
-
-void
-unaryop_svalue2::accept (visitor *v) const
-{
-  v->visit_unaryop_svalue2 (this);
-  m_arg->accept (v);
-}
-
-/* class binop_svalue2 : public svalue2.  */
-
-/* FIXME.  */
-
-void
-binop_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  if (simple)
-    {
-      pp_character (pp, '(');
-      m_arg0->dump_to_pp (pp, simple);
-      pp_string (pp, op_symbol_code (m_op));
-      m_arg1->dump_to_pp (pp, simple);
-      pp_character (pp, ')');
-    }
-  else
-    {
-      pp_string (pp, "binop_svalue2 (");
-      // TODO: type?
-      pp_string (pp, get_tree_code_name (m_op));
-      pp_string (pp, ", ");
-      m_arg0->dump_to_pp (pp, simple);
-      pp_string (pp, ", ");
-      m_arg1->dump_to_pp (pp, simple);
-      pp_character (pp, ')');
-    }
-}
-
-/* FIXME.  */
-
-void
-binop_svalue2::accept (visitor *v) const
-{
-  v->visit_binop_svalue2 (this);
-  m_arg0->accept (v);
-  m_arg1->accept (v);
-}
-
-/* class sub_svalue2 : public svalue2.  */
-
-sub_svalue2::sub_svalue2 (tree type, const svalue2 *parent_svalue,
-			  const region2 *subregion)
-: svalue2 (complexity::from_pair (parent_svalue->get_complexity (),
-				  subregion->get_complexity ()),
-	   type),
-  m_parent_svalue (parent_svalue), m_subregion (subregion)
-{
-}
-
-/* FIXME.  */
-
-void
-sub_svalue2::accept (visitor *v) const
-{
-  v->visit_sub_svalue2 (this);
-  m_parent_svalue->accept (v);
-  m_subregion->accept (v);
-}
-
-void
-sub_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  if (simple)
-    {
-      pp_string (pp, "SUB(");
-      m_parent_svalue->dump_to_pp (pp, simple);
-      pp_string (pp, ", ");
-      m_subregion->dump_to_pp (pp, simple);
-      pp_character (pp, ')');
-    }
-  else
-    {
-      pp_string (pp, "sub_svalue2 (");
-      // TODO: type?
-      pp_string (pp, ", ");
-      m_parent_svalue->dump_to_pp (pp, simple);
-      pp_string (pp, ", ");
-      m_subregion->dump_to_pp (pp, simple);
-      pp_character (pp, ')');
-    }
-}
-
-/* class widening_svalue2 : public svalue2.  */
-
-/* FIXME.  */
-
-void
-widening_svalue2::accept (visitor *v) const
-{
-  v->visit_widening_svalue2 (this);
-  m_base_sval->accept (v);
-  m_iter_sval->accept (v);
-}
-
-void
-widening_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  if (simple)
-    {
-      pp_string (pp, "WIDENING(");
-      pp_character (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);
-      pp_character (pp, ')');
-    }
-  else
-    {
-      pp_string (pp, "widening_svalue2 (");
-      // TODO: type?
-      pp_string (pp, ", ");
-      pp_character (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);
-      pp_character (pp, ')');
-    }
-}
-
-/* FIXME.  */
-
-enum widening_svalue2::direction_t
-widening_svalue2::get_direction () const
-{
-  tree base_cst = m_base_sval->maybe_get_constant ();
-  if (base_cst == NULL_TREE)
-    return DIR_UNKNOWN;
-  tree iter_cst = m_iter_sval->maybe_get_constant ();
-  if (iter_cst == NULL_TREE)
-    return DIR_UNKNOWN;
-
-  tree iter_gt_base = fold_binary (GT_EXPR, boolean_type_node,
-				   iter_cst, base_cst);
-  if (iter_gt_base == boolean_true_node)
-    return DIR_ASCENDING;
-
-  tree iter_lt_base = fold_binary (LT_EXPR, boolean_type_node,
-				   iter_cst, base_cst);
-  if (iter_lt_base == boolean_true_node)
-    return DIR_DESCENDING;
-
-  return DIR_UNKNOWN;
-}
-
-/* FIXME.  */
-
-tristate
-widening_svalue2::eval_condition_without_cm (enum tree_code op,
-					     tree rhs_cst) const
-{
-  tree base_cst = m_base_sval->maybe_get_constant ();
-  if (base_cst == NULL_TREE)
-    return tristate::TS_UNKNOWN;
-  tree iter_cst = m_iter_sval->maybe_get_constant ();
-  if (iter_cst == NULL_TREE)
-    return tristate::TS_UNKNOWN;
-
-  switch (get_direction ())
-    {
-    default:
-      gcc_unreachable ();
-    case DIR_ASCENDING:
-      /* LHS is in [base_cst, +ve infinity), assuming no overflow.  */
-      switch (op)
-	{
-	case LE_EXPR:
-	case LT_EXPR:
-	  {
-	    /* [BASE, +INF) OP RHS:
-	       This is either true or false at +ve ininity,
-	       It can be true for points X where X OP RHS, so we have either
-	       "false", or "unknown".  */
-	    tree base_op_rhs = fold_binary (op, boolean_type_node,
-					    base_cst, rhs_cst);
-	    if (base_op_rhs == boolean_true_node)
-	      return tristate::TS_UNKNOWN;
-	    else
-	      return tristate::TS_FALSE;
-	  }
-
-	case GE_EXPR:
-	case GT_EXPR:
-	  {
-	    /* [BASE, +INF) OP RHS:
-	       This is true at +ve infinity.  It will be true everywhere
-	       in the range if BASE >= RHS.  */
-	    tree base_op_rhs = fold_binary (op, boolean_type_node,
-					    base_cst, rhs_cst);
-	    if (base_op_rhs == boolean_true_node)
-	      return tristate::TS_TRUE;
-	    else
-	      return tristate::TS_UNKNOWN;
-	  }
-
-	case EQ_EXPR:
-	  {
-	    /* [BASE, +INF) == RHS:
-	       Could this be true at any point in the range?  If so we
-	       have "unknown", otherwise we have "false".  */
-	    tree base_le_rhs = fold_binary (LE_EXPR, boolean_type_node,
-					    base_cst, rhs_cst);
-	    if (base_le_rhs == boolean_true_node)
-	      return tristate::TS_UNKNOWN;
-	    else
-	      return tristate::TS_FALSE;
-	  }
-
-	case NE_EXPR:
-	  {
-	    /* [BASE, +INF) != RHS:
-	       Could we have equality at any point in the range?  If so we
-	       have "unknown", otherwise we have "true".  */
-	    tree base_le_rhs = fold_binary (LE_EXPR, boolean_type_node,
-					    base_cst, rhs_cst);
-	    if (base_le_rhs == boolean_true_node)
-	      return tristate::TS_UNKNOWN;
-	    else
-	      return tristate::TS_TRUE;
-	  }
-
-	default:
-	  return tristate::TS_UNKNOWN;
-	}
-
-    case DIR_DESCENDING:
-      /* LHS is in (-ve infinity, base_cst], assuming no overflow.  */
-      // TODO:
-      return tristate::TS_UNKNOWN;
-
-    case DIR_UNKNOWN:
-      return tristate::TS_UNKNOWN;
-    }
-}
-
-/* class placeholder_svalue2 : public svalue2.  */
-
-/* FIXME.  */
-
-void
-placeholder_svalue2::accept (visitor *v) const
-{
-  v->visit_placeholder_svalue2 (this);
-}
-
-void
-placeholder_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  if (simple)
-    {
-      pp_printf (pp, "PLACEHOLDER(%qs)", m_name);
-    }
-  else
-    {
-      pp_printf (pp, "placeholder_svalue2 (%qs)", m_name);
-      // TODO: type?
-    }
-}
-
-/* class unmergeable_svalue2 : public svalue2.  */
-
-/* FIXME.  */
-
-void
-unmergeable_svalue2::accept (visitor *v) const
-{
-  v->visit_unmergeable_svalue2 (this);
-  m_arg->accept (v);
-}
-
-void
-unmergeable_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  if (simple)
-    {
-      pp_string (pp, "UNMERGEABLE(");
-      m_arg->dump_to_pp (pp, simple);
-      pp_character (pp, ')');
-    }
-  else
-    {
-      pp_string (pp, "unmergeable_svalue2 (");
-      m_arg->dump_to_pp (pp, simple);
-      pp_character (pp, ')');
-    }
-}
-
-/* class compound_svalue2 : public svalue2.  */
-
-/* FIXME.  */
-
-void
-compound_svalue2::accept (visitor *v) const
-{
-  v->visit_compound_svalue2 (this);
-  for (binding_map::iterator_t iter = m_map.begin ();
-       iter != m_map.end (); ++iter)
-    {
-      //(*iter).first.accept (v);
-      (*iter).second->accept (v);
-    }
-}
-
-void
-compound_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  if (simple)
-    {
-      pp_string (pp, "COMPOUND(");
-      m_map.dump_to_pp (pp, simple, false);
-      pp_character (pp, ')');
-    }
-  else
-    {
-      pp_string (pp, "compound_svalue2 (");
-      // TODO: type?
-      pp_string (pp, ", ");
-      pp_character (pp, '{');
-      m_map.dump_to_pp (pp, simple, false);
-      pp_string (pp, "}, ");
-      pp_character (pp, ')');
-    }
-}
-
-/* FIXME.  */
-
-complexity
-compound_svalue2::calc_complexity (const binding_map &map)
-{
-  unsigned num_child_nodes = 0;
-  unsigned max_child_depth = 0;
-  for (binding_map::iterator_t iter = map.begin ();
-       iter != map.end (); ++iter)
-    {
-      const complexity &sval_c = (*iter).second->get_complexity ();
-      num_child_nodes += sval_c.m_num_nodes;
-      max_child_depth = MAX (max_child_depth, sval_c.m_max_depth);
-    }
-  return complexity (num_child_nodes + 1, max_child_depth + 1);
-}
-
-/* class conjured_svalue2 : public svalue2.  */
-
-/* FIXME.  */
-
-void
-conjured_svalue2::accept (visitor *v) const
-{
-  v->visit_conjured_svalue2 (this);
-  m_id_reg->accept (v);
-}
-
-void
-conjured_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  if (simple)
-    {
-      pp_string (pp, "CONJURED(");
-      pp_gimple_stmt_1 (pp, m_stmt, 0, (dump_flags_t)0);
-      pp_string (pp, ", ");
-      m_id_reg->dump_to_pp (pp, simple);
-      pp_character (pp, ')');
-    }
-  else
-    {
-      pp_string (pp, "conjured_svalue2 (");
-      // TODO: type?
-      pp_string (pp, ", ");
-      pp_gimple_stmt_1 (pp, m_stmt, 0, (dump_flags_t)0);
-      pp_string (pp, ", ");
-      m_id_reg->dump_to_pp (pp, simple);
-      pp_character (pp, ')');
-    }
-}
-
-/* class region2 and its various subclasses.  */
-
-/* Get a string for KIND for use in debug dumps.  */
-
-const char *
-region2::kind_to_str (enum kind kind)
-{
-  switch (kind)
-    {
-    default:
-      gcc_unreachable ();
-    case RK_FRAME:
-      return "frame";
-    case RK_GLOBALS:
-      return "globals";
-    case RK_CODE:
-      return "code";
-    case RK_FUNCTION:
-      return "function";
-    case RK_STACK:
-      return "stack";
-    case RK_HEAP:
-      return "heap";
-    case RK_ROOT:
-      return "root";
-#if 0
-    case RK_SYMBOLIC:
-      return "symbolic";
-#endif
-    }
-}
-
-/* class region2.  */
-
-region2::~region2 ()
-{
-  delete m_cached_offset;
-}
-
-/* FIXME.  */
-
-int
-region2::cmp_ids (const region2 *reg1, const region2 *reg2)
-{
-  return (long)reg1->get_id () - (long)reg2->get_id ();
-}
-
-/* Equality operator for region2.
-   After comparing base class fields and kind, the rest of the
-   comparison is handled off to a "compare_fields" member function
-   specific to the appropriate subclass.  */
-
-bool
-region2::operator== (const region2 &other) const
-{
-  if (m_parent != other.m_parent)
-    return false;
-  if (m_type != other.m_type)
-    return false;
-
-  enum kind this_kind = get_kind ();
-  enum kind other_kind = other.get_kind ();
-  if (this_kind != other_kind)
-    return false;
-#if 0
-  /* Compare views.  */
-  if (m_view_regs.length () != other.m_view_regs.length ())
-    return false;
-  int i;
-  region2_id *rid;
-  FOR_EACH_VEC_ELT (m_view_regs, i, rid)
-    if (! (*rid == other.m_view_regs[i]))
-      return false;
-#endif
-  switch (this_kind)
-    {
-    default:
-      gcc_unreachable ();
-    case RK_FRAME:
-      {
-	const frame_region2 &this_sub
-	  = (const frame_region2 &)*this;
-	const frame_region2 &other_sub
-	  = (const frame_region2 &)other;
-	return this_sub.compare_fields (other_sub);
-      }
-#if 0
-    case RK_GLOBALS:
-      {
-	const globals_region2 &this_sub
-	  = (const globals_region2 &)*this;
-	const globals_region2 &other_sub
-	  = (const globals_region2 &)other;
-	return this_sub.compare_fields (other_sub);
-      }
-    case RK_CODE:
-      {
-	const code_region2 &this_sub
-	  = (const code_region2 &)*this;
-	const code_region2 &other_sub
-	  = (const code_region2 &)other;
-	return this_sub.compare_fields (other_sub);
-      }
-    case RK_FUNCTION:
-      {
-	const function_region2 &this_sub
-	  = (const function_region2 &)*this;
-	const function_region2 &other_sub
-	  = (const function_region2 &)other;
-	return this_sub.compare_fields (other_sub);
-      }
-    case RK_STACK:
-      {
-	const stack_region2 &this_sub
-	  = (const stack_region2 &)*this;
-	const stack_region2 &other_sub
-	  = (const stack_region2 &)other;
-	return this_sub.compare_fields (other_sub);
-      }
-    case RK_ROOT:
-      {
-	const root_region2 &this_sub
-	  = (const root_region2 &)*this;
-	const root_region2 &other_sub
-	  = (const root_region2 &)other;
-	return this_sub.compare_fields (other_sub);
-      }
-#endif
-    case RK_SYMBOLIC:
-      {
-	const symbolic_region2 &this_sub
-	  = (const symbolic_region2 &)*this;
-	const symbolic_region2 &other_sub
-	  = (const symbolic_region2 &)other;
-	return this_sub.compare_fields (other_sub);
-      }
-#if 0
-    case RK_HEAP:
-      {
-	const heap_region2 &this_sub
-	  = (const heap_region2 &)*this;
-	const heap_region2 &other_sub
-	  = (const heap_region2 &)other;
-	return this_sub.compare_fields (other_sub);
-      }
-#endif
-    case RK_DECL:
-      {
-	const decl_region2 &this_sub
-	  = (const decl_region2 &)*this;
-	const decl_region2 &other_sub
-	  = (const decl_region2 &)other;
-	return this_sub.compare_fields (other_sub);
-      }
-    case RK_FIELD:
-      {
-	const field_region2 &this_sub
-	  = (const field_region2 &)*this;
-	const field_region2 &other_sub
-	  = (const field_region2 &)other;
-	return this_sub.compare_fields (other_sub);
-      }
-    case RK_ELEMENT:
-      {
-	const element_region2 &this_sub
-	  = (const element_region2 &)*this;
-	const element_region2 &other_sub
-	  = (const element_region2 &)other;
-	return this_sub.compare_fields (other_sub);
-      }
-    case RK_OFFSET:
-      {
-	const offset_region2 &this_sub
-	  = (const offset_region2 &)*this;
-	const offset_region2 &other_sub
-	  = (const offset_region2 &)other;
-	return this_sub.compare_fields (other_sub);
-      }
-    case RK_CAST:
-      {
-	const cast_region2 &this_sub
-	  = (const cast_region2 &)*this;
-	const cast_region2 &other_sub
-	  = (const cast_region2 &)other;
-	return this_sub.compare_fields (other_sub);
-      }
-    }
-}
-
-/* FIXME.  */
-
-const region2 *
-region2::get_base_region () const
-{
-  const region2 *iter = this;
-  while (iter)
-    {
-      switch (iter->get_kind ())
-	{
-	case RK_FIELD:
-	case RK_ELEMENT:
-	case RK_OFFSET:
-	  iter = iter->get_parent_region ();
-	  continue;
-	case RK_CAST:
-	  iter = iter->dyn_cast_cast_region2 ()->get_original_region ();
-	  continue;
-	default:
-	  return iter;
-	}
-    }
-  return iter;
-}
-
-/* Return true if get_base_region() == this for this region.  */
-
-bool
-region2::base_region_p () const
-{
-  switch (get_kind ())
-    {
-    /* Region kinds representing a descendent of a base region.  */
-    case RK_FIELD:
-    case RK_ELEMENT:
-    case RK_OFFSET:
-    case RK_CAST:
-      return false;
-
-    default:
-      return true;
-    }
-}
-
-/* Return true if this region is ELDER or one of its descendents.  */
-// FIXME: unit test
-
-bool
-region2::descendent_of_p (const region2 *elder) const
-{
-  const region2 *iter = this;
-  while (iter)
-    {
-      if (iter == elder)
-	return true;
-      if (iter->get_kind () == RK_CAST)
-	iter = iter->dyn_cast_cast_region2 ()->get_original_region ();
-      else
-	iter = iter->get_parent_region ();
-    }
-  return false;
-}
-
-/* If this region is a frame_region2, or a descendent of one, return it.
-   Otherwise return NULL.  */
-
-const frame_region2 *
-region2::maybe_get_frame_region () const
-{
-  const region2 *iter = this;
-  while (iter)
-    {
-      if (const frame_region2 *frame_reg = iter->dyn_cast_frame_region2 ())
-	return frame_reg;
-      if (iter->get_kind () == RK_CAST)
-	iter = iter->dyn_cast_cast_region2 ()->get_original_region ();
-      else
-	iter = iter->get_parent_region ();
-    }
-  return NULL;
-}
-
-/* FIXME.  */
-
-tree
-region2::maybe_get_decl () const
-{
-  if (const decl_region2 *decl_reg = dyn_cast_decl_region2 ())
-    return decl_reg->get_decl ();
-  return NULL_TREE;
-}
-
-/* FIXME.  */
-
-region_offset
-region2::get_offset () const
-{
-  if(!m_cached_offset)
-    m_cached_offset = new region_offset (calc_offset ());
-  return *m_cached_offset;
-}
-
-/* FIXME.  */
-
-bool
-region2::get_byte_size (byte_size_t *out) const
-{
-  tree type = get_type ();
-
-  /* Bail out e.g. for heap-allocated regions.  */
-  if (!type)
-    return false;
-
-  HOST_WIDE_INT bytes = int_size_in_bytes (type);
-  if (bytes == -1)
-    {
-      /* FIXME: this will fail for VLAs
-	 TODO: how should we handle those?
-	 TODO: also, what happens for the "empty array final field" idiom?  */
-      return false;
-    }
-  *out = bytes;
-  return true;
-  /* FIXME: or maybe max_int_size_in_bytes (const_tree type)
-     FIXME: what about varying sizes??? */
-
-  /* See also bit_position (const_tree field)
-     and int_byte_position (const_tree field).  */
-  gcc_unreachable (); // TODO
-}
-
-/* FIXME.  */
-
-bool
-region2::get_bit_size (bit_size_t *out) const
-{
-  byte_size_t byte_size;
-  if (!get_byte_size (&byte_size))
-    return false;
-  *out = byte_size * BITS_PER_UNIT;
-  return true;
-}
-
-/* FIXME.  */
-/* FIXME: support the case where the bit offset is *within* one of the fields;
-   consider nested structs.  */
-
-static tree
-get_field_at_bit_offset (tree record_type, bit_offset_t bit_offset)
-{
-  gcc_assert (TREE_CODE (record_type) == RECORD_TYPE);
-  gcc_assert (bit_offset >= 0);
-
-  /* Find the first field that has an offset > BIT_OFFSET,
-     then return the one preceding it.
-     Skip other trees within the chain, such as FUNCTION_DECLs.  */
-  tree last_field = NULL_TREE;
-  for (tree iter = TYPE_FIELDS (record_type); iter != NULL_TREE;
-       iter = DECL_CHAIN (iter))
-    {
-      if (TREE_CODE (iter) == FIELD_DECL)
-	{
-	  int iter_field_offset = int_bit_position (iter);
-	  if (bit_offset < iter_field_offset)
-	    return last_field;
-	  last_field = iter;
-	}
-    }
-  return last_field;
-}
-
-/* FIXME.  */
-/* FIXME:
-   The bit offset is expressed relative to the base region in the cluster.
-   We need some kind of recursive thing that walks the types and components.
-   Or we need to subtract the offset, to get relative offset.
-   See p51 of my notebook.
-   Do we need a way to get the relative offset of a region, relative to
-   its parent?   */
-
-void
-region2::get_subregions_for_binding (region_model2_manager *mgr,
-				     bit_offset_t relative_bit_offset,
-				     bit_size_t size_in_bits,
-				     tree type,
-				     auto_vec <const region2 *> *out) const
-{
-  if (get_type () == NULL_TREE)
-    return;
-#if 1
-  if (relative_bit_offset == 0
-      && types_compatible_p (get_type (), type))
-    {
-      out->safe_push (this);
-      return;
-    }
-#endif
-  switch (TREE_CODE (get_type ()))
-    {
-    case ARRAY_TYPE:
-      {
-	tree element_type = TREE_TYPE (get_type ());
-	HOST_WIDE_INT hwi_byte_size = int_size_in_bytes (element_type);
-	if (hwi_byte_size > 0)
-	  {
-	    /* FIXME: for some reason this is segfaulting inside
-	       wi::divmod_internal.  */
-#if 0
-	    offset_int bits_per_element = hwi_byte_size << LOG2_BITS_PER_UNIT;
-	    //bit_offset_t*/ element_index = relative_bit_offset / bits_per_element;
-	    bit_offset_t element_index = relative_bit_offset.to_shwi () / bits_per_element.to_shwi ();
-	    tree element_index_cst
-	      = build_int_cst (integer_type_node, element_index.to_shwi ());
-	    bit_offset_t inner_bit_offset
-	      = relative_bit_offset % bits_per_element;
-	    // FIXME: type ^^^^
-#else
-	    HOST_WIDE_INT bits_per_element = hwi_byte_size << LOG2_BITS_PER_UNIT;
-	    //bit_offset_t*/ element_index = relative_bit_offset / bits_per_element;
-	    HOST_WIDE_INT element_index = (relative_bit_offset.to_shwi () / bits_per_element);
-	    tree element_index_cst
-	      = build_int_cst (integer_type_node, element_index);
-	    HOST_WIDE_INT inner_bit_offset
-	      = relative_bit_offset.to_shwi () % bits_per_element;
-	    // FIXME: type ^^^^
-#endif
-	    const region2 *subregion = mgr->get_element_region
-	      (this, element_type,
-	       mgr->get_or_create_constant_svalue2 (element_index_cst));
-	    subregion->get_subregions_for_binding (mgr, inner_bit_offset,
-						   size_in_bits, type, out);
-	  }
-      }
-      break;
-    case RECORD_TYPE:
-      {
-	/* The bit offset might be *within* one of the fields (such as
-	   with nested structs).
-	   So we want to find the enclosing field, adjust the offset,
-	   and repeat.  */
-	// TODO: maybe convert from recursion to iteration?
-	if (tree field = get_field_at_bit_offset (get_type (),
-						  relative_bit_offset))
-	  {
-	    int field_bit_offset = int_bit_position (field);
-	    const region2 *subregion = mgr->get_field_region (this, field);
-	    subregion->get_subregions_for_binding
-	      (mgr, relative_bit_offset - field_bit_offset,
-	       size_in_bits, type, out);
-	  }
-      }
-      break;
-    case UNION_TYPE:
-      {
-	for (tree field = TYPE_FIELDS (get_type ()); field != NULL_TREE;
-	     field = DECL_CHAIN (field))
-	  {
-	    const region2 *subregion = mgr->get_field_region (this, field);
-	    subregion->get_subregions_for_binding (mgr,
-						   relative_bit_offset,
-						   size_in_bits,
-						   type,
-						   out);
-	  }
-      }
-      break;
-    default:
-      /* Do nothing FIXME.  */
-      break;
-    }
-}
-
-/* FIXME.  */
-
-region_offset
-region2::calc_offset () const
-{
-  const region2 *iter_region = this;
-  bit_offset_t accum_bit_offset = 0;
-
-  while (iter_region)
-    {
-      switch (iter_region->get_kind ())
-	{
-	case RK_FIELD:
-	  {
-	    const field_region2 *field_region
-	      = (const field_region2 *)iter_region;
-	    iter_region = iter_region->get_parent_region ();
-
-	    /* Compare with e.g. gimple-fold.c's
-	       fold_nonarray_ctor_reference.  */
-	    tree field = field_region->get_field ();
-	    tree byte_offset = DECL_FIELD_OFFSET (field);
-	    tree field_offset = DECL_FIELD_BIT_OFFSET (field);
-	    /* Compute bit offset of the field.  */
-	    offset_int bitoffset
-	      = (wi::to_offset (field_offset)
-		 + (wi::to_offset (byte_offset) << LOG2_BITS_PER_UNIT));
-	    accum_bit_offset += bitoffset;
-	  }
-	  continue;
-
-	case RK_ELEMENT:
-	  {
-	    const element_region2 *element_region
-	      = (const element_region2 *)iter_region;
-	    iter_region = iter_region->get_parent_region ();
-
-	    if (tree idx_cst
-		  = element_region->get_index ()->maybe_get_constant ())
-	      {
-		gcc_assert (TREE_CODE (idx_cst) == INTEGER_CST);
-
-		tree elem_type = element_region->get_type ();
-
-		// FIXME:
-#if 0
-		/* Arithmetic on void-pointers is a GNU C extension, treating
-		   the size of a void as 1.
-		   https://gcc.gnu.org/onlinedocs/gcc/Pointer-Arith.html  */
-		if (TREE_CODE (elem_type) == VOID_TYPE)
-		  return offset_sid;
-#endif
-
-		offset_int element_idx = wi::to_offset (idx_cst);
-
-		/* First, use int_size_in_bytes, to reject the case where we
-		   have an incomplete type, or a non-constant value.  */
-		HOST_WIDE_INT hwi_byte_size = int_size_in_bytes (elem_type);
-		if (hwi_byte_size > 0)
-		  {
-		    offset_int element_bit_size
-		      = hwi_byte_size << LOG2_BITS_PER_UNIT;
-		    offset_int element_bit_offset
-		      = element_idx * element_bit_size;
-		    accum_bit_offset += element_bit_offset;
-		    continue;
-		  }
-	      }
-	    return region_offset::make_symbolic (iter_region);
-	  }
-	  continue;
-
-	case RK_OFFSET:
-	  {
-	    const offset_region2 *offset_region
-	      = (const offset_region2 *)iter_region;
-	    iter_region = iter_region->get_parent_region ();
-
-	    if (tree byte_offset_cst
-		  = offset_region->get_byte_offset ()->maybe_get_constant ())
-	      {
-		gcc_assert (TREE_CODE (byte_offset_cst) == INTEGER_CST);
-		/* Use a signed value for the byte offset, to handle
-		   negative offsets.  */
-		HOST_WIDE_INT byte_offset
-		  = wi::to_offset (byte_offset_cst).to_shwi ();
-		HOST_WIDE_INT bit_offset = byte_offset * BITS_PER_UNIT;
-		accum_bit_offset += bit_offset;
-	      }
-	    else
-	      return region_offset::make_symbolic (iter_region);
-	  }
-	  continue;
-
-#if 0
-	case RK_SYMBOLIC:
-	  {
-	    /* Get the offset of a region identified symbolically from a
-	       pointer svalue.  */
-	    const symbolic_region2 *symbolic_region
-	      = (const symbolic_region2 *)iter_region;
-	    const svalue2 *sval = symbolic_region->get_pointer ();
-	    switch (sval->get_kind ())
-	      {
-	      default:
-		gcc_unreachable ();
-	      case svalue2::SK_BINOP:
-		{
-		  const binop_svalue2 *binop_sval
-		    = as_a <const binop_svalue2 *> (sval);
-		  switch (binop_sval->get_op ())
-		    {
-		    default:
-		      gcc_unreachable ();
-		    case POINTER_PLUS_EXPR:
-		      {
-			// FIXME:
-			/* If we have a symbolic value expressing pointer arithmentic,
-			   try to convert it to a suitable region.  */
-			/* FIXME: but should it be an element_region2?  that expresses
-			   things via element indices, rather than via byte offsets.  */
-			/* Perhaps instead we should capture this as an offset relative
-			   to a base region in region_offset?  */
-			const svalue2 *ptr = binop_sval->get_arg0 ();
-			const svalue2 *offset = binop_sval->get_arg1 ();
-			gcc_unreachable ();
-		      }
-		    }
-		}
-	      }
-	    gcc_unreachable ();
-	  }
-	  break;
-#endif
-
-	case RK_CAST:
-	  {
-	    const cast_region2 *cast_reg
-	      = as_a <const cast_region2 *> (iter_region);
-	    iter_region = cast_reg->get_original_region ();
-	  }
-	  continue;
-
-	default:
-	  return region_offset::make_concrete (iter_region, accum_bit_offset);
-	}
-    }
-  return region_offset::make_concrete (iter_region, accum_bit_offset);
-}
-
-/* Set this region2's value to RHS_SVAL (or potentially a variant of it,
-   for some kinds of casts).  */
-#if 0
-void
-region2::set_value (region_model2 &model, region2 *lhs_reg, const svalue2 *rhs_sval,
-		    region_model2_context *ctxt)
-{
-  gcc_unreachable ();
-  /* Handle some kinds of casting.  */
-  if (m_type)
-    {
-      const svalue2 *sval = model.get_svalue2 (rhs_sval);
-      if (sval->get_type ())
-	rhs_sval = model.maybe_cast (m_type, rhs_sval, ctxt);
-
-      sval = model.get_svalue2 (rhs_sval);
-      if (sval->get_type ())
-	gcc_assert (m_type == sval->get_type ());
-    }
-
-  m_sval_id = rhs_sval;
-
-  /* Update views.
-     If this is a view, it becomes its parent's active view.
-     If there was already an active views, invalidate its value; otherwise
-     if the parent itself had a value, invalidate it.
-     If it's not a view, then deactivate any view that is active on this
-     region2.  */
-  {
-    if (m_is_view)
-      become_active_view (model, this_reg);
-    else
-      {
-	deactivate_any_active_view (model);
-	gcc_assert (m_active_view_reg.null_p ());
-      }
-  }
-}
-#endif
-
-
-#if 0
-/* Make this region2 (with id THIS_REG) the "active" view of its parent.
-   Any other active view has its value set to "unknown" and descendent values
-   cleared.
-   If there wasn't an active view, then set the parent's value to unknown, and
-   clear its descendent values (apart from this view).  */
-void
-region2::become_active_view (region_model2 &model, region2_id this_reg)
-{
-  gcc_assert (m_is_view);
-
-  region2 *parent_reg = model.get_region2 (m_parent_reg);
-  gcc_assert (parent_reg);
-
-  region2_id old_active_view_reg = parent_reg->m_active_view_reg;
-
-  if (old_active_view_reg == this_reg)
-    {
-      /* Already the active view: do nothing.  */
-      return;
-    }
-
-  /* We have a change of active view.  */
-  parent_reg->m_active_view_reg = this_reg;
-
-  if (old_active_view_reg.null_p ())
-    {
-      /* No previous active view, but the parent and its other children
-	 might have values.
-	 If so, invalidate those values - but not that of the new view.  */
-      region2_id_set below_region2 (&model);
-      model.get_descendents (m_parent_reg, &below_region2, this_reg);
-      for (unsigned i = 0; i < model.get_num_region2s (); i++)
-	{
-	  region2_id rid (region2_id::from_int (i));
-	  if (below_region2.region2_p (rid))
-	    {
-	      region2 *other_reg = model.get_region2 (rid);
-	      other_reg->m_sval_id = svalue2_id::null ();
-	    }
-	}
-      region2 *parent = model.get_region2 (m_parent_reg);
-      parent->m_sval_id
-	= model.add_svalue2 (new unknown_svalue2 (parent->get_type ()));
-    }
-  else
-    {
-      /* If there was an active view, invalidate it.  */
-      region2 *old_active_view = model.get_region2 (old_active_view_reg);
-      old_active_view->deactivate_view (model, old_active_view_reg);
-    }
-}
-
-/* If this region2 (with id THIS_REG) has an active view, deactivate it,
-   clearing m_active_view_reg.  */
-
-void
-region2::deactivate_any_active_view (region_model2 &model)
-{
-  if (m_active_view_reg.null_p ())
-    return;
-  region2 *view = model.get_region2 (m_active_view_reg);
-  view->deactivate_view (model, m_active_view_reg);
-  m_active_view_reg = region2_id::null ();
-}
-
-/* Clear any values for region2s below THIS_REG.
-   Set the view's value to unknown.  */
-
-void
-region2::deactivate_view (region_model2 &model, region2_id this_view_reg)
-{
-  gcc_assert (is_view_p ());
-
-  /* Purge values from old_active_this_view_reg and all its
-     descendents.  Potentially we could use a poison value
-     for this, but let's use unknown for now.  */
-  region2_id_set below_view (&model);
-  model.get_descendents (this_view_reg, &below_view, region2_id::null ());
-
-  for (unsigned i = 0; i < model.get_num_region2s (); i++)
-    {
-      region2_id rid (region2_id::from_int (i));
-      if (below_view.region2_p (rid))
-	{
-	  region2 *other_reg = model.get_region2 (rid);
-	  other_reg->m_sval_id = svalue2_id::null ();
-	}
-    }
-
-  m_sval_id = model.add_svalue2 (new unknown_svalue2 (get_type ()));
-}
-#endif
-
-/* Get a value for this region2, either its value if it has one,
-   or, failing that, "inherit" a value from first ancestor with a
-   non-null value.
-
-   For example, when getting the value for a local variable within
-   a stack frame that doesn't have one, the frame doesn't have a value
-   either, but the stack as a whole will have an "uninitialized" poison
-   value, so inherit that.  */
-#if 0
-svalue2_id
-region2::get_value (region_model2 &model, bool non_null,
-		   region_model2_context *ctxt)
-{
-  /* If this region2 has a value, use it. */
-  if (!m_sval_id.null_p ())
-    return m_sval_id;
-
-  /* Otherwise, "inherit" value from first ancestor with a
-     non-null value. */
-
-  region2 *parent = model.get_region2 (m_parent_reg);
-  if (parent)
-    {
-      svalue2_id inherited_sval
-	= parent->get_inherited_child_sval (this, model, ctxt);
-      if (!inherited_sval.null_p ())
-	return inherited_sval;
-    }
-
-  /* If a non-null value has been requested, then generate
-     a new unknown value.  Store it, so that repeated reads from this
-     region2 will yield the same unknown value.  */
-  if (non_null)
-    {
-      svalue2_id unknown_sval = model.add_svalue2 (new unknown_svalue2 (m_type));
-      m_sval_id = unknown_sval;
-      return unknown_sval;
-    }
-
-  return svalue2_id::null ();
-}
-
-/* Get a value for CHILD, inheriting from this region2.
-
-   Recurse, so this region2 will inherit a value if it doesn't already
-   have one.  */
-
-svalue2_id
-region2::get_inherited_child_sval (region2 *child,
-				 region_model2 &model,
-				 region_model2_context *ctxt)
-{
-  if (m_sval_id.null_p ())
-    {
-      /* Recurse.  */
-      if (!m_parent_reg.null_p ())
-	{
-	  region2 *parent = model.get_region2 (m_parent_reg);
-	  m_sval_id = parent->get_inherited_child_sval (this, model, ctxt);
-	}
-    }
-
-  if (!m_sval_id.null_p ())
-    {
-      /* Clone the parent's value, so that attempts to update it
-	 (e.g giving a specific value to an inherited "uninitialized"
-	 value) touch the child, and not the parent.  */
-      const svalue2 *this_value = model.get_svalue2 (m_sval_id);
-      svalue2_id new_child_sval
-	= this_value->get_child_sval (this, child, model, ctxt);
-      if (ctxt)
-	ctxt->on_inherited_svalue2 (m_sval_id, new_child_sval);
-      child->m_sval_id = new_child_sval;
-      return new_child_sval;
-    }
-
-  return svalue2_id::null ();
-}
-#endif
-
-// FIXME:
-/* Copy from SRC_REG to DST_REG, using CTXT for any issues that occur.
-   Copy across any value for the region2, and handle structs, unions
-   and arrays recursively.  */
-
-void
-region_model2::copy_region (const region2 *dst_reg, const region2 *src_reg,
-			    region_model2_context *ctxt)
-{
-  gcc_assert (dst_reg);
-  gcc_assert (src_reg);
-  if (dst_reg == src_reg)
-    return;
-
-  const svalue2 *sval = get_store_value (src_reg);
-  set_value (dst_reg, sval, ctxt);
-
-#if 0
-  /* Copy across any value for the src region2 itself.  */
-  const svalue2 *sval = src_reg->get_value (*this, true, ctxt);
-  set_value (dst_reg, sid, ctxt);
-
-  if (dst_reg->get_kind () != src_reg->get_kind ())
-    return;
-
-  /* Copy across child region2s for structs, unions, and arrays.  */
-  // FIXME
-  switch (dst_reg->get_kind ())
-    {
-    case RK_FRAME:
-    case RK_GLOBALS:
-    case RK_CODE:
-    case RK_FUNCTION:
-      return;
-    case RK_STACK:
-    case RK_HEAP:
-    case RK_ROOT:
-    case RK_SYMBOLIC:
-      return;
-    }
-#endif
-}
-
-/* Generate a hash value for this region2.  The work is done by the
-   add_to_hash vfunc.  */
-
-hashval_t
-region2::hash () const
-{
-  inchash::hash hstate;
-  add_to_hash (hstate);
-  return hstate.end ();
-}
-
-/* Print a one-liner representation of this region2 to PP, assuming
-   that this region2 is within MODEL and its id is THIS_REG.  */
-#if 0
-void
-region2::print (const region_model2 &model,
-	       region2_id this_reg,
-	       pretty_printer *pp) const
-{
-  this_reg.print (pp);
-  pp_string (pp, ": {");
-
-  /* vfunc.  */
-  print_fields (model, this_reg, pp);
-
-  pp_string (pp, "}");
-}
-#endif
-
-/* Base class implementation of region2::dump_dot_to_pp vfunc.  */
-#if 0
-void
-region2::dump_dot_to_pp (const region_model2 &model,
-			region2_id this_reg,
-			pretty_printer *pp) const
-{
-  this_reg.dump_node_name_to_pp (pp);
-  pp_printf (pp, " [shape=none,margin=0,style=filled,fillcolor=%s,label=\"",
-	     "lightgrey");
-  pp_write_text_to_stream (pp);
-  print (model, this_reg, pp);
-  pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/false);
-  pp_string (pp, "\"];");
-  pp_newline (pp);
-
-  /* Add edge to svalue2.  */
-  if (!m_sval_id.null_p ())
-    {
-      this_reg.dump_node_name_to_pp (pp);
-      pp_string (pp, " -> ");
-      m_sval_id.dump_node_name_to_pp (pp);
-      pp_string (pp, ";");
-      pp_newline (pp);
-    }
-
-  /* Add edge to parent.  */
-  if (!m_parent_reg.null_p ())
-    {
-      this_reg.dump_node_name_to_pp (pp);
-      pp_string (pp, " -> ");
-      m_parent_reg.dump_node_name_to_pp (pp);
-      pp_string (pp, ";");
-      pp_newline (pp);
-    }
-}
-#endif
-
-/* Dump a tree-like ASCII-art representation of this region2 to PP.  */
-#if 0
-void
-region2::dump_to_pp (const region_model2 &model,
-		    region2_id this_reg,
-		    pretty_printer *pp,
-		    const char *prefix,
-		    bool is_last_child) const
-{
-  print (model, this_reg, pp);
-  pp_newline (pp);
-
-  const char *new_prefix;
-  if (!m_parent_reg.null_p ())
-    new_prefix = ACONCAT ((prefix, is_last_child ? "  " : "|  ", NULL));
-  else
-    new_prefix = prefix;
-
-  const char *begin_color = colorize_start (pp_show_color (pp), "note");
-  const char *end_color = colorize_stop (pp_show_color (pp));
-  char *field_prefix
-    = ACONCAT ((begin_color, new_prefix, "|:", end_color, NULL));
-
-  if (!m_sval_id.null_p ())
-    {
-      pp_printf (pp, "%s sval: ", field_prefix);
-      model.get_svalue2 (m_sval_id)->print (model, m_sval_id, pp);
-      pp_newline (pp);
-    }
-  if (m_type)
-    {
-      pp_printf (pp, "%s type: ", field_prefix);
-      print_quoted_type (pp, m_type);
-      pp_newline (pp);
-    }
-
-  /* Find the children.  */
-
-  auto_vec<region2_id> child_regs;
-  unsigned i;
-  for (unsigned i = 0; i < model.get_num_region2s (); ++i)
-    {
-      region2_id rid = region2_id::from_int (i);
-      region2 *child = model.get_region2 (rid);
-      if (child->m_parent_reg == this_reg)
-	child_regs.safe_push (rid);
-    }
-
-  /* Print the children, using dump_child_label to label them.  */
-
-  region2_id *child_reg;
-  FOR_EACH_VEC_ELT (child_regs, i, child_reg)
-    {
-      is_last_child = (i == child_regs.length () - 1);
-      if (!this_reg.null_p ())
-	{
-	  const char *tail = is_last_child ? "`-" : "|-";
-	  pp_printf (pp, "%r%s%s%R", "note", new_prefix, tail);
-	}
-      dump_child_label (model, this_reg, *child_reg, pp);
-      model.get_region2 (*child_reg)->dump_to_pp (model, *child_reg, pp,
-						 new_prefix,
-						 is_last_child);
-    }
-}
-#endif
-
-DEBUG_FUNCTION void
-region2::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);
-  pp_newline (&pp);
-  pp_flush (&pp);
-}
-
-/* FIXME.  */
-
-DEBUG_FUNCTION label_text
-region2::get_desc (bool simple) const
-{
-  pretty_printer pp;
-  pp_format_decoder (&pp) = default_tree_printer;
-  dump_to_pp (&pp, simple);
-  return label_text::take (xstrdup (pp_formatted_text (&pp)));
-}
-
-/* FIXME.  */
-
-void
-region2::accept (visitor *v) const
-{
-  v->visit_region (this);
-  if (m_parent)
-    m_parent->accept (v);
-}
-
-/* Base implementation of region2::validate vfunc.
-   Assert that the fields of "region2" are valid; subclasses should
-   chain up their implementation to this one.  */
-#if 0
-void
-region2::validate (const region_model2 &model) const
-{
-  m_parent_reg.validate (model);
-  m_sval_id.validate (model);
-  unsigned i;
-  region2_id *view_reg;
-  FOR_EACH_VEC_ELT (m_view_regs, i, view_reg)
-    {
-      gcc_assert (!view_reg->null_p ());
-      view_reg->validate (model);
-    }
-  m_active_view_reg.validate (model);
-}
-#endif
-
-#if 0
-/* Add a new region2 with id VIEW_REG as a view of this region2.  */
-
-void
-region2::add_view (region2_id view_reg, region_model2 *model)
-{
-  gcc_assert (!view_reg.null_p ());
-  region2 *new_view = model->get_region2 (view_reg);
-  new_view->m_is_view = true;
-  gcc_assert (!new_view->m_parent_reg.null_p ());
-  gcc_assert (new_view->m_sval_id.null_p ());
-
-  //gcc_assert (new_view->get_type () != NULL_TREE);
-  // TODO: this can sometimes be NULL, when viewing through a (void *)
-
-  // TODO: the type ought to not be present yet
-
-  m_view_regs.safe_push (view_reg);
-}
-
-/* Look for a view of type TYPE of this region2, returning its id if found,
-   or null otherwise.  */
-
-region2_id
-region2::get_view (tree type, region_model2 *model) const
-{
-  unsigned i;
-  region2_id *view_reg;
-  FOR_EACH_VEC_ELT (m_view_regs, i, view_reg)
-    {
-      region2 *view = model->get_region2 (*view_reg);
-      gcc_assert (view->m_is_view);
-      if (view->get_type () == type)
-	return *view_reg;
-    }
-  return region2_id::null ();
-}
-#endif
-
-/* Return true if this is a symbolic region for deferencing an
-   unknown ptr.
-   We shouldn't attempt to bind values for this region (but
-   can unbind values for other regions).  */
-
-bool
-region2::symbolic_for_unknown_ptr_p () const
-{
-  if (const symbolic_region2 *sym_reg = dyn_cast_symbolic_region2 ())
-    if (sym_reg->get_pointer ()->get_kind () == svalue2::SK_UNKNOWN)
-      return true;
-  return false;
-}
-
-/* region2's ctor.  */
-
-region2::region2 (complexity c, unsigned id, const region2 *parent, tree type)
-: m_complexity (c), m_id (id), m_parent (parent), m_type (type),
-  m_cached_offset (NULL)
-{
-  gcc_assert (type == NULL_TREE || TYPE_P (type));
-}
-
-/* Base implementation of region2::add_to_hash vfunc; subclasses should
-   chain up to this.  */
-
-void
-region2::add_to_hash (inchash::hash &hstate) const
-{
-  /* We deliberatly omit the id, to avoid different IDs preventing
-     consolidation.  */
-  hstate.add_ptr (m_parent);
-  hstate.add_ptr (m_type);
-}
-
-/* FIXME.  */
-
-int
-region2::cmp_ptrs (const void *p1, const void *p2)
-{
-  const region2 * const *reg1 = (const region2 * const *)p1;
-  const region2 * const *reg2 = (const region2 * const *)p2;
-
-  return cmp_ids (*reg1, *reg2);
-}
-
-/* Determine if a pointer to this region2 must be non-NULL.
-
-   Generally, pointers to region2s must be non-NULL, but pointers
-   to symbolic_region2s might, in fact, be NULL.
-
-   This allows us to simulate functions like malloc and calloc with:
-   - only one "outcome" from each statement,
-   - the idea that the pointer is on the heap if non-NULL
-   - the possibility that the pointer could be NULL
-   - the idea that successive values returned from malloc are non-equal
-   - to be able to zero-fill for calloc.  */
-
-bool
-region2::non_null_p (const region_model2 &model) const
-{
-#if 1
-  /* FIXME: malloc is now simulated via a heap_allocated_region2.
-     That said, maybe we do want the possibilty of NULL for symbolic
-     regions too.  */
-  /* Are we within a symbolic_region2?  If so, it could be NULL, and we
-     have to fall back on the constraints.  */
-  if (const symbolic_region2 *sym_reg = dyn_cast_symbolic_region2 ())
-    {
-      return false; // TODO: really?
-    }
-  if (get_kind () == RK_HEAP_ALLOCATED)
-    return false;
-#endif
-
-  return true;
-}
-
-/* Comparator for trees to impose a deterministic ordering on
-   T1 and T2.  */
-
-static int
-tree_cmp (const_tree t1, const_tree t2)
-{
-  gcc_assert (t1);
-  gcc_assert (t2);
-
-  /* Test tree codes first.  */
-  if (TREE_CODE (t1) != TREE_CODE (t2))
-    return TREE_CODE (t1) - TREE_CODE (t2);
-
-  /* From this point on, we know T1 and T2 have the same tree code.  */
-
-  if (DECL_P (t1))
-    {
-      if (DECL_NAME (t1) && DECL_NAME (t2))
-	return strcmp (IDENTIFIER_POINTER (DECL_NAME (t1)),
-		       IDENTIFIER_POINTER (DECL_NAME (t2)));
-      else
-	{
-	  if (DECL_NAME (t1))
-	    return -1;
-	  else if (DECL_NAME (t2))
-	    return 1;
-	  else
-	    return DECL_UID (t1) - DECL_UID (t2);
-	}
-    }
-
-  switch (TREE_CODE (t1))
-    {
-    case SSA_NAME:
-      {
-	if (SSA_NAME_VAR (t1) && SSA_NAME_VAR (t2))
-	  {
-	    int var_cmp = tree_cmp (SSA_NAME_VAR (t1), SSA_NAME_VAR (t2));
-	    if (var_cmp)
-	      return var_cmp;
-	    return SSA_NAME_VERSION (t1) - SSA_NAME_VERSION (t2);
-	  }
-	else
-	  {
-	    if (SSA_NAME_VAR (t1))
-	      return -1;
-	    else if (SSA_NAME_VAR (t2))
-	      return 1;
-	    else
-	      return SSA_NAME_VERSION (t1) - SSA_NAME_VERSION (t2);
-	  }
-      }
-      break;
-
-    case INTEGER_CST:
-      return tree_int_cst_compare (t1, t2);
-
-    case REAL_CST:
-      {
-	const real_value *rv1 = TREE_REAL_CST_PTR (t1);
-	const real_value *rv2 = TREE_REAL_CST_PTR (t2);
-	if (real_compare (UNORDERED_EXPR, rv1, rv2))
-	  {
-	    /* Impose an arbitrary order on NaNs relative to other NaNs
-	       and to non-NaNs.  */
-	    if (int cmp_isnan = real_isnan (rv1) - real_isnan (rv2))
-	      return cmp_isnan;
-	    if (int cmp_issignaling_nan
-		  = real_issignaling_nan (rv1) - real_issignaling_nan (rv2))
-	      return cmp_issignaling_nan;
-	    return real_isneg (rv1) - real_isneg (rv2);
-	  }
-	if (real_compare (LT_EXPR, rv1, rv2))
-	  return -1;
-	if (real_compare (GT_EXPR, rv1, rv2))
-	  return 1;
-	return 0;
-      }
-
-    case STRING_CST:
-      return strcmp (TREE_STRING_POINTER (t1),
-		     TREE_STRING_POINTER (t2));
-
-    default:
-      gcc_unreachable ();
-      break;
-    }
-
-  gcc_unreachable ();
-
-  return 0;
-}
-
-/* qsort comparator for trees to impose a deterministic ordering on
-   P1 and P2.  */
-
-static int
-tree_cmp (const void *p1, const void *p2)
-{
-  const_tree t1 = *(const_tree const *)p1;
-  const_tree t2 = *(const_tree const *)p2;
-
-  return tree_cmp (t1, t2);
-}
-
-/* class frame_region2 : public space_region2.  */
-
-frame_region2::~frame_region2 ()
-{
-  for (map_t::iterator iter = m_locals.begin ();
-       iter != m_locals.end ();
-       ++iter)
-    delete (*iter).second;
-}
-
-void
-frame_region2::accept (visitor *v) const
-{
-  region2::accept (v);
-  if (m_calling_frame)
-    m_calling_frame->accept (v);
-}
-
-void
-frame_region2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  if (simple)
-    pp_printf (pp, "frame: %qs@%i", function_name (m_fun), get_stack_depth ());
-  else
-    pp_printf (pp, "frame_region2(%qs, index: %i, depth: %i)",
-	       function_name (m_fun), m_index, get_stack_depth ());
-}
-
-/* Compare the fields of this frame_region2 with OTHER, returning true
-   if they are equal.
-   For use by region2::operator==.  */
-
-bool
-frame_region2::compare_fields (const frame_region2 &other) const
-{
-#if 0
-  if (!map_region2::compare_fields (other))
-    return false;
-#endif
-  if (m_fun != other.m_fun)
-    return false;
-  if (m_index != other.m_index)
-    return false;
-  if (m_calling_frame != other.m_calling_frame)
-    return false;
-  /* We don't compare m_locals.  */
-  return true;
-}
-
-/* Implementation of region2::add_to_hash vfunc for frame_region2.  */
-#if 0
-void
-frame_region2::add_to_hash (inchash::hash &hstate) const
-{
-  map_region2::add_to_hash (hstate);
-  hstate.add_ptr (m_fun);
-  hstate.add_int (m_depth);
-}
-#endif
-
-const region2 *
-frame_region2::get_region_for_local (region_model2_manager *mgr,
-				     tree expr) const
-{
-  // TODO: could also check that VAR_DECLs are locals
-  gcc_assert (TREE_CODE (expr) == PARM_DECL
-	      || TREE_CODE (expr) == VAR_DECL
-	      || TREE_CODE (expr) == SSA_NAME
-	      || TREE_CODE (expr) == RESULT_DECL);
-
-  /* Ideally we'd use mutable here.  */
-  // TODO: when is it available?
-  map_t &mutable_locals = const_cast <map_t &> (m_locals);
-
-  if (region2 **slot = mutable_locals.get (expr))
-    return *slot;
-  decl_region2 *reg
-    = new decl_region2 (mgr->alloc_region_id (), this, expr);
-  mutable_locals.put (expr, reg);
-  return reg;
-}
-
-/* class globals_region2 : public space_region2.  */
-
-/* Compare the fields of this globals_region2 with OTHER, returning true
-   if they are equal.
-   For use by region2::operator==.  */
-#if 0
-bool
-globals_region2::compare_fields (const globals_region2 &other) const
-{
-  return map_region2::compare_fields (other);
-}
-#endif
-
-void
-globals_region2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  if (simple)
-    pp_string (pp, "::");
-  else
-    pp_string (pp, "globals");
-}
-
-/* class code_region2 : public map_region2.  */
-
-/* Compare the fields of this code_region2 with OTHER, returning true
-   if they are equal.
-   For use by region2::operator==.  */
-#if 0
-bool
-code_region2::compare_fields (const code_region2 &other) const
-{
-  return map_region2::compare_fields (other);
-}
-#endif
-
-/* FIXME.  */
-
-void
-code_region2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  if (simple)
-    pp_string (pp, "code region");
-  else
-    pp_string (pp, "code_region2()");
-}
-
-/* class function_region2 : public region2.  */
-
-/* Compare the fields of this function_region2 with OTHER, returning true
-   if they are equal.
-   For use by region2::operator==.  */
-#if 0
-bool
-function_region2::compare_fields (const function_region2 &other) const
-{
-  return map_region2::compare_fields (other);
-}
-#endif
-
-/* FIXME.  */
-
-void
-function_region2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  if (simple)
-    {
-      dump_quoted_tree (pp, m_fndecl);
-    }
-  else
-    {
-      pp_string (pp, "function_region2(");
-      dump_quoted_tree (pp, m_fndecl);
-      pp_string (pp, ")");
-    }
-}
-
-/* class label_region2 : public region2.  */
-
-/* FIXME.  */
-
-void
-label_region2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  if (simple)
-    {
-      dump_quoted_tree (pp, m_label);
-    }
-  else
-    {
-      pp_string (pp, "label_region2(");
-      dump_quoted_tree (pp, m_label);
-      pp_string (pp, ")");
-    }
-}
-
-/* class stack_region2 : public region2.  */
-
-void
-stack_region2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  if (simple)
-    pp_string (pp, "stack region");
-  else
-    pp_string (pp, "stack_region2()");
-}
-
-/* Compare the fields of this stack_region2 with OTHER, returning true
-   if they are equal.
-   For use by region2::operator==.  */
-#if 0
-bool
-stack_region2::compare_fields (const stack_region2 &other) const
-{
-  if (m_frame_regs.length () != other.m_frame_regs.length ())
-    return false;
-
-  int i;
-  region2_id *frame_reg;
-  FOR_EACH_VEC_ELT (m_frame_regs, i, frame_reg)
-    if (m_frame_regs[i] != other.m_frame_regs[i])
-      return false;
-
-  return true;
-}
-#endif
-
-/* Implementation of region2::add_to_hash vfunc for stack_region2.  */
-#if 0
-void
-stack_region2::add_to_hash (inchash::hash &hstate) const
-{
-  region2::add_to_hash (hstate);
-
-  int i;
-  region2_id *frame_reg;
-  FOR_EACH_VEC_ELT (m_frame_regs, i, frame_reg)
-    inchash::add (*frame_reg, hstate);
-}
-#endif
-
-/* Attempt to merge STACK_REGION2_A and STACK_REGION2_B using MERGER.
-   Return true if the merger is possible, false otherwise.  */
-#if 0
-bool
-stack_region2::can_merge_p (const stack_region2 *stack_region2_a,
-			   const stack_region2 *stack_region2_b,
-			   model_merger *merger)
-{
-  if (stack_region2_a->get_num_frames ()
-      != stack_region2_b->get_num_frames ())
-    return false;
-
-  region_model2 *merged_model = merger->m_merged_model;
-
-  region2_id rid_merged_stack
-    = merged_model->get_root_region2 ()->ensure_stack_region2 (merged_model);
-
-  stack_region2 *merged_stack
-    = merged_model->get_region2 <stack_region2> (rid_merged_stack);
-
-  /* First, create all frames in the merged model, without populating them.
-     The merging code assumes that all frames in the merged model already exist,
-     so we have to do this first to handle the case in which a local in an
-     older frame points at a local in a more recent frame.  */
-    for (unsigned i = 0; i < stack_region2_a->get_num_frames (); i++)
-      {
-	region2_id rid_a = stack_region2_a->get_frame_reg (i);
-	frame_region2 *frame_a = merger->get_region2_a <frame_region2> (rid_a);
-
-	region2_id rid_b = stack_region2_b->get_frame_reg (i);
-	frame_region2 *frame_b = merger->get_region2_b <frame_region2> (rid_b);
-
-	if (frame_a->get_function () != frame_b->get_function ())
-	  return false;
-
-	frame_region2 *merged_frame = new frame_region2 (rid_merged_stack,
-						       frame_a->get_function (),
-						       frame_a->get_depth ());
-	region2_id rid_merged_frame = merged_model->add_region2 (merged_frame);
-	merged_stack->push_frame (rid_merged_frame);
-      }
-
-    /* Now populate the frames we created.  */
-    for (unsigned i = 0; i < stack_region2_a->get_num_frames (); i++)
-      {
-	region2_id rid_a = stack_region2_a->get_frame_reg (i);
-	frame_region2 *frame_a = merger->get_region2_a <frame_region2> (rid_a);
-
-	region2_id rid_b = stack_region2_b->get_frame_reg (i);
-	frame_region2 *frame_b = merger->get_region2_b <frame_region2> (rid_b);
-
-	region2_id rid_merged_frame = merged_stack->get_frame_reg (i);
-	frame_region2 *merged_frame
-	  = merged_model->get_region2 <frame_region2> (rid_merged_frame);
-	if (!map_region2::can_merge_p (frame_a, frame_b,
-				      merged_frame, rid_merged_frame,
-				      merger))
-	  return false;
-      }
-
-  return true;
-}
-#endif
-
-/* For debugging purposes: look for a grandchild region2 within one of
-   the child frame region2s, where the grandchild is for a decl named
-   IDENTIFIER (or an SSA_NAME for such a decl):
-
-     stack_region2
-     `-frame_region2
-       `-region2 for decl named IDENTIFIER
-
-   returning its value, or svalue2_id::null if none are found.  */
-#if 0
-svalue2_id
-stack_region2::get_value_by_name (tree identifier,
-				 const region_model2 &model) const
-{
-  int i;
-  region2_id *frame_reg;
-  FOR_EACH_VEC_ELT (m_frame_regs, i, frame_reg)
-    {
-      frame_region2 *frame = model.get_region2<frame_region2> (*frame_reg);
-      const svalue2 *sval = frame->get_value_by_name (identifier, model);
-      if (!sid.null_p ())
-	return sid;
-    }
-
-  return svalue2_id::null ();
-}
-#endif
-
-/* class heap_region2 : public region2.  */
-
-/* Compare the fields of this heap_region2 with OTHER, returning true
-   if they are equal.
-   For use by region2::operator==.  */
-#if 0
-bool
-heap_region2::compare_fields (const heap_region2 &) const
-{
-  /* Empty.  */
-  return true;
-}
-#endif
-
-void
-heap_region2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  if (simple)
-    pp_string (pp, "heap region");
-  else
-    pp_string (pp, "heap_region2()");
-}
-
-/* class root_region2 : public region2.  */
-
-/* root_region2's default ctor.  */
-
-root_region2::root_region2 (unsigned id)
-: region2 (complexity (1, 1), id, NULL, NULL_TREE)
-{
-}
-
-#if 0
-/* Compare the fields of this root_region2 with OTHER, returning true
-   if they are equal.
-   For use by region2::operator==.  */
-
-bool
-root_region2::compare_fields (const root_region2 &other) const
-{
-  if (m_stack_reg != other.m_stack_reg)
-    return false;
-  if (m_globals_reg != other.m_globals_reg)
-    return false;
-  if (m_code_reg != other.m_code_reg)
-    return false;
-  if (m_heap_reg != other.m_heap_reg)
-    return false;
-  return true;
-}
-#endif
-
-void
-root_region2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  if (simple)
-    pp_string (pp, "root region");
-  else
-    pp_string (pp, "root_region2()");
-}
-
-/* Create a new frame_region2 for a call to FUN and push it onto
-   the stack.
-
-   If ARG_SVALS is non-NULL, use it to populate the parameters
-   in the new frame.
-   Otherwise, populate them with unknown values.
-
-   Return the region2_id of the new frame.  */
-#if 0
-region2_id
-root_region2::push_frame (region_model2 *model, function *fun,
-			 vec<svalue2_id> *arg_svals,
-			 region_model2_context *ctxt)
-{
-  gcc_assert (fun);
-  /* arg_svals can be NULL.  */
-
-  ensure_stack_region2 (model);
-  stack_region2 *stack = model->get_region2 <stack_region2> (m_stack_reg);
-
-  frame_region2 *region2 = new frame_region2 (m_stack_reg, fun,
-					   stack->get_num_frames ());
-  region2_id frame_reg = model->add_region2 (region2);
-
-  // TODO: unify these cases by building a vec of unknown?
-
-  if (arg_svals)
-    {
-      /* Arguments supplied from a caller frame.  */
-
-      tree fndecl = fun->decl;
-      unsigned idx = 0;
-      for (tree iter_parm = DECL_ARGUMENTS (fndecl); iter_parm;
-	   iter_parm = DECL_CHAIN (iter_parm), ++idx)
-	{
-	  /* If there's a mismatching declaration, the call stmt might
-	     not have enough args.  Handle this case by leaving the
-	     rest of the params as uninitialized.  */
-	  if (idx >= arg_svals->length ())
-	    break;
-	  svalue2_id arg_sval = (*arg_svals)[idx];
-	  region2_id parm_reg
-	    = region2->get_or_create (model, frame_reg, iter_parm,
-				     TREE_TYPE (iter_parm), ctxt);
-	  model->set_value (parm_reg, arg_sval, ctxt);
-
-	  /* Also do it for default SSA name (sharing the same unknown
-	     value).  */
-	  tree parm_default_ssa = ssa_default_def (fun, iter_parm);
-	  if (parm_default_ssa)
-	    {
-	      region2_id defssa_reg
-		= region2->get_or_create (model, frame_reg, parm_default_ssa,
-					 TREE_TYPE (iter_parm), ctxt);
-	      model->set_value (defssa_reg, arg_sval, ctxt);
-	    }
-	}
-    }
-  else
-    {
-      /* No known arguments (a top-level call within the analysis).  */
-
-      /* Params have a defined, unknown value; they should not inherit
-	 from the poisoned uninit value.  */
-      tree fndecl = fun->decl;
-      for (tree iter_parm = DECL_ARGUMENTS (fndecl); iter_parm;
-	   iter_parm = DECL_CHAIN (iter_parm))
-	{
-	  region2_id parm_reg
-	    = region2->get_or_create (model, frame_reg, iter_parm,
-				     TREE_TYPE (iter_parm), ctxt);
-	  svalue2_id parm_sval
-	    = model->set_to_new_unknown_value (parm_reg, TREE_TYPE (iter_parm),
-					       ctxt);
-
-	  /* Also do it for default SSA name (sharing the same unknown
-	     value).  */
-	  tree parm_default_ssa = ssa_default_def (fun, iter_parm);
-	  if (parm_default_ssa)
-	    {
-	      region2_id defssa_reg
-		= region2->get_or_create (model, frame_reg, parm_default_ssa,
-					 TREE_TYPE (iter_parm), ctxt);
-	      model->get_region2 (defssa_reg)->set_value (*model, defssa_reg,
-							 parm_sval, ctxt);
-	    }
-	}
-    }
-
-  stack->push_frame (frame_reg);
-
-  return frame_reg;
-}
-
-/* Return the region2_id of the stack region2, creating it if doesn't
-   already exist.  */
-
-region2_id
-root_region2::ensure_stack_region2 (region_model2 *model)
-{
-  if (m_stack_reg.null_p ())
-    {
-      svalue2_id uninit_sval
-	= model->add_svalue2 (new poisoned_svalue2 (POISON_KIND_UNINIT,
-						  NULL_TREE));
-      m_stack_reg
-	= model->add_region2 (new stack_region2 (model->get_root_reg (),
-					       uninit_sval));
-    }
-  return m_stack_reg;
-}
-
-/* Return the region2_id of the heap region2, creating it if doesn't
-   already exist.  */
-
-region2_id
-root_region2::ensure_heap_region2 (region_model2 *model)
-{
-  if (m_heap_reg.null_p ())
-    {
-      svalue2_id uninit_sval
-	= model->add_svalue2 (new poisoned_svalue2 (POISON_KIND_UNINIT,
-						  NULL_TREE));
-      m_heap_reg
-	= model->add_region2 (new heap_region2 (model->get_root_reg (),
-					      uninit_sval));
-    }
-  return m_heap_reg;
-}
-
-/* Attempt to merge ROOT_REGION2_A and ROOT_REGION2_B into
-   MERGED_ROOT_REGION2 using MERGER.
-   Return true if the merger is possible, false otherwise.  */
-
-bool
-root_region2::can_merge_p (const root_region2 *root_region2_a,
-			  const root_region2 *root_region2_b,
-			  root_region2 *merged_root_region2,
-			  model_merger *merger)
-{
-  /* We can only merge if the stacks are sufficiently similar.  */
-  stack_region2 *stack_a = root_region2_a->get_stack_region2 (merger->m_model_a);
-  stack_region2 *stack_b = root_region2_b->get_stack_region2 (merger->m_model_b);
-  if (stack_a && stack_b)
-    {
-      /* If the two models both have a stack, attempt to merge them.  */
-      merged_root_region2->ensure_stack_region2 (merger->m_merged_model);
-      if (!stack_region2::can_merge_p (stack_a, stack_b, merger))
-	return false;
-    }
-  else if (stack_a || stack_b)
-    /* Don't attempt to merge if one model has a stack and the other
-       doesn't.  */
-    return false;
-
-  map_region2 *globals_a = root_region2_a->get_globals_region2 (merger->m_model_a);
-  map_region2 *globals_b = root_region2_b->get_globals_region2 (merger->m_model_b);
-  if (globals_a && globals_b)
-    {
-      /* If both models have globals region2s, attempt to merge them.  */
-      region2_id merged_globals_reg
-	= merged_root_region2->ensure_globals_region2 (merger->m_merged_model);
-      map_region2 *merged_globals
-	= merged_root_region2->get_globals_region2 (merger->m_merged_model);
-      if (!map_region2::can_merge_p (globals_a, globals_b,
-				    merged_globals, merged_globals_reg,
-				    merger))
-	return false;
-    }
-  /* otherwise, merge as "no globals".  */
-
-  map_region2 *code_a = root_region2_a->get_code_region2 (merger->m_model_a);
-  map_region2 *code_b = root_region2_b->get_code_region2 (merger->m_model_b);
-  if (code_a && code_b)
-    {
-      /* If both models have code region2s, attempt to merge them.  */
-      region2_id merged_code_reg
-	= merged_root_region2->ensure_code_region2 (merger->m_merged_model);
-      map_region2 *merged_code
-	= merged_root_region2->get_code_region2 (merger->m_merged_model);
-      if (!map_region2::can_merge_p (code_a, code_b,
-				    merged_code, merged_code_reg,
-				    merger))
-	return false;
-    }
-  /* otherwise, merge as "no code".  */
-
-  heap_region2 *heap_a = root_region2_a->get_heap_region2 (merger->m_model_a);
-  heap_region2 *heap_b = root_region2_b->get_heap_region2 (merger->m_model_b);
-  if (heap_a && heap_b)
-    {
-      /* If both have a heap, create a "merged" heap.
-	 Actually merging the heap contents happens via the region_svalue2
-	 instances, as needed, when seeing pairs of region_svalue2 instances.  */
-      merged_root_region2->ensure_heap_region2 (merger->m_merged_model);
-    }
-  /* otherwise, merge as "no heap".  */
-
-  return true;
-}
-#endif
-
-/* Implementation of region2::add_to_hash vfunc for root_region2.  */
-#if 0
-void
-root_region2::add_to_hash (inchash::hash &hstate) const
-{
-  region2::add_to_hash (hstate);
-  inchash::add (m_stack_reg, hstate);
-  inchash::add (m_globals_reg, hstate);
-  inchash::add (m_code_reg, hstate);
-  inchash::add (m_heap_reg, hstate);
-}
-#endif
-
-/* For debugging purposes: look for a descendant region2 for a local
-   or global decl named IDENTIFIER (or an SSA_NAME for such a decl),
-   returning its value, or svalue2_id::null if none are found.  */
-#if 0
-svalue2_id
-root_region2::get_value_by_name (tree identifier,
-				const region_model2 &model) const
-{
-  if (stack_region2 *stack = get_stack_region2 (&model))
-    {
-      const svalue2 *sval = stack->get_value_by_name (identifier, model);
-      if (!sid.null_p ())
-	return sid;
-    }
-  if (map_region2 *globals = get_globals_region2 (&model))
-    {
-      const svalue2 *sval = globals->get_value_by_name (identifier, model);
-      if (!sid.null_p ())
-	return sid;
-    }
-  return svalue2_id::null ();
-}
-#endif
-
-/* class symbolic_region2 : public map_region2.  */
-
-/* Compare the fields of this symbolic_region2 with OTHER, returning true
-   if they are equal.
-   For use by region2::operator==.  */
-
-bool
-symbolic_region2::compare_fields (const symbolic_region2 &other) const
-{
-  return m_sval_ptr == other.m_sval_ptr;
-}
-
-/* FIXME.  */
-
-void
-symbolic_region2::accept (visitor *v) const
-{
-  region2::accept (v);
-  m_sval_ptr->accept (v);
-}
-
-/* FIXME.  */
-
-void
-symbolic_region2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  if (simple)
-    {
-      pp_string (pp, "(*");
-      m_sval_ptr->dump_to_pp (pp, simple);
-      pp_string (pp, ")");
-    }
-  else
-    {
-      pp_string (pp, "symbolic_region2(");
-      get_parent_region ()->dump_to_pp (pp, simple);
-      pp_string (pp, ", ");
-      print_quoted_type (pp, get_type ());
-      pp_string (pp, ", ");
-      m_sval_ptr->dump_to_pp (pp, simple);
-      pp_string (pp, ")");
-    }
-}
-
-/* class decl_region2 : public region2.  */
-
-void
-decl_region2::add_to_hash (inchash::hash &hstate) const
-{
-  region2::add_to_hash (hstate);
-  hstate.add_ptr (m_decl);
-}
-
-bool
-decl_region2::compare_fields (const decl_region2 &other) const
-{
-  return m_decl == other.m_decl;
-}
-
-void
-decl_region2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  if (simple)
-    pp_printf (pp, "%E", m_decl);
-  else
-    {
-      pp_string (pp, "decl_region2(");
-      get_parent_region ()->dump_to_pp (pp, simple);
-      pp_string (pp, ", ");
-      print_quoted_type (pp, get_type ());
-      pp_printf (pp, ", %qE)", m_decl);
-    }
-}
-
-/* FIXME.  */
-
-int
-decl_region2::get_stack_depth () const
-{
-  if (get_parent_region () == NULL)
-    return 0;
-  if (const frame_region2 *frame_reg
-	= get_parent_region ()->dyn_cast_frame_region2 ())
-    return frame_reg->get_stack_depth ();
-  return 0;
-}
-
-/* class field_region2 : public region2.  */
-
-void
-field_region2::add_to_hash (inchash::hash &hstate) const
-{
-  region2::add_to_hash (hstate);
-  hstate.add_ptr (m_field);
-}
-
-bool
-field_region2::compare_fields (const field_region2 &other) const
-{
-  return m_field == other.m_field;
-}
-
-void
-field_region2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  if (simple)
-    {
-      get_parent_region ()->dump_to_pp (pp, simple);
-      pp_string (pp, ".");
-      pp_printf (pp, "%E", m_field);
-    }
-  else
-    {
-      pp_string (pp, "field_region2(");
-      get_parent_region ()->dump_to_pp (pp, simple);
-      pp_string (pp, ", ");
-      print_quoted_type (pp, get_type ());
-      pp_printf (pp, ", %qE)", m_field);
-    }
-}
-
-/* class element_region2 : public region2.  */
-
-bool
-element_region2::compare_fields (const element_region2 &other) const
-{
-  return m_index == other.m_index;
-}
-
-/* FIXME.  */
-
-void
-element_region2::accept (visitor *v) const
-{
-  region2::accept (v);
-  m_index->accept (v);
-}
-
-void
-element_region2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  if (simple)
-    {
-      //pp_string (pp, "(");
-      get_parent_region ()->dump_to_pp (pp, simple);
-      pp_string (pp, "[");
-      m_index->dump_to_pp (pp, simple);
-      pp_string (pp, "]");
-      //pp_string (pp, ")");
-    }
-  else
-    {
-      pp_string (pp, "element_region2(");
-      get_parent_region ()->dump_to_pp (pp, simple);
-      pp_string (pp, ", ");
-      print_quoted_type (pp, get_type ());
-      pp_string (pp, ", ");
-      m_index->dump_to_pp (pp, simple);
-      pp_printf (pp, ")");
-    }
-}
-
-void
-element_region2::add_to_hash (inchash::hash &hstate) const
-{
-  region2::add_to_hash (hstate);
-  hstate.add_ptr (m_index);
-}
-
-/* class offset_region2 : public region2.  */
-
-bool
-offset_region2::compare_fields (const offset_region2 &other) const
-{
-  return m_byte_offset == other.m_byte_offset;
-}
-
-/* FIXME.  */
-
-void
-offset_region2::accept (visitor *v) const
-{
-  region2::accept (v);
-  m_byte_offset->accept (v);
-}
-
-void
-offset_region2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  if (simple)
-    {
-      //pp_string (pp, "(");
-      get_parent_region ()->dump_to_pp (pp, simple);
-      pp_string (pp, "+");
-      m_byte_offset->dump_to_pp (pp, simple);
-      //pp_string (pp, ")");
-    }
-  else
-    {
-      pp_string (pp, "offset_region2(");
-      get_parent_region ()->dump_to_pp (pp, simple);
-      pp_string (pp, ", ");
-      print_quoted_type (pp, get_type ());
-      pp_string (pp, ", ");
-      m_byte_offset->dump_to_pp (pp, simple);
-      pp_printf (pp, ")");
-    }
-}
-
-void
-offset_region2::add_to_hash (inchash::hash &hstate) const
-{
-  region2::add_to_hash (hstate);
-  hstate.add_ptr (m_byte_offset);
-}
-
-/* class cast_region2 : public region2.  */
-
-bool
-cast_region2::compare_fields (const cast_region2 &other) const
-{
-  return m_original_region == other.m_original_region;
-}
-
-/* FIXME.  */
-
-void
-cast_region2::accept (visitor *v) const
-{
-  region2::accept (v);
-  m_original_region->accept (v);
-}
-
-void
-cast_region2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  if (simple)
-    {
-      pp_string (pp, "CAST_REG(");
-      print_quoted_type (pp, get_type ());
-      pp_string (pp, ", ");
-      m_original_region->dump_to_pp (pp, simple);
-      pp_string (pp, ")");
-    }
-  else
-    {
-      pp_string (pp, "cast_region2(");
-      m_original_region->dump_to_pp (pp, simple);
-      pp_string (pp, ", ");
-      print_quoted_type (pp, get_type ());
-      pp_printf (pp, ")");
-    }
-}
-
-void
-cast_region2::add_to_hash (inchash::hash &hstate) const
-{
-  region2::add_to_hash (hstate);
-  hstate.add_ptr (m_original_region);
-}
-
-/* class heap_allocated_region2 : public region2.  */
-
-void
-heap_allocated_region2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  // TODO:
-  if (simple)
-    pp_printf (pp, "HEAP_ALLOCATED_REGION(%i)", get_id ());
-  else
-    pp_printf (pp, "heap_allocated_region2(%i)", get_id ());
-}
-
-/* class alloca_region2 : public region2.  */
-
-void
-alloca_region2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  // TODO:
-  if (simple)
-    pp_string (pp, "ALLOCA_REGION");
-  else
-    pp_string (pp, "alloca_region2()");
-}
-
-/* class string_region2 : public region2.  */
-
-void
-string_region2::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  // TODO:
-  if (simple)
-    dump_tree (pp, m_string_cst);
-  else
-    {
-      pp_string (pp, "string_region2(");
-      dump_tree (pp, m_string_cst);
-      pp_string (pp, " (");
-      pp_pointer (pp, m_string_cst);
-      pp_string (pp, "))");
-    }
-}
-
-/* class unknown_region2 : public region2.  */
-
-void
-unknown_region2::dump_to_pp (pretty_printer *pp, bool /*simple*/) const
-{
-  pp_string (pp, "UNKNOWN_REGION");
-}
-
-/* class region_model2_manager.  */
-
-region_model2_manager::region_model2_manager ()
-: m_next_region_id (0),
-  m_root_region (alloc_region_id ()),
-  m_stack_region (alloc_region_id (), &m_root_region),
-  m_heap_region (alloc_region_id (), &m_root_region),
-  m_unknown_NULL (NULL),
-  m_max_complexity (0, 0),
-  m_code_region (alloc_region_id (), &m_root_region),
-  m_fndecls_map (), m_labels_map (),
-  m_globals_region (alloc_region_id (), &m_root_region),
-  m_globals_map (),
-  m_store2_mgr (this)
-{
-  // TODO
-}
-
-region_model2_manager::~region_model2_manager ()
-{
-  /* Delete consolidated svalues.  */
-  for (constants_map_t::iterator iter = m_constants_map.begin ();
-       iter != m_constants_map.end (); ++iter)
-    delete (*iter).second;
-  for (unknowns_map_t::iterator iter = m_unknowns_map.begin ();
-       iter != m_unknowns_map.end (); ++iter)
-    delete (*iter).second;
-  delete m_unknown_NULL;
-  for (setjmp_values_map_t::iterator iter = m_setjmp_values_map.begin ();
-       iter != m_setjmp_values_map.end (); ++iter)
-    delete (*iter).second;
-  for (poisoned_values_map_t::iterator iter = m_poisoned_values_map.begin ();
-       iter != m_poisoned_values_map.end (); ++iter)
-    delete (*iter).second;
-  for (initial_values_map_t::iterator iter = m_initial_values_map.begin ();
-       iter != m_initial_values_map.end (); ++iter)
-    delete (*iter).second;
-  for (pointer_values_map_t::iterator iter = m_pointer_values_map.begin ();
-       iter != m_pointer_values_map.end (); ++iter)
-    delete (*iter).second;
-  for (unaryop_values_map_t::iterator iter = m_unaryop_values_map.begin ();
-       iter != m_unaryop_values_map.end (); ++iter)
-    delete (*iter).second;
-  for (binop_values_map_t::iterator iter = m_binop_values_map.begin ();
-       iter != m_binop_values_map.end (); ++iter)
-    delete (*iter).second;
-  for (sub_values_map_t::iterator iter = m_sub_values_map.begin ();
-       iter != m_sub_values_map.end (); ++iter)
-    delete (*iter).second;
-  for (unmergeable_values_map_t::iterator iter
-	 = m_unmergeable_values_map.begin ();
-       iter != m_unmergeable_values_map.end (); ++iter)
-    delete (*iter).second;
-  for (widening_values_map_t::iterator iter = m_widening_values_map.begin ();
-       iter != m_widening_values_map.end (); ++iter)
-    delete (*iter).second;
-  for (compound_values_map_t::iterator iter = m_compound_values_map.begin ();
-       iter != m_compound_values_map.end (); ++iter)
-    delete (*iter).second;
-  for (conjured_values_map_t::iterator iter = m_conjured_values_map.begin ();
-       iter != m_conjured_values_map.end (); ++iter)
-    delete (*iter).second;
-
-  /* Delete consolidated regions.  */
-  for (fndecls_map_t::iterator iter = m_fndecls_map.begin ();
-       iter != m_fndecls_map.end (); ++iter)
-    delete (*iter).second;
-  for (labels_map_t::iterator iter = m_labels_map.begin ();
-       iter != m_labels_map.end (); ++iter)
-    delete (*iter).second;
-  for (globals_map_t::iterator iter = m_globals_map.begin ();
-       iter != m_globals_map.end (); ++iter)
-    delete (*iter).second;
-  for (string_map_t::iterator iter = m_string_map.begin ();
-       iter != m_string_map.end (); ++iter)
-    delete (*iter).second;
-}
-
-/* Return true if C exceeds the complexity limit for svalues.  */
-
-bool
-region_model2_manager::too_complex_p (const complexity &c) const
-{
-  /* TODO: check both num nodes and max_depth
-     TODO: use params.  */
-#if 0
-  if (c.m_num_nodes > 50)
-    return true;
-#endif
-#if 1
-  if (c.m_max_depth > param_analyzer_max_svalue_depth)
-    return true;
-#endif
-  return false;
-}
-
-/* If SVAL exceeds the complexity limit for svalues, delete it
-   and return true.
-   Otherwise update m_max_complexity and return false.  */
-
-bool
-region_model2_manager::reject_if_too_complex (svalue2 *sval)
-{
-  const complexity &c = sval->get_complexity ();
-  if (!too_complex_p (c))
-    {
-      if (m_max_complexity.m_num_nodes < c.m_num_nodes)
-	m_max_complexity.m_num_nodes = c.m_num_nodes;
-      if (m_max_complexity.m_max_depth < c.m_max_depth)
-	m_max_complexity.m_max_depth = c.m_max_depth;
-      return false;
-    }
-
-  /* TODO.  */
-  if (0)
-    {
-      pretty_printer pp;
-      pp_format_decoder (&pp) = default_tree_printer;
-      sval->dump_to_pp (&pp, true);
-      warning (0, "svalue is too complicated: %qs", pp_formatted_text (&pp));
-    }
-
-  delete sval;
-
-  return true;
-}
-
-/* Macro for imposing a complexity limit on svalues, for use within
-   region_model2_manager member functions.
-
-   If SVAL is exceeds the complexity limit, delete it and return an UNKNOWN
-   value of the same type.
-   Otherwise update m_max_complexity and carry on.  */
-
-#define RETURN_UNKNOWN_IF_TOO_COMPLEX(SVAL)			\
-  do {								\
-    svalue2 *sval_ = (SVAL);					\
-    tree type_ = sval_->get_type ();				\
-    if (reject_if_too_complex (sval_))				\
-      return get_or_create_unknown_svalue2 (type_);		\
-  } while (0)
-
-/* svalue2 consolidation.  */
-
-/* Return an svalue2 *for a constant_svalue2 for CST_EXPR,
-   creating the constant_svalue2 if necessary.
-   The constant_svalue2 instances are reused, based on pointer equality
-   of trees  */
-const svalue2 *
-region_model2_manager::get_or_create_constant_svalue2 (tree cst_expr)
-{
-  gcc_assert (cst_expr);
-
-  constant_svalue2 **slot = m_constants_map.get (cst_expr);
-  if (slot)
-    return *slot;
-  constant_svalue2 *cst_sval = new constant_svalue2 (cst_expr);
-  RETURN_UNKNOWN_IF_TOO_COMPLEX (cst_sval);
-  m_constants_map.put (cst_expr, cst_sval);
-  return cst_sval;
-  // TODO: cst equality vs tree pointer equality?
-}
-
-/* Return an svalue2 *for a unknown_svalue2 for TYPE (which can be NULL),
-   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)
-{
-  /* FIXME: remove this special case in favor of fixing traits to
-     allow for a NULL key (as opposed to it being the "empty" value) .  */
-  if (type == NULL_TREE)
-    {
-      if (!m_unknown_NULL)
-	m_unknown_NULL = new unknown_svalue2 (type);
-      return m_unknown_NULL;
-    }
-
-  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)
-{
-  /* The initial value of a cast is a cast of the initial value.  */
-  if (const cast_region2 *cast_reg = reg->dyn_cast_cast_region2 ())
-    {
-      const region2 *original_reg = cast_reg->get_original_region ();
-      return get_or_create_cast (cast_reg->get_type (),
-				 get_or_create_initial_value (original_reg));
-    }
-
-  if (initial_svalue2 **slot = m_initial_values_map.get (reg))
-    return *slot;
-  initial_svalue2 *initial_sval = new initial_svalue2 (reg->get_type (), reg);
-  RETURN_UNKNOWN_IF_TOO_COMPLEX (initial_sval);
-  m_initial_values_map.put (reg, initial_sval);
-  return initial_sval;
-}
-
-const svalue2 *
-region_model2_manager::get_or_create_setjmp_svalue2 (const setjmp_record &r,
-						     tree type)
-{
-  setjmp_svalue2::key_t key (r, type);
-  if (setjmp_svalue2 **slot = m_setjmp_values_map.get (key))
-    return *slot;
-  setjmp_svalue2 *setjmp_sval = new setjmp_svalue2 (r, type);
-  RETURN_UNKNOWN_IF_TOO_COMPLEX (setjmp_sval);
-  m_setjmp_values_map.put (key, setjmp_sval);
-  return setjmp_sval;
-}
-
-const svalue2 *
-region_model2_manager::get_or_create_poisoned_svalue2 (enum poison_kind kind,
-						       tree type)
-{
-  poisoned_svalue2::key_t key (kind, type);
-  if (poisoned_svalue2 **slot = m_poisoned_values_map.get (key))
-    return *slot;
-  poisoned_svalue2 *poisoned_sval = new poisoned_svalue2 (kind, type);
-  RETURN_UNKNOWN_IF_TOO_COMPLEX (poisoned_sval);
-  m_poisoned_values_map.put (key, poisoned_sval);
-  return poisoned_sval;
-}
-
-const svalue2 *
-region_model2_manager::get_ptr_svalue2 (tree ptr_type, const region2 *pointee)
-{
-  /* If this is a symbolic region from dereferencing a pointer, and the types
-     match, then return the original pointer.  */
-  if (const symbolic_region2 *sym_reg = pointee->dyn_cast_symbolic_region2 ())
-    if (ptr_type == sym_reg->get_pointer ()->get_type ())
-      return sym_reg->get_pointer ();
-
-  // FIXME: use ptr_type here:
-  if (region_svalue2 **slot = m_pointer_values_map.get (pointee))
-    return *slot;
-  region_svalue2 *sval = new region_svalue2 (ptr_type, pointee);
-  RETURN_UNKNOWN_IF_TOO_COMPLEX (sval);
-  m_pointer_values_map.put (pointee, sval);
-  return sval;
-}
-
-const svalue2 *
-region_model2_manager::maybe_fold_unaryop (tree type, enum tree_code op,
-					   const svalue2 *arg)
-{
-  /* Ops on "unknown" are also unknown.  */
-  if (arg->get_kind () == svalue2::SK_UNKNOWN)
-    return get_or_create_unknown_svalue2 (type);
-
-  switch (op)
-    {
-    default: break;
-    case NOP_EXPR:
-      {
-	/* Handle redundant casts.  */
-	if (arg->get_type ()
-	    && useless_type_conversion_p (arg->get_type (), type))
-	  return arg;
-
-	/* Fold "cast<TYPE> (cast <INNER_TYPE> (innermost_arg))
-	     => "cast<TYPE> (innermost_arg)",
-	   unless INNER_TYPE is narrower than TYPE.  */
-	if (const svalue2 *innermost_arg = arg->maybe_undo_cast ())
-	  {
-	    tree inner_type = arg->get_type ();
-	    if (TYPE_PRECISION (type) <= TYPE_PRECISION (inner_type))
-	      return maybe_fold_unaryop (type, op, innermost_arg);
-	  }
-      }
-      break;
-    case TRUTH_NOT_EXPR:
-      {
-	/* Invert comparisons e.g. "!(x == y)" => "x != y".  */
-	if (const binop_svalue2 *binop = arg->dyn_cast_binop_svalue2 ())
-	  if (TREE_CODE_CLASS (binop->get_op ()) == tcc_comparison)
-	    {
-	      enum tree_code inv_op
-		= invert_tree_comparison (binop->get_op (),
-					  HONOR_NANS (binop->get_type ()));
-	      if (inv_op != ERROR_MARK)
-		return get_or_create_binop (binop->get_type (), inv_op,
-					    binop->get_arg0 (),
-					    binop->get_arg1 ());
-	    }
-      }
-      break;
-    }
-
-  /* Constants.  */
-  if (tree cst = arg->maybe_get_constant ())
-    if (tree result = fold_unary (op, type, cst))
-      return get_or_create_constant_svalue2 (result);
-
-  return NULL;
-}
-
-const svalue2 *
-region_model2_manager::get_or_create_unaryop (tree type, enum tree_code op,
-					      const svalue2 *arg)
-{
-  if (const svalue2 *folded = maybe_fold_unaryop  (type, op, arg))
-    return folded;
-  unaryop_svalue2::key_t key (type, op, arg);
-  if (unaryop_svalue2 **slot = m_unaryop_values_map.get (key))
-    return *slot;
-  unaryop_svalue2 *unaryop_sval = new unaryop_svalue2 (type, op, arg);
-  RETURN_UNKNOWN_IF_TOO_COMPLEX (unaryop_sval);
-  m_unaryop_values_map.put (key, unaryop_sval);
-  return unaryop_sval;
-}
-
-const svalue2 *
-region_model2_manager::get_or_create_cast (tree type, const svalue2 *arg)
-{
-  return get_or_create_unaryop (type, NOP_EXPR, arg);
-}
-
-const svalue2 *
-region_model2_manager::maybe_fold_binop (tree type, enum tree_code op,
-					 const svalue2 *arg0,
-					 const svalue2 *arg1)
-{
-  tree cst0 = arg0->maybe_get_constant ();
-  tree cst1 = arg1->maybe_get_constant ();
-  /* (CST OP CST).  */
-  if (cst0 && cst1)
-    {
-      if (tree result = fold_binary (op, type, cst0, cst1))
-	if (CONSTANT_CLASS_P (result))
-	  return get_or_create_constant_svalue2 (result);
-    }
-
-  if (FLOAT_TYPE_P (type)
-      || (arg0->get_type () && FLOAT_TYPE_P (arg0->get_type ()))
-      || (arg1->get_type () && FLOAT_TYPE_P (arg1->get_type ())))
-    return NULL;
-
-  switch (op)
-    {
-    default:
-      break;
-    case POINTER_PLUS_EXPR:
-    case PLUS_EXPR:
-      /* (VAL + 0) -> VAL.  */
-      if (cst1 && zerop (cst1) && type == arg0->get_type ())
-	return arg0;
-      break;
-    case MINUS_EXPR:
-      /* (VAL - 0) -> VAL.  */
-      if (cst1 && zerop (cst1) && type == arg0->get_type ())
-	return arg0;
-      break;
-    case MULT_EXPR:
-      /* (VAL * 0).  */
-      if (cst1 && zerop (cst1))
-	// FIXME: check for integer type
-	return get_or_create_constant_svalue2 (build_int_cst (type, 0));
-      /* (VAL * 1) -> VAL.  */
-      if (cst1 && integer_onep (cst1))
-	return arg0;
-      break;
-    case TRUTH_ANDIF_EXPR:
-    case TRUTH_AND_EXPR:
-      if (cst1)
-	{
-	  if (zerop (cst1))
-	    /* "(ARG0 && 0)" -> "0".  */
-	    return get_or_create_constant_svalue2 (build_int_cst (type, 0));
-	  else
-	    /* "(ARG0 && nonzero-cst)" -> "ARG0".  */
-	    return get_or_create_cast (type, arg0);
-	}
-      break;
-    case TRUTH_ORIF_EXPR:
-    case TRUTH_OR_EXPR:
-      if (cst1)
-	{
-	  if (zerop (cst1))
-	    /* "(ARG0 || 0)" -> "ARG0".  */
-	    return get_or_create_cast (type, arg0);
-	  else
-	    /* "(ARG0 && nonzero-cst)" -> "nonzero-cst".  */
-	    return get_or_create_cast (type, arg1);
-	}
-      break;
-    }
-
-  /* For associative ops, fold "(X op CST_A) op CST_B)" to
-     "X op (CST_A op CST_B)".  */
-  if (cst1 && associative_tree_code (op))
-    if (const binop_svalue2 *binop = arg0->dyn_cast_binop_svalue2 ())
-      if (binop->get_op () == op
-	  && binop->get_arg1 ()->maybe_get_constant ()
-	  && type == binop->get_type ()
-	  && type == binop->get_arg0 ()->get_type ()
-	  && type == binop->get_arg1 ()->get_type ())
-	return get_or_create_binop
-	  (type, op, binop->get_arg0 (),
-	   get_or_create_binop (type, op,
-				binop->get_arg1 (), arg1));
-
-  /* FIXME.  pointer_plus_expr on pointer_plus_expr...
-     associative_tree_code is false for POINTER_PLUS_EXPR, but we
-     can fold:
-       "(PTR ptr+ CST_A) ptr+ CST_B)" to "PTR ptr+ (CST_A ptr+ CST_B)".  */
-  /* e.g. in data-model-1.c: test_42c.  */
-  if (cst1 && op == POINTER_PLUS_EXPR)
-    if (const binop_svalue2 *binop = arg0->dyn_cast_binop_svalue2 ())
-      if (binop->get_op () == POINTER_PLUS_EXPR)
-	if (tree cst01 = binop->get_arg1 ()->maybe_get_constant ())
-	  {
-	    return get_or_create_binop
-	      (type, op, binop->get_arg0 (),
-	       get_or_create_binop (size_type_node, op,
-				    binop->get_arg1 (), arg1));
-	  }
-
-  /* Ops on "unknown" are also unknown (unless we can use one of the
-     identities above).  */
-  if (arg0->get_kind () == svalue2::SK_UNKNOWN
-      || arg1->get_kind () == svalue2::SK_UNKNOWN)
-    return get_or_create_unknown_svalue2 (type);
-
-  // TODO
-
-  return NULL;
-}
-
-const svalue2 *
-region_model2_manager::get_or_create_binop (tree type, enum tree_code op,
-					    const svalue2 *arg0,
-					    const svalue2 *arg1)
-{
-  /* For commutative ops, put any constant on the RHS.  */
-  if (arg0->maybe_get_constant () && commutative_tree_code (op))
-    std::swap (arg0, arg1);
-
-  if (const svalue2 *folded = maybe_fold_binop (type, op, arg0, arg1))
-    return folded;
-
-  binop_svalue2::key_t key (type, op, arg0, arg1);
-  if (binop_svalue2 **slot = m_binop_values_map.get (key))
-    return *slot;
-  binop_svalue2 *binop_sval = new binop_svalue2 (type, op, arg0, arg1);
-  RETURN_UNKNOWN_IF_TOO_COMPLEX (binop_sval);
-  m_binop_values_map.put (key, binop_sval);
-  return binop_sval;
-}
-
-/* FIXME.  */
-
-static const svalue2 *
-maybe_get_char_from_string_cst (tree string_cst, tree byte_offset_cst,
-				region_model2_manager *mgr)
-{
-  gcc_assert (TREE_CODE (string_cst) == STRING_CST);
-
-  /* Adapted from fold_read_from_constant_string.  */
-  scalar_int_mode char_mode;
-  if (TREE_CODE (byte_offset_cst) == INTEGER_CST
-      && compare_tree_int (byte_offset_cst,
-			   TREE_STRING_LENGTH (string_cst)) < 0
-      && is_int_mode (TYPE_MODE (TREE_TYPE (TREE_TYPE (string_cst))),
-		      &char_mode)
-      && GET_MODE_SIZE (char_mode) == 1)
-    {
-      tree char_cst
-	= build_int_cst_type (TREE_TYPE (TREE_TYPE (string_cst)),
-			      (TREE_STRING_POINTER (string_cst)
-			       [TREE_INT_CST_LOW (byte_offset_cst)]));
-      return mgr->get_or_create_constant_svalue2 (char_cst);
-    }
-  return NULL;
-}
-
-/* Subroutine of region_model2_manager::get_or_create_sub_svalue2.
-   Return a folded svalue2, or NULL.  */
-
-const svalue2 *
-region_model2_manager::maybe_fold_sub_svalue2 (tree type,
-					       const svalue2 *parent_svalue2,
-					       const region2 *subregion)
-{
-  /* Subvalues of "unknown" are unknown.  */
-  if (parent_svalue2->get_kind () == svalue2::SK_UNKNOWN)
-    return get_or_create_unknown_svalue2 (type);
-
-  /* If we have a subregion of a zero-fill, it's zero.  */
-  if (const unaryop_svalue2 *unary
-      = parent_svalue2->dyn_cast_unaryop_svalue2 ())
-    {
-      if (unary->get_op () == NOP_EXPR)
-	if (tree cst = unary->get_arg ()->maybe_get_constant ())
-	  if (zerop (cst))
-	    {
-	      const svalue2 *cst_sval
-		= get_or_create_constant_svalue2 (cst);
-	      return get_or_create_cast (type, cst_sval);
-	    }
-    }
-
-  /* Handle getting individual chars from a STRING_CST.  */
-  if (tree cst = parent_svalue2->maybe_get_constant ())
-    if (TREE_CODE (cst) == STRING_CST)
-      if (const element_region2 *element_reg
-	    = subregion->dyn_cast_element_region2 ())
-	{
-	  const svalue2 *idx_sval = element_reg->get_index ();
-	  if (tree cst_idx = idx_sval->maybe_get_constant ())
-	    if (const svalue2 *char_sval
-		= maybe_get_char_from_string_cst (cst, cst_idx, this))
-	      return get_or_create_cast (type, char_sval);
-	}
-
-  /* SUB(INIT(r)).FIELD -> INIT(r.FIELD)
-     i.e.
-     Subvalue(InitialValue(R1), FieldRegion(R2, F))
-       -> InitialValue(FieldRegion(R1, F)).  */
-  if (const initial_svalue2 *init_sval
-        = parent_svalue2->dyn_cast_initial_svalue2 ())
-    {
-      if (const field_region2 *field_reg = subregion->dyn_cast_field_region2 ())
-	{
-	  const region2 *field_reg_new
-	    = get_field_region (init_sval->get_region (),
-				field_reg->get_field ());
-	  return get_or_create_initial_value (field_reg_new);
-	}
-    }
-  // TODO: etc similar for element_region
-
-  return NULL;
-}
-
-/* FIXME.  */
-
-const svalue2 *
-region_model2_manager::get_or_create_sub_svalue2 (tree type,
-						  const svalue2 *parent_svalue2,
-						  const region2 *subregion)
-{
-  if (const svalue2 *folded
-	= maybe_fold_sub_svalue2 (type, parent_svalue2, subregion))
-    return folded;
-
-  sub_svalue2::key_t key (type, parent_svalue2, subregion);
-  if (sub_svalue2 **slot = m_sub_values_map.get (key))
-    return *slot;
-  sub_svalue2 *sub_sval
-    = new sub_svalue2 (type, parent_svalue2, subregion);
-  RETURN_UNKNOWN_IF_TOO_COMPLEX (sub_sval);
-  m_sub_values_map.put (key, sub_sval);
-  return sub_sval;
-}
-
-/* FIXME.  */
-
-const svalue2 *
-region_model2_manager::get_or_create_unmergeable (const svalue2 *arg)
-{
-  if (arg->get_kind () == svalue2::SK_UNMERGEABLE)
-    return arg;
-
-  if (unmergeable_svalue2 **slot = m_unmergeable_values_map.get (arg))
-    return *slot;
-  unmergeable_svalue2 *unmergeable_sval = new unmergeable_svalue2 (arg);
-  RETURN_UNKNOWN_IF_TOO_COMPLEX (unmergeable_sval);
-  m_unmergeable_values_map.put (arg, unmergeable_sval);
-  return unmergeable_sval;
-}
-
-/* FIXME.  */
-
-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, 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, point, base_sval, iter_sval);
-  RETURN_UNKNOWN_IF_TOO_COMPLEX (widening_sval);
-  m_widening_values_map.put (key, widening_sval);
-  return widening_sval;
-}
-
-/* FIXME.  */
-
-const svalue2 *
-region_model2_manager::
-get_or_create_compound_svalue2 (tree type,
-				const binding_map &map)
-{
-  compound_svalue2::key_t tmp_key (type, &map);
-  if (compound_svalue2 **slot = m_compound_values_map.get (tmp_key))
-    return *slot;
-  compound_svalue2 *compound_sval
-    = new compound_svalue2 (type, map);
-  RETURN_UNKNOWN_IF_TOO_COMPLEX (compound_sval);
-  /* Use make_key rather than reusing the key, so that we use a
-     ptr to compound_sval's binding_map, rather than the MAP param.  */
-  m_compound_values_map.put (compound_sval->make_key (), compound_sval);
-  return compound_sval;
-}
-
-/* FIXME.  */
-
-const svalue2 *
-region_model2_manager::
-get_or_create_conjured_svalue2 (tree type, const gimple *stmt,
-				const region2 *id_reg)
-{
-  conjured_svalue2::key_t key (type, stmt, id_reg);
-  if (conjured_svalue2 **slot = m_conjured_values_map.get (key))
-    return *slot;
-  conjured_svalue2 *conjured_sval
-    = new conjured_svalue2 (type, stmt, id_reg);
-  RETURN_UNKNOWN_IF_TOO_COMPLEX (conjured_sval);
-  m_conjured_values_map.put (key, conjured_sval);
-  return conjured_sval;
-}
-
-/* region2 consolidation.  */
-
-/* FIXME.  */
-
-const function_region2 *
-region_model2_manager::get_region_for_fndecl (tree fndecl)
-{
-  gcc_assert (TREE_CODE (fndecl) == FUNCTION_DECL);
-
-  function_region2 **slot = m_fndecls_map.get (fndecl);
-  if (slot)
-    return *slot;
-  function_region2 *reg
-    = new function_region2 (alloc_region_id (), &m_code_region, fndecl);
-  m_fndecls_map.put (fndecl, reg);
-  return reg;
-}
-
-/* FIXME.  */
-
-const label_region2 *
-region_model2_manager::get_region_for_label (tree label)
-{
-  gcc_assert (TREE_CODE (label) == LABEL_DECL);
-
-  label_region2 **slot = m_labels_map.get (label);
-  if (slot)
-    return *slot;
-
-  tree fndecl = DECL_CONTEXT (label);
-  gcc_assert (fndecl && TREE_CODE (fndecl) == FUNCTION_DECL);
-
-  const function_region2 *func_reg = get_region_for_fndecl (fndecl);
-  label_region2 *reg
-    = new label_region2 (alloc_region_id (), func_reg, label);
-  m_labels_map.put (label, reg);
-  return reg;
-}
-
-/* FIXME.  */
-
-const decl_region2 *
-region_model2_manager::get_region_for_global (tree expr)
-{
-  gcc_assert (TREE_CODE (expr) == VAR_DECL);
-
-  decl_region2 **slot = m_globals_map.get (expr);
-  if (slot)
-    return *slot;
-  decl_region2 *reg
-    = new decl_region2 (alloc_region_id (), &m_globals_region, expr);
-  m_globals_map.put (expr, reg);
-  return reg;
-}
-
-/* FIXME.  */
-
-const region2 *
-region_model2_manager::get_field_region (const region2 *parent, tree field)
-{
-  // FIXME: should only alloc_region_id if consolidation fails
-  region2 *field_reg = new field_region2 (alloc_region_id (), parent, field);
-  return const_cast <region2 *> (m_fields.consolidate (field_reg));
-}
-
-/* FIXME.  */
-
-const region2 *
-region_model2_manager::get_element_region (const region2 *parent,
-					   tree element_type,
-					   const svalue2 *index)
-{
-  // FIXME: should only alloc_region_id if consolidation fails
-  region2 *element_reg
-    = new element_region2 (alloc_region_id (), parent, element_type, index);
-  return const_cast <region2 *> (m_elements.consolidate (element_reg));
-}
-
-/* FIXME.  */
-
-const region2 *
-region_model2_manager::get_offset_region (const region2 *parent,
-					  tree type,
-					  const svalue2 *byte_offset)
-{
-  /* If BYTE_OFFSET is zero, return PARENT.  */
-  if (tree cst_offset = byte_offset->maybe_get_constant ())
-    if (zerop (cst_offset))
-      return get_cast_region (parent, type);
-
-  /* Fold OFFSET_REGION(OFFSET_REGION(REG, X), Y)
-     to   OFFSET_REGION(REG, (X + Y)).  */
-  if (const offset_region2 *parent_offset_reg
-	= parent->dyn_cast_offset_region2 ())
-    {
-      const svalue2 *sval_x = parent_offset_reg->get_byte_offset ();
-      const svalue2 *sval_sum
-	= get_or_create_binop (byte_offset->get_type (),
-			       PLUS_EXPR, sval_x, byte_offset);
-      return get_offset_region (parent->get_parent_region (), type, sval_sum);
-    }
-
-  // FIXME: should only alloc_region_id if consolidation fails
-  region2 *offset_reg
-    = new offset_region2 (alloc_region_id (), parent, type, byte_offset);
-  return const_cast <region2 *> (m_offset_regions.consolidate (offset_reg));
-}
-
-/* FIXME.  */
-
-const region2 *
-region_model2_manager::get_cast_region (const region2 *original_region,
-					tree type)
-{
-  /* If types match, return ORIGINAL_REGION.  */
-  if (type == original_region->get_type ())
-    return original_region;
-
-  // FIXME: should only alloc_region_id if consolidation fails
-  region2 *cast_reg
-    = new cast_region2 (alloc_region_id (), original_region, type);
-  return const_cast <region2 *> (m_cast_regions.consolidate (cast_reg));
-}
-
-/* FIXME.  */
-
-const frame_region2 *
-region_model2_manager::get_frame_region (const frame_region2 *calling_frame,
-					 function *fun)
-{
-  int index = calling_frame ? calling_frame->get_index () + 1 : 0;
-  // FIXME: should only alloc_region_id if consolidation fails
-  frame_region2 *frame_reg
-    = new frame_region2 (alloc_region_id (), &m_stack_region, calling_frame,
-			 fun, index);
-  return const_cast <frame_region2 *> (m_frames.consolidate (frame_reg));
-}
-
-/* FIXME.  */
-
-const region2 *
-region_model2_manager::get_symbolic_region (const svalue2 *sval)
-{
-  // FIXME: should only alloc_region_id if consolidation fails
-  region2 *reg
-    = new symbolic_region2 (alloc_region_id (), &m_root_region, sval);
-  return const_cast <region2 *> (m_symbolic_regions.consolidate (reg));
-}
-
-/* FIXME.  */
-
-const string_region2 *
-region_model2_manager::get_region_for_string (tree string_cst)
-{
-  gcc_assert (TREE_CODE (string_cst) == STRING_CST);
-
-  string_region2 **slot = m_string_map.get (string_cst);
-  if (slot)
-    return *slot;
-  string_region2 *reg
-    = new string_region2 (alloc_region_id (), &m_root_region, string_cst);
-  // FIXME: is there a better choice of parent region?
-  m_string_map.put (string_cst, reg);
-  return reg;
-}
-
-/* If we see a tree code we don't know how to handle, rather than
-   ICE or generate bogus results, create a dummy region2, and notify
-   CTXT so that it can mark the new state as being not properly
-   modelled.  The exploded graph can then stop exploring that path,
-   since any diagnostics we might issue will have questionable
-   validity.  */
-
-const region2 *
-region_model2_manager::
-get_region2_for_unexpected_tree_code (region_model2_context *ctxt,
-				      tree t,
-				      const dump_location_t &loc)
-{
-  gcc_assert (ctxt);
-  tree type = TYPE_P (t) ? t : TREE_TYPE (t);
-  region2 *new_reg
-    = new unknown_region2 (alloc_region_id (), &m_root_region, type); // FIXME
-  // or should this be something else?
-  ctxt->on_unexpected_tree_code (t, loc);
-  return new_reg;
-}
-
-/* FIXME.  */
-
-const region2 *
-region_model2_manager::create_region_for_heap_alloc ()
-{
-  // FIXME: should this be a symbolic region??
-  region2 *reg
-    = new heap_allocated_region2 (alloc_region_id (), &m_heap_region);
-  m_managed_dynamic_regions.safe_push (reg);
-  // TODO: selftest; verify no leak under valgrind
-  return reg;
-  /* TODO: maybe have an ID to model the different allocators?
-     (are they different parent regions? or some kind of discriminant var?)  */
-}
-
-/* FIXME.  */
-
-const region2 *
-region_model2_manager::create_region_for_alloca (const frame_region2 *frame)
-{
-  region2 *reg = new alloca_region2 (alloc_region_id (), frame);
-  m_managed_dynamic_regions.safe_push (reg);
-  // TODO: selftest; verify no leak under valgrind
-  return reg;
-}
-
-/* FIXME.  */
-
-template <typename T>
-static void
-log_managed_object (logger *logger, const T *obj)
-{
-  logger->start_log_line ();
-  pretty_printer *pp = logger->get_printer ();
-  pp_string (pp, "    ");
-  obj->dump_to_pp (pp, true);
-  logger->end_log_line ();
-}
-
-/* FIXME.  */
-
-template <>
-void
-log_managed_object (logger *logger, const frame_region2 *obj)
-{
-  logger->start_log_line ();
-  pretty_printer *pp = logger->get_printer ();
-  pp_string (pp, "    ");
-  obj->dump_to_pp (pp, true);
-  pp_printf (pp, " [with %i region(s) for locals]", obj->get_num_locals ());
-  logger->end_log_line ();
-}
-
-/* FIXME.  */
-
-template <typename K, typename T>
-static void
-log_uniq_map (logger *logger, bool show_objs, const char *title,
-	      const hash_map<K, T*> &uniq_map)
-{
-  logger->log ("  # %s: %li", title, uniq_map.elements ());
-  if (show_objs)
-    for (typename hash_map<K, T*>::iterator iter = uniq_map.begin ();
-	 iter != uniq_map.end (); ++iter)
-      {
-	T *managed_obj = (*iter).second;
-	log_managed_object<T> (logger, managed_obj);
-      }
-}
-
-/* FIXME.  */
-
-template <typename T>
-static void
-log_uniq_manager (logger *logger, bool show_objs, const char *title,
-		  const uniq_manager<T> &uniq_mgr)
-{
-  logger->log ("  # %s: %li", title, uniq_mgr.get_count ());
-  if (show_objs)
-    for (typename uniq_manager<T>::iterator_t iter = uniq_mgr.begin ();
-	 iter != uniq_mgr.end (); ++iter)
-      {
-	T* managed_obj = (*iter).second;
-	log_managed_object<T> (logger, managed_obj);
-      }
-}
-
-/* FIXME.  */
-
-void
-region_model2_manager::log_stats (logger *logger, bool show_objs) const
-{
-  LOG_SCOPE (logger);
-  logger->log ("svalue2 consolidation");
-  log_uniq_map (logger, show_objs, "constant_svalue2", m_constants_map);
-  log_uniq_map (logger, show_objs, "unknown_svalue2", m_unknowns_map);
-  if (m_unknown_NULL)
-    log_managed_object (logger, m_unknown_NULL);
-  log_uniq_map (logger, show_objs, "poisoned_svalue2", m_poisoned_values_map);
-  log_uniq_map (logger, show_objs, "setjmp_svalue2", m_setjmp_values_map);
-  log_uniq_map (logger, show_objs, "initial_svalue2", m_initial_values_map);
-  log_uniq_map (logger, show_objs, "region_svalue2", m_pointer_values_map);
-  log_uniq_map (logger, show_objs, "unaryop_svalue2", m_unaryop_values_map);
-  log_uniq_map (logger, show_objs, "binop_svalue2", m_binop_values_map);
-  log_uniq_map (logger, show_objs, "sub_svalue2", m_sub_values_map);
-  log_uniq_map (logger, show_objs, "unmergeable_svalue2",
-		m_unmergeable_values_map);
-  log_uniq_map (logger, show_objs, "widening_svalue2", m_widening_values_map);
-  log_uniq_map (logger, show_objs, "compound_svalue2", m_compound_values_map);
-  log_uniq_map (logger, show_objs, "conjured_svalue2", m_conjured_values_map);
-  logger->log ("max accepted svalue num_nodes: %i",
-	       m_max_complexity.m_num_nodes);
-  logger->log ("max accepted svalue max_depth: %i",
-	       m_max_complexity.m_max_depth);
-
-  logger->log ("region2 consolidation");
-  logger->log ("  next region id: %i", m_next_region_id);
-  log_uniq_map (logger, show_objs, "function_region2", m_fndecls_map);
-  log_uniq_map (logger, show_objs, "label_region2", m_labels_map);
-  log_uniq_map (logger, show_objs, "decl_region2 for globals", m_globals_map);
-  log_uniq_manager (logger, show_objs, "field_region2", m_fields);
-  log_uniq_manager (logger, show_objs, "element_region2", m_elements);
-  log_uniq_manager (logger, show_objs, "offset_region2", m_offset_regions);
-  log_uniq_manager (logger, show_objs, "cast_region2", m_cast_regions);
-  log_uniq_manager (logger, show_objs, "frame_region2", m_frames);
-  log_uniq_manager (logger, show_objs, "symbolic_region2", m_symbolic_regions);
-  log_uniq_map (logger, show_objs, "string_region2", m_string_map);
-  logger->log ("  # managed dynamic regions: %i",
-	       m_managed_dynamic_regions.length ());
-  m_store2_mgr.log_stats (logger, show_objs);
-}
-
-/* FIXME.  */
-// FIXME: this is here so it can use log_uniq_manager
-
-void
-store2_manager::log_stats (logger *logger, bool show_objs) const
-{
-  LOG_SCOPE (logger);
-  log_uniq_manager (logger, show_objs, "concrete_binding2",
-		    m_concrete_binding_key_mgr);
-  log_uniq_manager (logger, show_objs, "symbolic_binding2",
-		    m_symbolic_binding_key_mgr);
-}
-
-/* class region_model2.  */
-
-/* FIXME: region_model2's default ctor.  */
-
-region_model2::region_model2 (region_model2_manager *mgr)
-: m_mgr (mgr), m_store (), m_current_frame (NULL)
-{
-  //m_root_reg = add_region2 (new root_region2 ());
-  m_constraints2 = new constraint_manager2 (mgr);
-  // TODO
-}
-
-/* region_model2's copy ctor.  */
-
-region_model2::region_model2 (const region_model2 &other)
-: m_mgr (other.m_mgr), m_store (other.m_store),
-  m_constraints2 (new constraint_manager2 (*other.m_constraints2)),
-  m_current_frame (other.m_current_frame)
-{
-}
-
-/* region_model2's dtor.  */
-
-region_model2::~region_model2 ()
-{
-  // empty?
-  delete m_constraints2;
-}
-
-/* region_model2's assignment operator.  */
-
-region_model2 &
-region_model2::operator= (const region_model2 &other)
-{
-  /* m_mgr is const.  */
-  gcc_assert (m_mgr == other.m_mgr);
-
-  m_store = other.m_store;
-
-  delete m_constraints2;
-  m_constraints2 = new constraint_manager2 (*other.m_constraints2);
-
-  m_current_frame = other.m_current_frame;
-
-  return *this;
-}
-
-/* Equality operator for region_model2.
-
-   FIXME:
-   Amongst other things this directly compares the svalue2 and region2
-   vectors and so for this to be meaningful both this and OTHER should
-   have been canonicalized.  */
-
-bool
-region_model2::operator== (const region_model2 &other) const
-{
-  /* We can only compare instances that use the same manager.  */
-  gcc_assert (m_mgr == other.m_mgr);
-
-  if (m_store != other.m_store)
-    return false;
-
-  if (*m_constraints2 != *other.m_constraints2)
-    return false;
-
-  if (m_current_frame != other.m_current_frame)
-    return false;
-
-  //gcc_checking_assert (hash () == other.hash ());
-
-  return true;
-}
-
-/* Generate a hash value for this region_model2.  */
-
-#if 0
-hashval_t
-region_model2::hash () const
-{
-  hashval_t result = 0;
-  int i;
-
-  const svalue2 *svalue2;
-  FOR_EACH_VEC_ELT (m_svalue2s, i, svalue2)
-    result ^= svalue2->hash ();
-
-  region2 *region2;
-  FOR_EACH_VEC_ELT (m_region2s, i, region2)
-    result ^= region2->hash ();
-
-  result ^= m_constraints->hash ();
-
-  return result;
-}
-
-/* Print an all-on-one-line representation of this region_model2 to PP,
-   which must support %E for trees.  */
-
-void
-region_model2::print (pretty_printer *pp) const
-{
-  int i;
-
-  pp_string (pp, "svalue2s: [");
-  const svalue2 *svalue2;
-  FOR_EACH_VEC_ELT (m_svalue2s, i, svalue2)
-    {
-      if (i > 0)
-	pp_string (pp, ", ");
-      print_svalue2 (svalue2_id::from_int (i), pp);
-    }
-
-  pp_string (pp, "], region2s: [");
-
-  region2 *region2;
-  FOR_EACH_VEC_ELT (m_region2s, i, region2)
-    {
-      if (i > 0)
-	pp_string (pp, ", ");
-      region2->print (*this, region2_id::from_int (i), pp);
-    }
-
-  pp_string (pp, "], constraints: ");
-
-  m_constraints->print (pp);
-}
-
-/* Print the svalue2 with id SID to PP.  */
-
-void
-region_model2::print_svalue2 (const svalue2 *sval, pretty_printer *pp) const
-{
-  get_svalue2 (sid)->print (*this, sid, pp);
-}
-
-/* Dump a .dot representation of this region_model2 to PP, showing
-   the values and the hierarchy of region2s.  */
-
-void
-region_model2::dump_dot_to_pp (pretty_printer *pp) const
-{
-  graphviz_out gv (pp);
-
-  pp_string (pp, "digraph \"");
-  pp_write_text_to_stream (pp);
-  pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/false);
-  pp_string (pp, "\" {\n");
-
-  gv.indent ();
-
-  pp_string (pp, "overlap=false;\n");
-  pp_string (pp, "compound=true;\n");
-
-  int i;
-
-  const svalue2 *svalue2;
-  FOR_EACH_VEC_ELT (m_svalue2s, i, svalue2)
-    svalue2->dump_dot_to_pp (*this, svalue2_id::from_int (i), pp);
-
-  region2 *region2;
-  FOR_EACH_VEC_ELT (m_region2s, i, region2)
-    region2->dump_dot_to_pp (*this, region2_id::from_int (i), pp);
-
-  /* TODO: constraints.  */
-
-  /* Terminate "digraph" */
-  gv.outdent ();
-  pp_string (pp, "}");
-  pp_newline (pp);
-}
-
-/* Dump a .dot representation of this region_model2 to FP.  */
-
-void
-region_model2::dump_dot_to_file (FILE *fp) const
-{
-  pretty_printer pp;
-  pp_format_decoder (&pp) = default_tree_printer;
-  pp.buffer->stream = fp;
-  dump_dot_to_pp (&pp);
-  pp_flush (&pp);
-}
-
-/* Dump a .dot representation of this region_model2 to PATH.  */
-
-void
-region_model2::dump_dot (const char *path) const
-{
-  FILE *fp = fopen (path, "w");
-  dump_dot_to_file (fp);
-  fclose (fp);
-}
-#endif
-
-// FIXME:
-/* Dump a representation of this model to PP, showing the
-   region2 hierarchy, the svalue2s, and any constraints.
-
-   If SIMPLE is true, show only the most pertinent information,
-   in a form that attempts to be less verbose.
-   Otherwise, show all information.
-
-   If MULTILINE... FIXME.  */
-
-void
-region_model2::dump_to_pp (pretty_printer *pp, bool simple,
-			   bool multiline) const
-{
-  /* Dump stack.  */
-  pp_printf (pp, "stack depth: %i", get_stack_depth ());
-  if (multiline)
-    pp_newline (pp);
-  else
-    pp_string (pp, " {");
-  for (const frame_region2 *iter_frame = m_current_frame; iter_frame;
-       iter_frame = iter_frame->get_calling_frame ())
-    {
-      if (multiline)
-	pp_string (pp, "  ");
-      else if (iter_frame != m_current_frame)
-	pp_string (pp, ", ");
-      pp_printf (pp, "frame (index %i): ", iter_frame->get_index ());
-      iter_frame->dump_to_pp (pp, simple);
-      if (multiline)
-	pp_newline (pp);
-    }
-  if (!multiline)
-    pp_string (pp, "}");
-
-  /* Dump store.  */
-  if (!multiline)
-    pp_string (pp, ", {");
-  m_store.dump_to_pp (pp, simple, multiline,
-		      m_mgr->get_store2_manager ());
-  if (!multiline)
-    pp_string (pp, "}");
-
-  /* Dump constraints.  */
-  pp_string (pp, "constraint_manager2:");
-  if (multiline)
-    pp_newline (pp);
-  else
-    pp_string (pp, " {");
-  m_constraints2->dump_to_pp (pp, multiline);
-  if (!multiline)
-    pp_string (pp, "}");
-}
-
-/* Dump a representation of this model to FILE.  */
-
-void
-region_model2::dump (FILE *fp, bool simple, bool multiline) const
-{
-  pretty_printer pp;
-  pp_format_decoder (&pp) = default_tree_printer;
-  pp_show_color (&pp) = pp_show_color (global_dc->printer);
-  pp.buffer->stream = fp;
-  dump_to_pp (&pp, simple, multiline);
-  pp_newline (&pp);
-  pp_flush (&pp);
-}
-
-/* Dump a multiline representation of this model to stderr.  */
-
-DEBUG_FUNCTION void
-region_model2::dump (bool simple) const
-{
-  dump (stderr, simple, true);
-}
-
-/* Dump a summary of this model to PP.  */
-
-void
-region_model2::summarize_to_pp (pretty_printer *pp, bool simple) const
-{
-  m_store.summarize_to_pp (pp, simple);
-
-  // FIXME:
-#if 0
-  /* Dump constraints.  */
-  pp_string (pp, "constraint_manager2:");
-  if (multiline)
-    pp_newline (pp);
-  else
-    pp_string (pp, " {");
-  m_constraints2->dump_to_pp (pp, multiline);
-  if (!multiline)
-    pp_string (pp, "}");
-#endif
-}
-
-/* Dump a summary of this model to stderr.  */
-
-DEBUG_FUNCTION void
-region_model2::summarize (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;
-  summarize_to_pp (&pp, simple);
-  pp_newline (&pp);
-  pp_flush (&pp);
-}
-
-/* Dump a multiline representation of this model to stderr.  */
-
-DEBUG_FUNCTION void
-region_model2::debug () const
-{
-  dump (true);
-}
-
-#if 0
-/* Dump VEC to PP, in the form "{VEC elements}: LABEL".  */
-
-static void
-dump_vec_of_tree (pretty_printer *pp,
-		  bool *is_first,
-		  const auto_vec<tree> &vec,
-		  const char *label)
-{
-  if (vec.length () == 0)
-    return;
-
-  dump_separator (pp, is_first);
-  pp_printf (pp, "{");
-  unsigned i;
-  tree key;
-  FOR_EACH_VEC_ELT (vec, i, key)
-    {
-      if (i > 0)
-	pp_string (pp, ", ");
-      dump_tree (pp, key);
-    }
-  pp_printf (pp, "}: %s", label);
-}
-
-/* Dump all *REP_PATH_VARS to PP in compact form, updating *IS_FIRST.
-   Subroutine of region_model2::dump_to_pp.  */
-
-void
-region_model2::dump_summary_of_rep_path_vars (pretty_printer *pp,
-					     auto_vec<path_var> *rep_path_vars,
-					     bool *is_first)
-{
-  /* Print pointers, constants, and poisoned values that aren't "uninit";
-     gather keys for unknown and uninit values.  */
-  unsigned i;
-  path_var *pv;
-  auto_vec<tree> unknown_trees;
-  auto_vec<tree> uninit_trees;
-  FOR_EACH_VEC_ELT (*rep_path_vars, i, pv)
-    {
-      if (TREE_CODE (pv->m_tree) == STRING_CST)
-	continue;
-      tentative_region_model2_context ctxt;
-      region2_id child_reg = get_lvalue (*pv, &ctxt);
-      if (ctxt.had_errors_p ())
-	continue;
-      region2 *child_region2 = get_region2 (child_reg);
-      if (!child_region2)
-	continue;
-      const svalue2 *sval = child_region2->get_value_direct ();
-      if (sid.null_p ())
-	continue;
-      const svalue2 *sval = get_svalue2 (sid);
-      switch (sval->get_kind ())
-	{
-	default:
-	  gcc_unreachable ();
-	case SK_REGION2:
-	  {
-	    const region_svalue2 *region2_sval
-	      = as_a <const region_svalue2 *> (sval);
-	    region2_id pointee_reg = region2_sval->get_pointee ();
-	    gcc_assert (!pointee_reg.null_p ());
-	    tree pointee = get_representative_path_var (pointee_reg).m_tree;
-	    dump_separator (pp, is_first);
-	    dump_tree (pp, pv->m_tree);
-	    pp_string (pp, ": ");
-	    pp_character (pp, '&');
-	    if (pointee)
-	      dump_tree (pp, pointee);
-	    else
-	      pointee_reg.print (pp);
-	  }
-	  break;
-	case SK_CONSTANT:
-	  dump_separator (pp, is_first);
-	  dump_tree (pp, pv->m_tree);
-	  pp_string (pp, ": ");
-	  dump_tree (pp, sval->dyn_cast_constant_svalue2 ()->get_constant ());
-	  break;
-	case SK_UNKNOWN:
-	  unknown_trees.safe_push (pv->m_tree);
-	  break;
-	case SK_POISONED:
-	  {
-	    const poisoned_svalue2 *poisoned_sval
-	      = as_a <const poisoned_svalue2 *> (sval);
-	    enum poison_kind pkind = poisoned_sval->get_poison_kind ();
-	    if (pkind == POISON_KIND_UNINIT)
-	      uninit_trees.safe_push (pv->m_tree);
-	    else
-	      {
-		dump_separator (pp, is_first);
-		dump_tree (pp, pv->m_tree);
-		pp_printf (pp, ": %s", poison_kind_to_str (pkind));
-	      }
-	  }
-	  break;
-	case SK_SETJMP:
-	  dump_separator (pp, is_first);
-	  pp_printf (pp, "setjmp: EN: %i",
-		     sval->dyn_cast_setjmp_svalue2 ()->get_enode_index ());
-	  break;
-	}
-    }
-
-  /* Print unknown and uninitialized values in consolidated form.  */
-  dump_vec_of_tree (pp, is_first, unknown_trees, "unknown");
-  dump_vec_of_tree (pp, is_first, uninit_trees, "uninit");
-}
-#endif
-
-#if 0
-
-/* Assert that this object is valid.  */
-
-void
-region_model2::validate () const
-{
-  /* Skip this in a release build.  */
-#if !CHECKING_P
-  return;
-#endif
-
-  m_constraints->validate ();
-
-  unsigned i;
-  region2 *r;
-  FOR_EACH_VEC_ELT (m_region2s, i, r)
-    r->validate (*this);
-
-  // TODO: anything else?
-
-  /* Verify that the stack region2 (if any) has an "uninitialized" value.  */
-  region2 *stack_region2 = get_root_region2 ()->get_stack_region2 (this);
-  if (stack_region2)
-    {
-      svalue2_id stack_value_sval = stack_region2->get_value_direct ();
-      const svalue2 *stack_value = get_svalue2 (stack_value_sval);
-      gcc_assert (stack_value->get_kind () == SK_POISONED);
-      poisoned_svalue2 *subclass = stack_value->dyn_cast_poisoned_svalue2 ();
-      gcc_assert (subclass);
-      gcc_assert (subclass->get_poison_kind () == POISON_KIND_UNINIT);
-    }
-}
-
-#endif
-
-/* FIXME.  */
-
-void
-region_model2::canonicalize ()
-{
-  m_store.canonicalize (m_mgr->get_store2_manager ());
-  m_constraints2->canonicalize ();
-}
-
-/* FIXME.  */
-
-bool
-region_model2::canonicalized_p () const
-{
-  region_model2 copy (*this);
-  copy.canonicalize ();
-  return *this == copy;
-}
-
-/* FIXME.  */
-
-void
-region_model2::loop_replay_hack (const region_model2 *dst_state)
-{
-  m_store.loop_replay_hack (dst_state->get_store (), m_mgr);
-}
-
-/* FIXME.  */
-
-bool
-region_model2::on_stmt (const gimple *stmt,
-			region_model2_context *ctxt)
-{
-  switch (stmt->code)
-    {
-    case GIMPLE_CALL:
-      gcc_unreachable ();
-      //return on_gcall (snode, as_a <const gcall *> (stmt));
-
-    case GIMPLE_COND:
-      /* This is a no-op; the condition is handled by the true/false
-	 superedges.  */
-      return true;
-
-    case GIMPLE_GOTO:
-      /* TODO: GIMPLE_GOTO needs to be handled by the engine, not the
-	 region_model2, but I don't think this is a regression relative to
-	 the old implementation.  */
-    case GIMPLE_SWITCH:
-    case GIMPLE_LABEL:
-      /* No-op.  */
-      return true;
-
-    case GIMPLE_ASSIGN:
-      return on_gassign (as_a <const gassign *> (stmt), ctxt);
-
-    case GIMPLE_RETURN:
-      return on_greturn (as_a <const greturn *> (stmt), ctxt);
-
-    default:
-      sorry_at (stmt->location, "unhandled stmt: %qs",
-		gimple_code_name[stmt->code]);
-      return false;
-    }
-}
-
-/* If ASSIGN is a stmt that can be modelled via
-     set_value (lhs_reg, SVALUE, CTXT)
-   for some SVALUE, get the SVALUE.
-   Otherwise return NULL.  */
-
-const svalue2 *
-region_model2::get_gassign_result (const gassign *assign,
-				   region_model2_context *ctxt)
-{
-  tree lhs = gimple_assign_lhs (assign);
-  tree rhs1 = gimple_assign_rhs1 (assign);
-  enum tree_code op = gimple_assign_rhs_code (assign);
-  switch (op)
-    {
-    default:
-      return NULL;
-
-    case POINTER_PLUS_EXPR:
-      {
-	/* e.g. "_1 = a_10(D) + 12;" */
-	tree ptr = rhs1;
-	tree offset = gimple_assign_rhs2 (assign);
-
-	const svalue2 *ptr_sval = get_rvalue (ptr, ctxt);
-	const svalue2 *offset_sval = get_rvalue (offset, ctxt);
-	/* Quoting tree.def, "the second operand [of a POINTER_PLUS_EXPR]
-	   is an integer of type sizetype".  */
-	offset_sval = m_mgr->get_or_create_cast (size_type_node, offset_sval);
-
-	const svalue2 *sval_binop
-	  = m_mgr->get_or_create_binop (TREE_TYPE (lhs), op,
-					ptr_sval, offset_sval);
-	return sval_binop;
-      }
-      break;
-
-    case POINTER_DIFF_EXPR:
-      {
-	/* e.g. "_1 = p_2(D) - q_3(D);".  */
-	tree rhs2 = gimple_assign_rhs2 (assign);
-	const svalue2 *rhs1_sval = get_rvalue (rhs1, ctxt);
-	const svalue2 *rhs2_sval = get_rvalue (rhs2, ctxt);
-
-	// TODO: perhaps fold to zero if they're known to be equal?
-
-	const svalue2 *sval_binop
-	  = m_mgr->get_or_create_binop (TREE_TYPE (lhs), op,
-					rhs1_sval, rhs2_sval);
-	return sval_binop;
-      }
-      break;
-
-    /* Assignments of the form
-	set_value (lvalue (LHS), rvalue (EXPR))
-       for various EXPR.
-       We already have the lvalue for the LHS above, as "lhs_reg".  */
-    case ADDR_EXPR: /* LHS = &RHS;  */
-    case BIT_FIELD_REF:
-    case COMPONENT_REF: /* LHS = op0.op1;  */
-    case MEM_REF:
-    case REAL_CST:
-    case COMPLEX_CST:
-    case VECTOR_CST:
-    case INTEGER_CST:
-    case ARRAY_REF:
-    case SSA_NAME: /* LHS = VAR; */
-    case VAR_DECL: /* LHS = VAR; */
-    case PARM_DECL:/* LHS = VAR; */
-    case REALPART_EXPR:
-    case IMAGPART_EXPR:
-      return get_rvalue (rhs1, ctxt);
-
-    case ABS_EXPR:
-    case ABSU_EXPR:
-    case CONJ_EXPR:
-    case BIT_NOT_EXPR:
-    case FIX_TRUNC_EXPR:
-    case FLOAT_EXPR:
-    case NEGATE_EXPR:
-    case NOP_EXPR:
-    case VIEW_CONVERT_EXPR:
-      {
-	/* Unary ops.  */
-	const svalue2 *rhs_sval = get_rvalue (rhs1, ctxt);
-	const svalue2 *sval_unaryop
-	  = m_mgr->get_or_create_unaryop (TREE_TYPE (lhs), op, rhs_sval);
-	return sval_unaryop;
-      }
-
-    case EQ_EXPR:
-    case GE_EXPR:
-    case LE_EXPR:
-    case NE_EXPR:
-    case GT_EXPR:
-    case LT_EXPR:
-    case UNORDERED_EXPR:
-    case ORDERED_EXPR:
-      {
-	tree rhs2 = gimple_assign_rhs2 (assign);
-
-	// TODO: constraints between svalue2s
-	const svalue2 *rhs1_sval = get_rvalue (rhs1, ctxt);
-	const svalue2 *rhs2_sval = get_rvalue (rhs2, ctxt);
-
-	tristate t = eval_condition (rhs1_sval, op, rhs2_sval);
-	if (t.is_known ())
-	  return get_rvalue (t.is_true ()
-			     ? boolean_true_node
-			     : boolean_false_node,
-			     ctxt);
-	else
-	  {
-	    // TODO: symbolic value for binop
-	    const svalue2 *sval_binop
-	      = m_mgr->get_or_create_binop (TREE_TYPE (lhs), op,
-					    rhs1_sval, rhs2_sval);
-	    return sval_binop;
-	  }
-      }
-      break;
-
-    case PLUS_EXPR:
-    case MINUS_EXPR:
-    case MULT_EXPR:
-    case MULT_HIGHPART_EXPR:
-    case TRUNC_DIV_EXPR:
-    case CEIL_DIV_EXPR:
-    case FLOOR_DIV_EXPR:
-    case ROUND_DIV_EXPR:
-    case TRUNC_MOD_EXPR:
-    case CEIL_MOD_EXPR:
-    case FLOOR_MOD_EXPR:
-    case ROUND_MOD_EXPR:
-    case RDIV_EXPR:
-    case EXACT_DIV_EXPR:
-    case LSHIFT_EXPR:
-    case RSHIFT_EXPR:
-    case LROTATE_EXPR:
-    case RROTATE_EXPR:
-    case BIT_IOR_EXPR:
-    case BIT_XOR_EXPR:
-    case BIT_AND_EXPR:
-    case MIN_EXPR:
-    case MAX_EXPR:
-    case COMPLEX_EXPR:
-      {
-	/* Binary ops.  */
-	tree rhs2 = gimple_assign_rhs2 (assign);
-
-	const svalue2 *rhs1_sval = get_rvalue (rhs1, ctxt);
-	const svalue2 *rhs2_sval = get_rvalue (rhs2, ctxt);
-
-	const svalue2 *sval_binop
-	  = m_mgr->get_or_create_binop (TREE_TYPE (lhs), op,
-					rhs1_sval, rhs2_sval);
-	return sval_binop;
-      }
-
-    /* Vector expressions.  In theory we could implement these elementwise,
-       but for now, simply return unknown values.  */
-    case VEC_DUPLICATE_EXPR:
-    case VEC_SERIES_EXPR:
-    case VEC_COND_EXPR:
-    case VEC_PERM_EXPR:
-      return m_mgr->get_or_create_unknown_svalue2 (TREE_TYPE (lhs));
-    }
-}
-
-// FIXME:
-/* Update this model for the ASSIGN stmt, using CTXT to report any
-   diagnostics.  */
-
-bool
-region_model2::on_gassign (const gassign *assign,
-			   region_model2_context *ctxt)
-{
-  tree lhs = gimple_assign_lhs (assign);
-  tree rhs1 = gimple_assign_rhs1 (assign);
-
-  const region2 *lhs_reg = get_lvalue (lhs, ctxt);
-
-  /* Most assignments are handled by:
-       set_value (lhs_reg, SVALUE, CTXT)
-     for some SVALUE.  */
-  if (const svalue2 *sval = get_gassign_result (assign, ctxt))
-    {
-      set_value (lhs_reg, sval, ctxt);
-      return true;
-    }
-
-  enum tree_code op = gimple_assign_rhs_code (assign);
-  switch (op)
-    {
-    default:
-      {
-	if (1)
-	  sorry_at (assign->location, "unhandled assignment op: %qs",
-		    get_tree_code_name (op));
-	gcc_unreachable ();
-	//set_to_new_unknown_value (lhs_reg, TREE_TYPE (lhs), ctxt);
-      }
-      break;
-
-    case CONSTRUCTOR:
-      {
-	if (TREE_CLOBBER_P (rhs1))
-	  {
-	    /* e.g. "x ={v} {CLOBBER};"  */
-	    /* TODO: should we remove the value, or replace it with an
-	       "unknown" value?  */
-	    clobber_region (lhs_reg);
-	  }
-	else
-	  {
-	    /* Any CONSTRUCTOR that survives to this point is either
-	       just a zero-init of everything, or a vector.  */
-	    if (!CONSTRUCTOR_NO_CLEARING (rhs1))
-	      zero_fill_region (lhs_reg);
-	    unsigned ix;
-	    tree index;
-	    tree val;
-	    FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (rhs1), ix, index, val)
-	      {
-		/* FIXME: this code assumes we're dealing with a vector.  */
-		gcc_assert (TREE_CODE (TREE_TYPE (rhs1)) == VECTOR_TYPE);
-		if (!index)
-		  index = build_int_cst (integer_type_node, ix);
-		gcc_assert (TREE_CODE (index) == INTEGER_CST);
-		const svalue2 *index_sval
-		  = m_mgr->get_or_create_constant_svalue2 (index);
-		gcc_assert (index_sval);
-		const region2 *sub_reg
-		  = m_mgr->get_element_region (lhs_reg,
-					       TREE_TYPE (val),
-					       index_sval);
-		const svalue2 *val_sval = get_rvalue (val, ctxt);
-		set_value (sub_reg, val_sval, ctxt);
-	      }
-	  }
-      }
-      break;
-
-    case STRING_CST:
-      {
-	/* e.g. "struct s2 x = {{'A', 'B', 'C', 'D'}};".  */
-	/* Add a default binding, rather than a direct one, so that array
-	   access will "inherit" the individual chars.  */
-	const svalue2 *rhs_sval = get_rvalue (rhs1, ctxt);
-	m_store.set_value (m_mgr->get_store2_manager(), lhs_reg, rhs_sval,
-			   BK_default);
-      }
-      break;
-    }
-
-  return true;
-}
-
-/* FIXME.  */
-#if 0
-bool
-region_model2::on_gcall (const supernode *snode,
-			 const gcall *call)
-{
-  region_model2_context *ctxt = NULL; // FIXME
-
-  bool unknown_effects = false;
-
-  /* Debugging/test support.  */
-  if (is_special_named_call_p (call, "__analyzer_dump", 0))
-    {
-      /* Handle the builtin "__analyzer_dump" by dumping state
-	 to stderr.  */
-      dump (false);
-      dump (true);
-    }
-  else if (is_special_named_call_p (call, "__analyzer_eval", 1))
-    {
-      /* Handle the builtin "__analyzer_eval" by evaluating the input
-	 and dumping as a dummy warning, so that test cases can use
-	 dg-warning to validate the result (and so unexpected warnings will
-	 lead to DejaGnu failures).  */
-      tree t_arg = gimple_call_arg (call, 0);
-      tristate t
-	= eval_condition (t_arg,
-			  NE_EXPR,
-			  integer_zero_node,
-			  NULL/*&ctxt*/); // FIXME
-      warning_at (call->location, 0, "%s", t.as_string ());
-      return true;
-    }
-
-  if (tree callee_fndecl = get_fndecl_for_call (call, ctxt))
-    {
-      /* Get any left-hand side for the destination of the result.  */
-      region2 *lhs_reg = NULL;
-      tree lhs_type = NULL_TREE;
-      if (tree lhs = gimple_call_lhs (call))
-	{
-	  lhs_reg = get_lvalue (lhs, ctxt);
-	  lhs_type = TREE_TYPE (lhs);
-	}
-
-      if (is_named_call_p (callee_fndecl, "malloc", call, 1))
-	{
-	  tree size_arg = gimple_call_arg (call, 0);
-	  const svalue2 *size_sval = get_rvalue (size_arg, ctxt);
-	  region2 *new_reg = create_region_for_heap_alloc (size_sval);
-	  if (lhs_reg)
-	    {
-	      const svalue2 *ptr_sval = m_mgr->get_ptr_svalue2 (lhs_type, new_reg);
-	      set_value (lhs_reg, ptr_sval, ctxt);
-	    }
-	  else
-	    {
-	      // FIXME: report a leak here?
-	    }
-	  return true;
-	}
-      else if (is_named_call_p (callee_fndecl, "__builtin_alloca", call, 1))
-	{
-	  tree size_arg = gimple_call_arg (call, 0);
-	  const svalue2 *size_sval = get_rvalue (size_arg, ctxt);
-	  region2 *new_reg = create_region_for_alloca (size_sval);
-	  if (lhs_reg)
-	    {
-	      const svalue2 *ptr_sval = m_mgr->get_ptr_svalue2 (lhs_type, new_reg);
-	      set_value (lhs_reg, ptr_sval, ctxt);
-	    }
-	  return true;
-	}
-      else if (is_named_call_p (callee_fndecl, "strlen", call, 1))
-	{
-	  tree arg = gimple_call_arg (call, 0);
-	  const svalue2 *arg_sval = get_rvalue (arg, ctxt);
-	  region2 *buf_reg = deref_rvalue (arg_sval, ctxt);
-	  if (string_region2 *str_reg = buf_reg->dyn_cast_string_region2 ())
-	    {
-	      tree str_cst = str_reg->get_string_cst ();
-	      /* TREE_STRING_LENGTH is sizeof, not strlen.  */
-	      int sizeof_cst = TREE_STRING_LENGTH (str_cst);
-	      int strlen_cst = sizeof_cst - 1;
-	      tree t_cst = build_int_cst (lhs_type, strlen_cst);
-	      const svalue2 *result_sval
-		= m_mgr->get_or_create_constant_svalue2 (t_cst);
-	      set_value (lhs_reg, result_sval, ctxt);
-	      return true;
-	    }
-	  /* Otherwise an unknown value.  */
-	  return true;
-	}
-
-      if (!fndecl_has_gimple_body_p (callee_fndecl)
-	  && !DECL_PURE_P (callee_fndecl))
-	unknown_effects = true;
-    }
-  else
-    unknown_effects = true;
-
-  if (unknown_effects)
-    handle_unrecognized_call (call, ctxt);
-
-  return true;
-}
-#endif
-
-/* A subclass of pending_diagnostic for complaining about uses of
-   poisoned values.  */
-
-class poisoned_value_diagnostic2
-: public pending_diagnostic_subclass<poisoned_value_diagnostic2>
-{
-public:
-  poisoned_value_diagnostic2 (tree expr, enum poison_kind pkind)
-  : m_expr (expr), m_pkind (pkind)
-  {}
-
-  const char *get_kind () const FINAL OVERRIDE { return "poisoned_value_diagnostic2"; }
-
-  bool operator== (const poisoned_value_diagnostic2 &other) const
-  {
-    return m_expr == other.m_expr;
-  }
-
-  bool emit (rich_location *rich_loc) FINAL OVERRIDE
-  {
-    switch (m_pkind)
-      {
-      default:
-	gcc_unreachable ();
-      case POISON_KIND_FREED:
-	{
-	  diagnostic_metadata m;
-	  m.add_cwe (416); /* "CWE-416: Use After Free".  */
-	  return warning_meta (rich_loc, m,
-			       OPT_Wanalyzer_use_after_free,
-			       "use after %<free%> of %qE",
-			       m_expr);
-	}
-	break;
-      case POISON_KIND_POPPED_STACK:
-	{
-	  /* TODO: which CWE?  */
-	  return warning_at
-	    (rich_loc,
-	     OPT_Wanalyzer_use_of_pointer_in_stale_stack_frame,
-	     "dereferencing pointer %qE to within stale stack frame",
-	     m_expr);
-	}
-	break;
-      }
-  }
-
-  label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
-  {
-    switch (m_pkind)
-      {
-      default:
-	gcc_unreachable ();
-      case POISON_KIND_FREED:
-	return ev.formatted_print ("use after %<free%> of %qE here",
-				   m_expr);
-      case POISON_KIND_POPPED_STACK:
-	return ev.formatted_print
-	  ("dereferencing pointer %qE to within stale stack frame",
-	   m_expr);
-      }
-  }
-
-private:
-  tree m_expr;
-  enum poison_kind m_pkind;
-};
-
-/* Update this model for the CALL stmt, using CTXT to report any
-   diagnostics - the first half.
-
-   Updates to the region_model2 that should be made *before* sm-states
-   are updated are done here; other updates to the region_model2 are done
-   in region_model2::on_call_post.
-
-   Return true if the function call has unknown side effects (it wasn't
-   recognized and we don't have a body for it, or are unable to tell which
-   fndecl it is).  */
-
-bool
-region_model2::on_call_pre (const gcall *call, region_model2_context *ctxt)
-{
-  /* Check for uses of poisoned values.
-     For now, special-case "free", to avoid warning about "use-after-free"
-     when "double free" would be more precise.  */
-#if 0
-  if (!is_special_named_call_p (call, "free", 1))
-    for (unsigned i = 0; i < gimple_call_num_args (call); i++)
-      check_for_poison (gimple_call_arg (call, i), ctxt);
-#endif
-
-  bool unknown_side_effects = false;
-
-  if (tree callee_fndecl = get_fndecl_for_call (call, ctxt))
-    {
-      call_details cd (call, this, ctxt);
-
-      /* The various impl_call_* member functions are implemented
-	 in region-model2-impl-calls.cc.
-	 Having them split out into separate functions makes it easier
-	 to put breakpoints on the handling of specific functions.  */
-
-      /* FIXME: should we be returning bools from these,
-	 or dropping through?
-	 Existing behavior was to return "true", but that looks wrong.  */
-      if (is_named_call_p (callee_fndecl, "malloc", call, 1))
-	return impl_call_malloc (cd);
-      else if (is_named_call_p (callee_fndecl, "calloc", call, 2))
-	return impl_call_calloc (cd);
-      else if (is_named_call_p (callee_fndecl, "__builtin_alloca", call, 1))
-	return impl_call_alloca (cd);
-      else if (gimple_call_builtin_p (call, BUILT_IN_EXPECT)
-	       || gimple_call_builtin_p (call, BUILT_IN_EXPECT_WITH_PROBABILITY)
-	       || gimple_call_internal_p (call, IFN_BUILTIN_EXPECT))
-	return impl_call_builtin_expect (cd);
-      else if (is_named_call_p (callee_fndecl, "memset", call, 3)
-	       || gimple_call_builtin_p (call, BUILT_IN_MEMSET))
-	{
-	  impl_call_memset (cd);
-	  return false;
-	}
-      else if (is_named_call_p (callee_fndecl, "strlen", call, 1))
-	{
-	  if (impl_call_strlen (cd))
-	    return false;
-	}
-      else if (!fndecl_has_gimple_body_p (callee_fndecl)
-	       && !DECL_PURE_P (callee_fndecl)
-	       && !fndecl_built_in_p (callee_fndecl))
-	unknown_side_effects = true;
-    }
-  else
-    unknown_side_effects = true;
-
-  /* Some of the above cases update the lhs of the call based on the
-     return value.  If we get here, it hasn't been done yet, so do that
-     now.  */
-  if (tree lhs = gimple_call_lhs (call))
-    {
-      const region2 *lhs_region = get_lvalue (lhs, ctxt);
-      // FIXME
-      if (TREE_CODE (lhs) == SSA_NAME)
-	{
-	  const svalue2 *sval = m_mgr->get_or_create_initial_value (lhs_region);
-	  set_value (lhs_region, sval, ctxt);
-	}
-    }
-
-#if 0
-  if (lhs_reg)
-    set_to_new_unknown_value (lhs_reg, lhs_type, ctxt);
-#endif
-
-  return unknown_side_effects;
-}
-
-/* Update this model for the CALL stmt, using CTXT to report any
-   diagnostics - the second half.
-
-   Updates to the region_model2 that should be made *after* sm-states
-   are updated are done here; other updates to the region_model2 are done
-   in region_model2::on_call_pre.
-
-   If UNKNOWN_SIDE_EFFECTS is true, also call handle_unrecognized_call
-   to purge state.  */
-
-void
-region_model2::on_call_post (const gcall *call,
-			     bool unknown_side_effects,
-			     region_model2_context *ctxt)
-{
-  if (tree callee_fndecl = get_fndecl_for_call (call, ctxt))
-    if (is_named_call_p (callee_fndecl, "free", call, 1))
-      {
-	call_details cd (call, this, ctxt);
-	impl_call_free (cd);
-	return;
-      }
-
-  if (unknown_side_effects)
-    handle_unrecognized_call (call, ctxt);
-}
-
-/* Handle a call CALL to a function with unknown behavior.
-
-   Traverse the region2s in this model, determining what region2s are
-   reachable from pointer arguments to CALL and from global variables,
-   recursively.
-
-   Set all reachable region2s to new unknown values and purge sm-state
-   from their values, and from values that point to them.  */
-void
-region_model2::handle_unrecognized_call (const gcall *call,
-					 region_model2_context *ctxt)
-{
-  tree fndecl = get_fndecl_for_call (call, ctxt);
-
-  reachable_region2s reachable_regs (&m_store, m_mgr);
-
-  /* Determine the reachable region2s and their mutability.  */
-  {
-    /* Add globals and regions that already escaped in previous
-       unknown calls.  */
-    m_store.for_each_cluster (reachable_region2s::init_cluster_cb,
-			      &reachable_regs);
-
-    /* Params that are pointers.  */
-    tree iter_param_types = NULL_TREE;
-    if (fndecl)
-      iter_param_types = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
-    for (unsigned arg_idx = 0; arg_idx < gimple_call_num_args (call); arg_idx++)
-      {
-	/* Track expected param type, where available.  */
-	tree param_type = NULL_TREE;
-	if (iter_param_types)
-	  {
-	    param_type = TREE_VALUE (iter_param_types);
-	    gcc_assert (param_type);
-	    iter_param_types = TREE_CHAIN (iter_param_types);
-	  }
-
-	tree parm = gimple_call_arg (call, arg_idx);
-	const svalue2 *parm_sval = get_rvalue (parm, ctxt);
-	reachable_regs.handle_parm (parm_sval, param_type);
-	// FIXME: what about compound parms that contain ptrs?
-      }
-  }
-
-  /* Purge sm-state for the svalues that were reachable,
-     both in non-mutable and mutable form.  */
-  for (hash_set<const svalue2 *>::iterator iter
-	 = reachable_regs.begin_reachable_svals ();
-       iter != reachable_regs.end_reachable_svals (); ++iter)
-    {
-      const svalue2 *sval = (*iter);
-      ctxt->on_unknown_change (sval, false);
-    }
-  for (hash_set<const svalue2 *>::iterator iter
-	 = reachable_regs.begin_mutable_svals ();
-       iter != reachable_regs.end_mutable_svals (); ++iter)
-    {
-      const svalue2 *sval = (*iter);
-      ctxt->on_unknown_change (sval, true);
-    }
-
-  /* Mark any clusters that have escaped.  */
-  reachable_regs.mark_escaped_clusters (m_mgr->get_store2_manager ());
-
-  /* Update bindings for all clusters that have escaped, whether above,
-     or previously.  */
-  m_store.on_unknown_fncall (call, m_mgr->get_store2_manager ());
-
-#if 0
-  /* Purge sm-state for any remaining svalue2s that point to region2s that
-     were reachable.  This helps suppress leak false-positives.
-
-     For example, if we had a malloc call that was cast to a "foo *" type,
-     we could have a temporary void * for the result of malloc which has its
-     own svalue2, not reachable from the function call, but for which the
-     "foo *" svalue2 was reachable.  If we don't purge it, the temporary will
-     be reported as a leak.  */
-  int i;
-  const svalue2 *svalue2;
-  FOR_EACH_VEC_ELT (m_svalue2s, i, svalue2)
-    if (region_const svalue2 *ptr = svalue2->dyn_cast_region_svalue2 ())
-      {
-	region2 *pointee_reg = ptr->get_pointee ();
-	if (reachable_region2s.mutable_p (pointee_reg))
-	  ctxt->on_unknown_change (svalue2_id::from_int (i));
-      }
-
-  validate ();
-#endif
-}
-
-/* FIXME.  */
-
-static void
-add_svalues_to_set_cb (const svalue2 *sval, hash_set<const svalue2 *> *out)
-{
-  out->add (sval);
-}
-
-/* FIXME.
-   Traverse the region2s in this model, determining what region2s are
-   reachable from the store.
-
-   If EXTRA_SVAL is non-NULL, treat it as an additional "root"
-   for reachability (for handling return values from functions when
-   analyzing return of the only function on the stack).
-
-   Find svalues that haven't leaked.    */
-
-void
-region_model2::get_reachable_svalues (hash_set<const svalue2 *> *out,
-				      const svalue2 *extra_sval)
-{
-  reachable_region2s reachable_regs (&m_store, m_mgr);
-
-  /* Add globals and regions that already escaped in previous
-     unknown calls.  */
-  m_store.for_each_cluster (reachable_region2s::init_cluster_cb,
-			    &reachable_regs);
-
-  if (extra_sval)
-    reachable_regs.handle_sval (extra_sval);
-
-#if 0
-  /* Mark all locals in all frames (and what's reachable from them).  */
-  for (const frame_region2 *iter_frame = get_current_frame ();
-       iter_frame; iter_frame = iter_frame->get_calling_frame ())
-    {
-      region_model2_context *ctxt = NULL; // FIXME
-      unsigned i;
-      tree name;
-      FOR_EACH_SSA_NAME (i, name, iter_frame->get_function ())
-	{
-	  /* Ignore the ".MEM" SSA names.  */
-	  if (tree var = SSA_NAME_VAR (name))
-	    if (TREE_CODE (var) == VAR_DECL)
-	      if (VAR_DECL_IS_VIRTUAL_OPERAND (var))
-		continue;
-	  const region2 *name_reg
-	    = iter_frame->get_region_for_local (m_mgr, name, ctxt);
-	  reachable_regs.add (name_reg, false);
-	}
-      //#error
-      /* FIXME: handle both initial values (but not for uninit values)
-	 and explicitly bound values.  */
-      /* FIXME: we need to get at all live SSA names in the frame
-	 that might have values; is there a way to do this?  */
-      // (and all parms/locals)
-    }
-#else
-  /* Get regions for locals that have explicitly bound values.  */
-  for (store2::cluster_map_t::iterator iter = m_store.begin ();
-       iter != m_store.end (); ++iter)
-    {
-      const region2 *base_reg = (*iter).first;
-      if (const region2 *parent = base_reg->get_parent_region ())
-	if (parent->get_kind () == region2::RK_FRAME)
-	  reachable_regs.add (base_reg, false);
-    }
-  /* TODO: we also need to count the regions for live SSA names
-     (and params) at the current program point, for those that
-     still have their initial values (but not for uninit values).  */
-#endif
-
-#if 1
-  /* Populate *OUT based on the values that were reachable.  */
-  for (hash_set<const svalue2 *>::iterator iter
-	 = reachable_regs.begin_reachable_svals ();
-       iter != reachable_regs.end_reachable_svals (); ++iter)
-    {
-      out->add (*iter);
-    }
-#else
-  /* Populate *OUT based on the clusters that were reachable.  */
-  for (hash_set<const region2 *>::iterator iter = reachable_regs.begin ();
-       iter != reachable_regs.end (); ++iter)
-    {
-      const region2 *base_reg = (*iter);
-      binding_cluster2 *cluster = m_store.get_cluster (base_reg);
-      if (cluster)
-	cluster->for_each_value (add_svalues_to_set_cb, out);
-      else
-	{
-	  /* If not explicitly bound (or escaped), add initial svalue.
-	     FIXME: what about uninit?  */
-	  const svalue2 *init = m_mgr->get_or_create_initial_value (base_reg);
-	  out->add (init);
-	}
-    }
-#endif
-}
-
-/* Update this model for the RETURN_STMT, using CTXT to report any
-   diagnostics.  */
-
-bool
-region_model2::on_greturn (const greturn *return_stmt,
-			   region_model2_context *ctxt)
-{
-  tree callee = get_current_function ()->decl;
-  tree lhs = DECL_RESULT (callee);
-  tree rhs = gimple_return_retval (return_stmt);
-
-  if (lhs && rhs)
-    copy_region (get_lvalue (lhs, ctxt),
-		 get_lvalue (rhs, ctxt),
-		 ctxt);
-  return true;
-}
-
-/* Update this model for a call and return of setjmp/sigsetjmp at CALL within
-   ENODE, using CTXT to report any diagnostics.
-
-   This is for the initial direct invocation of setjmp/sigsetjmp (which returns
-   0), as opposed to any second return due to longjmp/sigsetjmp.  */
-
-void
-region_model2::on_setjmp (const gcall *call, const exploded_node *enode,
-			 region_model2_context *ctxt)
-{
-  const svalue2 *buf_ptr = get_rvalue (gimple_call_arg (call, 0), ctxt);
-  const region2 *buf_reg = deref_rvalue (buf_ptr, gimple_call_arg (call, 0),
-					 ctxt);
-
-  /* Create a setjmp_svalue2 for this call and store it in BUF_REG's
-     region2.  */
-  if (buf_reg)
-    {
-      setjmp_record r (enode, call);
-      const svalue2 *sval
-	= m_mgr->get_or_create_setjmp_svalue2 (r, buf_reg->get_type ());
-      set_value (buf_reg, sval, ctxt);
-    }
-
-  /* Direct calls to setjmp return 0.  */
-  if (tree lhs = gimple_call_lhs (call))
-    {
-      tree zero = build_int_cst (TREE_TYPE (lhs), 0);
-      const svalue2 *new_sval = m_mgr->get_or_create_constant_svalue2 (zero);
-      const region2 *lhs_reg = get_lvalue (lhs, ctxt);
-      set_value (lhs_reg, new_sval, ctxt);
-    }
-}
-
-/* Update this region_model2 for rewinding from a "longjmp" at LONGJMP_CALL
-   to a "setjmp" at SETJMP_CALL where the final stack depth should be
-   SETJMP_STACK_DEPTH.  Pop any stack frames.  Leak detection is *not*
-   done, and should be done by the caller.  */
-
-void
-region_model2::on_longjmp (const gcall *longjmp_call, const gcall *setjmp_call,
-			   int setjmp_stack_depth, region_model2_context *ctxt)
-{
-  /* Evaluate the val, using the frame of the "longjmp".  */
-  tree fake_retval = gimple_call_arg (longjmp_call, 1);
-  const svalue2 *fake_retval_sval = get_rvalue (fake_retval, ctxt);
-
-  /* Pop any frames until we reach the stack depth of the function where
-     setjmp was called.  */
-  gcc_assert (get_stack_depth () >= setjmp_stack_depth);
-  while (get_stack_depth () > setjmp_stack_depth)
-    {
-      /* Don't purge unused svalue2s yet, as we're using fake_retval_sval.  */
-      pop_frame (NULL, false, NULL, NULL, ctxt);
-    }
-
-  gcc_assert (get_stack_depth () == setjmp_stack_depth);
-
-  /* Assign to LHS of "setjmp" in new_state.  */
-  if (tree lhs = gimple_call_lhs (setjmp_call))
-    {
-      /* Passing 0 as the val to longjmp leads to setjmp returning 1.  */
-      tree t_zero = build_int_cst (TREE_TYPE (fake_retval), 0);
-      const svalue2 *zero_sval = m_mgr->get_or_create_constant_svalue2 (t_zero);
-      tristate eq_zero = eval_condition (fake_retval_sval, EQ_EXPR, zero_sval);
-      /* If we have 0, use 1.  */
-      if (eq_zero.is_true ())
-	{
-	  tree t_one = build_int_cst (TREE_TYPE (fake_retval), 1);
-	  const svalue2 *one_sval
-	    = m_mgr->get_or_create_constant_svalue2 (t_one);
-	  fake_retval_sval = one_sval;
-	}
-      else
-	{
-	  /* Otherwise note that the value is nonzero.  */
-	  m_constraints2->add_constraint (fake_retval_sval, NE_EXPR, zero_sval);
-	}
-
-      /* Decorate the return value from setjmp as being unmergeable,
-	 so that we don't attempt to merge states with it as zero
-	 with states in which it's nonzero, leading to a clean distinction
-	 in the exploded_graph betweeen the first return and the second
-	 return.  */
-      fake_retval_sval = m_mgr->get_or_create_unmergeable (fake_retval_sval);
-
-      const region2 *lhs_reg = get_lvalue (lhs, ctxt);
-      set_value (lhs_reg, fake_retval_sval, ctxt);
-    }
-}
-
-/* Update this region_model2 for a phi stmt of the form
-     LHS = PHI <...RHS...>.
-   where RHS is for the appropriate edge.  */
-
-void
-region_model2::handle_phi (const gphi *phi,
-			   tree lhs, tree rhs, bool is_back_edge,
-			   region_model2_context *ctxt)
-{
-  /* For now, don't bother tracking the .MEM SSA names.  */
-  if (tree var = SSA_NAME_VAR (lhs))
-    if (TREE_CODE (var) == VAR_DECL)
-      if (VAR_DECL_IS_VIRTUAL_OPERAND (var))
-	return;
-
-  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:
-      /* If we have a back edge, we probably have a loop.
-	 Use an unknown value, to avoid effectively unrolling the
-	 loop.
-	 To terminate, we need to avoid generating a series of
-	 models with an unbounded monotonically increasing number of
-	 redundant unknown values; hence we need to purge svalue2s
-	 before inserting the state into the exploded graph, to
-	 collect unused svalue2s.  */
-      mark_region_as_unknown (get_lvalue (lhs, ctxt));
-    }
-  else
-#endif
-    set_value (get_lvalue (lhs, ctxt), rhs_sval, ctxt);
-
-  if (ctxt)
-    ctxt->on_phi (phi, rhs);
-}
-
-/* Implementation of region_model2::get_lvalue; the latter adds type-checking.
-
-   Get the id of the region2 for PV within this region_model2,
-   emitting any diagnostics to CTXT.  */
-
-const region2 *
-region_model2::get_lvalue_1 (path_var pv,
-			     region_model2_context *ctxt)
-{
-  tree expr = pv.m_tree;
-
-  gcc_assert (expr);
-
-
-  switch (TREE_CODE (expr))
-    {
-    default:
-#if 1
-      return m_mgr->get_region2_for_unexpected_tree_code (ctxt, expr,
-							  dump_location_t ());
-#else
-      gcc_unreachable (); // FIXME
-#endif
-
-    case ARRAY_REF:
-      {
-	tree array = TREE_OPERAND (expr, 0);
-	tree index = TREE_OPERAND (expr, 1);
-#if 0
-	// TODO: operands 2 and 3, if present:
-	gcc_assert (TREE_OPERAND (expr, 2) == NULL_TREE);
-	gcc_assert (TREE_OPERAND (expr, 3) == NULL_TREE);
-#endif
-
-	const region2 *array_reg = get_lvalue (array, ctxt);
-	const svalue2 *index_sval = get_rvalue (index, ctxt);
-	return m_mgr->get_element_region (array_reg,
-					  TREE_TYPE (TREE_TYPE (array)),
-					  index_sval);
-#if 0
-	const region2 *base_array_reg = get_region2 (array_reg);
-	array_region2 *array_reg  = base_array_reg->dyn_cast_array_region2 ();
-	if (!array_reg)
-	  {
-	    /* Normally, array_reg ought to refer to an array_region2, since
-	       array's type will be ARRAY_TYPE.  However, if we have an
-	       unexpected tree code for array, we could have a
-	       symbolic_region2 here.  If so, we're in error-handling. */
-	    gcc_assert (base_array_reg->get_type () == NULL_TREE);
-	    return make_region2_for_unexpected_tree_code (ctxt, expr,
-							 dump_location_t ());
-	  }
-	return array_reg->get_element (this, array_reg, index_sval, ctxt);
-#endif
-      }
-      break;
-
-#if 0
-    case BIT_FIELD_REF:
-      {
-	/* For now, create a view, as if a cast, ignoring the bit positions.  */
-	tree obj = TREE_OPERAND (expr, 0);
-	return get_or_create_view (get_lvalue (obj, ctxt), TREE_TYPE (expr),
-				   ctxt);
-      };
-      break;
-#endif
-    case MEM_REF:
-      {
-	tree ptr = TREE_OPERAND (expr, 0);
-	tree offset = TREE_OPERAND (expr, 1);
-	const svalue2 *ptr_sval = get_rvalue (ptr, ctxt);
-	const svalue2 *offset_sval = get_rvalue (offset, ctxt);
-	const region2 *star_ptr = deref_rvalue (ptr_sval, ptr, ctxt);
-	return m_mgr->get_offset_region (star_ptr,
-					 TREE_TYPE (expr),
-					 offset_sval);
-      }
-      break;
-
-    case FUNCTION_DECL:
-      return m_mgr->get_region_for_fndecl (expr);
-
-    case LABEL_DECL:
-      return m_mgr->get_region_for_label (expr);
-
-    case VAR_DECL:
-      /* Handle globals.  */
-      if (is_global_var (expr))
-	return m_mgr->get_region_for_global (expr);
-
-      /* Fall through.  */
-
-    case SSA_NAME:
-    case PARM_DECL:
-    case RESULT_DECL:
-      {
-	gcc_assert (TREE_CODE (expr) == SSA_NAME
-		    || TREE_CODE (expr) == PARM_DECL
-		    || TREE_CODE (expr) == VAR_DECL
-		    || TREE_CODE (expr) == RESULT_DECL);
-
-	int stack_index = pv.m_stack_depth;
-	const frame_region2 *frame = get_frame_at_index (stack_index);
-	gcc_assert (frame);
-	return frame->get_region_for_local (m_mgr, expr);
-      }
-
-    case COMPONENT_REF:
-      {
-	/* obj.field  */
-	tree obj = TREE_OPERAND (expr, 0);
-	tree field = TREE_OPERAND (expr, 1);
-	//tree obj_type = TREE_TYPE (obj);
-	// FIXME:
-#if 0
-	if (TREE_CODE (obj_type) != RECORD_TYPE
-	    && TREE_CODE (obj_type) != UNION_TYPE)
-	  return make_region2_for_unexpected_tree_code (ctxt, obj_type,
-						       dump_location_t ());
-#endif
-	const region2 *obj_reg = get_lvalue (obj, ctxt);
-	return m_mgr->get_field_region (obj_reg, field);
-      }
-      break;
-#if 0
-    case CONST_DECL:
-      {
-	tree cst_type = TREE_TYPE (expr);
-	const region2 *cst_reg
-	  = add_region2_for_type (m_root_reg, cst_type, ctxt);
-	if (tree value = DECL_INITIAL (expr))
-	  {
-	    const svalue2 *sval = get_rvalue (value, ctxt);
-	    get_region2 (cst_reg)->set_value (*this, cst_reg, sid, ctxt);
-	  }
-	return cst_reg;
-      }
-      break;
-#endif
-
-    case STRING_CST:
-      return m_mgr->get_region_for_string (expr);
-
-#if 0
-    case NOP_EXPR:
-    case VIEW_CONVERT_EXPR:
-      {
-	tree obj = TREE_OPERAND (expr, 0);
-	return get_or_create_view (get_lvalue (obj, ctxt), TREE_TYPE (expr),
-				   ctxt);
-      };
-      break;
-#endif
-    }
-}
-
-/* Assert that SRC_TYPE can be converted to DST_TYPE as a no-op.  */
-
-static void
-assert_compat_types (tree src_type, tree dst_type)
-{
-  if (src_type && dst_type && !VOID_TYPE_P (dst_type))
-    if (!(useless_type_conversion_p (src_type, dst_type)))
-      internal_error ("incompatible types: %qT and %qT", src_type, dst_type);
-}
-
-/* Get the region2 for PV within this region_model2,
-   emitting any diagnostics to CTXT.  */
-
-const region2 *
-region_model2::get_lvalue (path_var pv,
-			   region_model2_context *ctxt)
-{
-  if (pv.m_tree == NULL_TREE)
-    return NULL;
-
-  const region2 *result_reg = get_lvalue_1 (pv, ctxt);
-  assert_compat_types (result_reg->get_type (), TREE_TYPE (pv.m_tree));
-  return result_reg;
-}
-
-/* Get the region2 for EXPR within this region_model2 (assuming the most
-   recent stack frame if it's a local).  */
-
-const region2 *
-region_model2::get_lvalue (tree expr,
-			   region_model2_context *ctxt)
-{
-  return get_lvalue (path_var (expr, get_stack_depth () - 1), ctxt);
-}
-
-/* Implementation of region_model2::get_rvalue; the latter adds type-checking.
-
-   Get the value of PV within this region_model2,
-   emitting any diagnostics to CTXT.  */
-
-const svalue2 *
-region_model2::get_rvalue_1 (path_var pv,
-			     region_model2_context *ctxt)
-{
-  gcc_assert (pv.m_tree);
-
-  switch (TREE_CODE (pv.m_tree))
-    {
-    default:
-      {
-	gcc_unreachable ();
-#if 0
-	const svalue2 *unknown_sval = new unknown_svalue2 (TREE_TYPE (pv.m_tree));
-	return add_svalue2 (unknown_sval);
-#endif
-      }
-      break;
-
-    case ADDR_EXPR:
-      {
-	/* "&EXPR".  */
-	tree expr = pv.m_tree;
-	tree op0 = TREE_OPERAND (expr, 0);
-#if 0
-	if (TREE_CODE (op0) == FUNCTION_DECL)
-	  return m_mgr->get_svalue2_for_fndecl (TREE_TYPE (expr), op0, ctxt);
-	else if (TREE_CODE (op0) == LABEL_DECL)
-	  return m_mgr->get_svalue2_for_label (TREE_TYPE (expr), op0, ctxt);
-#endif
-	const region2 *expr_reg = get_lvalue (op0, ctxt);
-	return m_mgr->get_ptr_svalue2 (TREE_TYPE (expr), expr_reg);
-      }
-      break;
-
-    case BIT_FIELD_REF:
-      {
-#if 0
-	tree expr = pv.m_tree;
-	const region2 *obj = get_lvalue (TREE_OPERAND (expr, 0), ctxt);
-	const svalue2 *num_bits = get_rvalue (TREE_OPERAND (expr, 1), ctxt);
-	const svalue2 *bit_offset = get_rvalue (TREE_OPERAND (expr, 2), ctxt);
-	/* FIXME: attempting to add bitwise access will ultimately require
-	   a new svalue2 type for accessing bits from another svalue2
-	   (e.g. to handle the case where the store has no binding for the
-	   bits), and probably a new region2 type also (or maybe just the
-	   latter, using a sub_svalue2 for the former).
-	   Is it possible to attempting to spot when we're accessing known
-	   values, and have this possibly return NULL?  */
-	return maybe_get_store_bits (TREE_TYPE (expr), obj, num_bits,
-				     bit_offset);
-#else
-	return m_mgr->get_or_create_unknown_svalue2 (TREE_TYPE (pv.m_tree));
-#endif
-      }
-      break;
-
-    case SSA_NAME:
-    case VAR_DECL:
-    case PARM_DECL:
-    case RESULT_DECL:
-    case ARRAY_REF:
-      {
-	const region2 *element_reg = get_lvalue (pv, ctxt);
-	return get_store_value (element_reg);
-      }
-
-    case REALPART_EXPR:
-    case IMAGPART_EXPR:
-    case VIEW_CONVERT_EXPR:
-      {
-	tree expr = pv.m_tree;
-	tree arg = TREE_OPERAND (expr, 0);
-	const svalue2 *arg_sval = get_rvalue (arg, ctxt);
-	const svalue2 *sval_unaryop
-	  = m_mgr->get_or_create_unaryop (TREE_TYPE (expr), TREE_CODE (expr),
-					  arg_sval);
-	return sval_unaryop;
-      };
-
-    case INTEGER_CST:
-    case REAL_CST:
-    case COMPLEX_CST:
-    case VECTOR_CST:
-    case STRING_CST:
-      return m_mgr->get_or_create_constant_svalue2 (pv.m_tree);
-
-    case POINTER_PLUS_EXPR:
-	{
-	  tree expr = pv.m_tree;
-	  tree ptr = TREE_OPERAND (expr, 0);
-	  tree offset = TREE_OPERAND (expr, 1);
-	  const svalue2 *ptr_sval = get_rvalue (ptr, ctxt);
-	  const svalue2 *offset_sval = get_rvalue (offset, ctxt);
-	  const svalue2 *sval_binop
-	    = m_mgr->get_or_create_binop (TREE_TYPE (expr), POINTER_PLUS_EXPR,
-					  ptr_sval, offset_sval);
-	  return sval_binop;
-	}
-
-    /* Binary ops.  */
-    case PLUS_EXPR:
-    case MULT_EXPR:
-	{
-	  tree expr = pv.m_tree;
-	  tree arg0 = TREE_OPERAND (expr, 0);
-	  tree arg1 = TREE_OPERAND (expr, 1);
-	  const svalue2 *arg0_sval = get_rvalue (arg0, ctxt);
-	  const svalue2 *arg1_sval = get_rvalue (arg1, ctxt);
-	  const svalue2 *sval_binop
-	    = m_mgr->get_or_create_binop (TREE_TYPE (expr), TREE_CODE (expr),
-					  arg0_sval, arg1_sval);
-	  return sval_binop;
-	}
-
-    case COMPONENT_REF:
-    case MEM_REF:
-      {
-	const region2 *ref_reg = get_lvalue (pv, ctxt);
-	return get_store_value (ref_reg);
-      }
-    }
-}
-
-/* Get the value of PV within this region_model2,
-   emitting any diagnostics to CTXT.  */
-
-const svalue2 *
-region_model2::get_rvalue (path_var pv,
-			   region_model2_context *ctxt)
-{
-  if (pv.m_tree == NULL_TREE)
-    return NULL;
-
-  const svalue2 *result_sval = get_rvalue_1 (pv, ctxt);
-
-  assert_compat_types (result_sval->get_type (), TREE_TYPE (pv.m_tree));
-
-  return result_sval;
-}
-
-/* Get the value of EXPR within this region_model2 (assuming the most
-   recent stack frame if it's a local).  */
-
-const svalue2 *
-region_model2::get_rvalue (tree expr,
-			   region_model2_context *ctxt)
-{
-  return get_rvalue (path_var (expr, get_stack_depth () - 1), ctxt);
-}
-
-/* FIXME.  */
-
-const svalue2 *
-region_model2::get_store_value (const region2 *reg) const
-{
-  const svalue2 *sval
-    = m_store.get_any_binding (m_mgr->get_store2_manager (), reg);
-  if (sval)
-    {
-      if (reg->get_type ())
-	sval = m_mgr->get_or_create_cast (reg->get_type (), sval);
-      return sval;
-    }
-
-  /* Special-case: read at a constant index within a STRING_CST.  */
-  if (const offset_region2 *offset_reg = reg->dyn_cast_offset_region2 ())
-    if (tree byte_offset_cst
-	  = offset_reg->get_byte_offset ()->maybe_get_constant ())
-      if (const string_region2 *str_reg
-	  = reg->get_parent_region ()->dyn_cast_string_region2 ())
-	{
-	  tree string_cst = str_reg->get_string_cst ();
-	  if (const svalue2 *char_sval
-		= maybe_get_char_from_string_cst (string_cst, byte_offset_cst,
-						  m_mgr))
-	    return m_mgr->get_or_create_cast (reg->get_type (), char_sval);
-	}
-
-  /* Special-case: read the initial char of a STRING_CST.  */
-  // FIXME: but only if the cast is to a char?
-  if (const cast_region2 *cast_reg = reg->dyn_cast_cast_region2 ())
-    if (const string_region2 *str_reg
-	= cast_reg->get_original_region ()->dyn_cast_string_region2 ())
-      if (reg->get_type ()) // FIXME
-      {
-	tree string_cst = str_reg->get_string_cst ();
-	tree byte_offset_cst = build_int_cst (integer_type_node, 0);
-	if (const svalue2 *char_sval
-	    = maybe_get_char_from_string_cst (string_cst, byte_offset_cst,
-					      m_mgr))
-	  return m_mgr->get_or_create_cast (reg->get_type (), char_sval);
-      }
-
-  /* Otherwise we implicitly have the initial value of the region.  */
-
-  /* Special-case: to avoid having to explicitly update all previously
-     untracked globals when calling an unknown fn, we instead change
-     the default here so we implicitly have an unknown value for such
-     regions.  */
-  // FIXME: what about symbolic writes also?
-  if (m_store.called_unknown_fn_p ())
-    if (reg->get_base_region ()->get_parent_region ()->get_kind ()
-	== region2::RK_GLOBALS)
-      return m_mgr->get_or_create_unknown_svalue2 (reg->get_type ());
-
-  /* FIXME: not if touched by symbolic value; handled in
-     binding_cluster2::get_any_binding.  */
-  return m_mgr->get_or_create_initial_value (reg);
-}
-
-/* Return false if REG does not exist, true if it may do.
-   This is for detecting regions within the stack that don't exist anymore
-   after frames are popped.  */
-
-bool
-region_model2::region_exists_p (const region2 *reg) const
-{
-  /* If within a stack frame, check that the stack frame is live.  */
-  if (const frame_region2 *enclosing_frame = reg->maybe_get_frame_region ())
-    {
-      /* Check that the current frame is the enclosing frame, or is called
-	 by it.  */
-      for (const frame_region2 *iter_frame = get_current_frame (); iter_frame;
-	   iter_frame = iter_frame->get_calling_frame ())
-	if (iter_frame == enclosing_frame)
-	  return true;
-      return false;
-    }
-
-  return true;
-}
-
-/* Build a cast of SRC_EXPR to DST_TYPE, or return NULL_TREE.
-
-   Adapted from gcc::jit::playback::context::build_cast, which in turn is
-   adapted from
-     - c/c-typeck.c:build_c_cast
-     - c/c-convert.c: convert
-     - convert.h
-   Only some kinds of cast are currently supported here.  */
-#if 0
-static tree
-build_cast (tree dst_type, tree src_expr)
-{
-  tree result = targetm.convert_to_type (dst_type, src_expr);
-  if (result)
-    return result;
-  enum tree_code dst_code = TREE_CODE (dst_type);
-  switch (dst_code)
-    {
-    case INTEGER_TYPE:
-    case ENUMERAL_TYPE:
-      result = convert_to_integer (dst_type, src_expr);
-      goto maybe_fold;
-
-    case BOOLEAN_TYPE:
-      /* Compare with c_objc_common_truthvalue_conversion and
-	 c_common_truthvalue_conversion. */
-      /* For now, convert to: (src_expr != 0)  */
-      result = build2 (NE_EXPR, dst_type,
-		       src_expr,
-		       build_int_cst (TREE_TYPE (src_expr), 0));
-      goto maybe_fold;
-
-    case REAL_TYPE:
-      result = convert_to_real (dst_type, src_expr);
-      goto maybe_fold;
-
-    case POINTER_TYPE:
-      result = build1 (NOP_EXPR, dst_type, src_expr);
-      goto maybe_fold;
-
-    default:
-      return NULL_TREE;
-
-    maybe_fold:
-      if (TREE_CODE (result) != C_MAYBE_CONST_EXPR)
-	result = fold (result);
-      return result;
-    }
-}
-#endif
-
-/* If the type of SID's underlying value is DST_TYPE, return SID.
-   Otherwise, attempt to create (or reuse) an svalue2 representing an access
-   of SID as a DST_TYPE and return that value's svalue2_id.  */
-#if 0
-svalue2_id
-region_model2::maybe_cast_1 (tree dst_type, const svalue2 *sval)
-{
-  const svalue2 *sval = get_svalue2 (sid);
-  tree src_type = sval->get_type ();
-  if (src_type == dst_type)
-    return sid;
-
-  if (POINTER_TYPE_P (dst_type)
-      || POINTER_TYPE_P (src_type))
-    {
-      /* Pointer to region2.  */
-      if (region_svalue2 *ptr_sval = sval->dyn_cast_region_svalue2 ())
-	return get_or_create_ptr_svalue2 (dst_type, ptr_sval->get_pointee ());
-
-      /* Unknown pointer?   Get or create a new unknown pointer of the
-	 correct type, preserving the equality between the pointers.  */
-      if (sval->dyn_cast_unknown_svalue2 ())
-	{
-	  equiv_class &ec = m_constraints->get_equiv_class (sid);
-
-	  /* Look for an existing pointer of the correct type within the EC.  */
-	  int i;
-	  const svalue2 **equiv_sval;
-	  FOR_EACH_VEC_ELT (ec.m_vars, i, equiv_sval)
-	    {
-	      const svalue2 *equiv_val = get_svalue2 (*equiv_sval);
-	      if (equiv_val->get_type () == dst_type)
-		return *equiv_sval;
-	    }
-
-	  /* Otherwise, create a new unknown pointer of the correct type.  */
-	  const svalue2 *unknown_sval = new unknown_svalue2 (dst_type);
-	  const svalue2 *new_ptr_sval = add_svalue2 (unknown_sval);
-	  m_constraints->add_constraint (sid, EQ_EXPR, new_ptr_sval);
-	  return new_ptr_sval;
-	}
-    }
-
-  /* Attempt to cast constants.  */
-  if (tree src_cst = sval->maybe_get_constant ())
-    {
-      if (tree dst = build_cast (dst_type, src_cst))
-	if (CONSTANT_CLASS_P (dst))
-	  return get_or_create_constant_svalue2 (dst);
-    }
-
-  /* Otherwise, return a new unknown value.  */
-  const svalue2 *unknown_sval = new unknown_svalue2 (dst_type);
-  return add_svalue2 (unknown_sval);
-}
-
-/* If the type of SID's underlying value is DST_TYPE, return SID.
-   Otherwise, attempt to create (or reuse) an svalue2 representing an access
-   of SID as a DST_TYPE and return that value's svalue2_id.
-
-   If the result != SID, then call CTXT's on_cast vfunc (if CTXT is non-NULL),
-   so that sm-state can be propagated from SID to the result.  */
-
-svalue2_id
-region_model2::maybe_cast (tree dst_type, const svalue2 *sval,
-			  region_model2_context *ctxt)
-{
-  const svalue2 *result = maybe_cast_1 (dst_type, sid);
-  if (result != sid)
-    if (ctxt)
-      {
-	/* Notify ctxt about a cast, so any sm-state can be copied.  */
-	ctxt->on_cast (sid, result);
-      }
-  return result;
-}
-#endif
-
-/* Ensure that the region2 for OBJ_REG has a child region2 for FIELD;
-   return the child region2's region2_id.  */
-#if 0
-region2_id
-region_model2::get_field_region (region2 *struct_or_union_reg, tree field,
-				 region_model2_context *ctxt)
-{
-  struct_or_union_region2 *sou_reg
-    = get_region2<struct_or_union_region2> (struct_or_union_reg);
-
-  /* Inherit constness from parent type.  */
-  const int qual_mask = TYPE_QUAL_CONST;
-  int sou_quals = TYPE_QUALS (sou_reg->get_type ()) & qual_mask;
-  tree field_type = TREE_TYPE (field);
-  tree field_type_with_quals = build_qualified_type (field_type, sou_quals);
-
-  // TODO: maybe convert to a vfunc?
-  if (sou_reg->get_kind () == RK_UNION)
-    {
-      /* Union.
-	 Get a view of the union as a whole, with the type of the field.  */
-      region2 *view_reg
-	= get_or_create_view (struct_or_union_reg, field_type_with_quals, ctxt);
-      return view_reg;
-    }
-  else
-    {
-      /* Struct.  */
-      region2 *child_reg
-	= sou_reg->get_or_create (this, struct_or_union_reg, field,
-				  field_type_with_quals, ctxt);
-      return child_reg;
-    }
-}
-#endif
-
-/* Get a region2 for referencing PTR_SVAL, creating a region2 if need be, and
-   potentially generating warnings via CTXT.
-   PTR_TREE if non-NULL can be used when emitting diagnostics.  */
-
-const region2 *
-region_model2::deref_rvalue (const svalue2 *ptr_sval, tree ptr_tree,
-			     region_model2_context *ctxt)
-{
-  gcc_assert (ptr_sval);
-
-  switch (ptr_sval->get_kind ())
-    {
-    default:
-      gcc_unreachable ();
-
-    case svalue2::SK_REGION:
-      {
-	const region_svalue2 *region2_sval
-	  = as_a <const region_svalue2 *> (ptr_sval);
-	return region2_sval->get_pointee ();
-      }
-
-    case svalue2::SK_BINOP:
-#if 1
-      {
-	const binop_svalue2 *binop_sval
-	  = as_a <const binop_svalue2 *> (ptr_sval);
-	switch (binop_sval->get_op ())
-	  {
-	  case POINTER_PLUS_EXPR:
-	    {
-	      /* If we have a symbolic value expressing pointer arithmentic,
-		 try to convert it to a suitable region.  */
-	      /* FIXME: but should it be an element_region2?  that expresses
-		 things via element indices, rather than via byte offsets.  */
-	      /* Perhaps instead we should capture this as an offset relative
-		 to a base region in region_offset?  */
-	      const region2 *parent_region
-		= deref_rvalue (binop_sval->get_arg0 (), NULL_TREE, ctxt);
-	      const svalue2 *offset = binop_sval->get_arg1 ();
-	      tree type= TREE_TYPE (ptr_sval->get_type ());
-	      return m_mgr->get_offset_region (parent_region, type, offset);
-	    }
-	  default:
-	    goto create_symbolic_region2;
-	  }
-      }
-#endif
-    case svalue2::SK_CONSTANT:
-    case svalue2::SK_INITIAL:
-    case svalue2::SK_UNARYOP:
-    case svalue2::SK_SUB:
-    case svalue2::SK_WIDENING:
-    case svalue2::SK_CONJURED:
-      goto create_symbolic_region2;
-
-    case svalue2::SK_POISONED:
-      {
-	if (ctxt)
-	  {
-	    tree ptr = get_representative_tree (ptr_sval);
-	    /* If we can't get a representative tree for PTR_SVAL
-	       (e.g. if it hasn't been bound into the store), then
-	       fall back on PTR_TREE, if non-NULL.  */
-	    if (!ptr)
-	      ptr = ptr_tree;
-	    if (ptr)
-	      {
-		const poisoned_svalue2 *poisoned_sval
-		  = as_a <const poisoned_svalue2 *> (ptr_sval);
-		enum poison_kind pkind = poisoned_sval->get_poison_kind ();
-		ctxt->warn (new poisoned_value_diagnostic2 (ptr, pkind));
-	      }
-	  }
-	goto create_symbolic_region2;
-      }
-
-    case svalue2::SK_UNKNOWN:
-      {
-      create_symbolic_region2:
-	/* We need a symbolic_region2 to represent this unknown region2.
-	   FIXME:
-	   We don't know if it on the heap, stack, or a global,
-	   so use the root region2 as parent.  */
-	const region2 *new_reg = m_mgr->get_symbolic_region (ptr_sval);
-#if 0
-	/* We need to write the region2 back into the pointer,
-	   or we'll get a new, different region2 each time.
-	   We do this by changing the meaning of ptr_sval, replacing
-	   the unknown value with the ptr to the new region2.
-	   We replace the meaning of the ID rather than simply writing
-	   to PTR's lvalue since there could be several places sharing
-	   the same unknown ptr value.  */
-	const svalue2 *ptr_val
-	  = new region_svalue2 (ptr_sval->get_type (), new_reg);
-	replace_svalue2 (ptr_sval, ptr_val);
-#endif
-	return new_reg;
-      }
-
-    case svalue2::SK_SETJMP:
-      goto create_symbolic_region2;
-    }
-
-  gcc_unreachable ();
-}
-
-#if 0
-
-/* Get a region2 *for referencing PTR, creating a region2 if need be, and
-   potentially generating warnings via CTXT.  */
-
-region2_id
-region_model2::deref_rvalue (tree ptr, region_model2_context *ctxt)
-{
-  const svalue2 *ptr_sval = get_rvalue (ptr, ctxt);
-  return deref_rvalue (ptr_sval, ctxt);
-}
-#endif
-
-/* Set the value of the region2 given by LHS_REG to the value given
-   by RHS_SVAL.  */
-
-void
-region_model2::set_value (const region2 *lhs_reg, const svalue2 *rhs_sval,
-			  region_model2_context */*ctxt*/)
-{
-  gcc_assert (lhs_reg);
-  gcc_assert (rhs_sval);
-
-  m_store.set_value (m_mgr->get_store2_manager(), lhs_reg, rhs_sval,
-		     BK_direct);
-}
-
-/* Set the value of the region2 given by LHS to the value given
-   by RHS.  */
-
-void
-region_model2::set_value (tree lhs, tree rhs, region_model2_context *ctxt)
-{
-  const region2 *lhs_reg = get_lvalue (lhs, ctxt);
-  const svalue2 *rhs_sval = get_rvalue (rhs, ctxt);
-  gcc_assert (lhs_reg);
-  gcc_assert (rhs_sval);
-  set_value (lhs_reg, rhs_sval, ctxt);
-}
-
-/* FIXME.  */
-
-void
-region_model2::clobber_region (const region2 *reg)
-{
-  m_store.clobber_region (m_mgr->get_store2_manager(), reg);
-}
-
-/* FIXME.  */
-
-void
-region_model2::purge_region (const region2 *reg)
-{
-  m_store.purge_region (m_mgr->get_store2_manager(), reg);
-}
-
-/* Zero-fill REG.  */
-
-void
-region_model2::zero_fill_region (const region2 *reg)
-{
-  m_store.zero_fill_region (m_mgr->get_store2_manager(), reg);
-}
-
-/* Mark REG as having unknown content.  */
-
-void
-region_model2::mark_region_as_unknown (const region2 *reg)
-{
-  m_store.mark_region_as_unknown (m_mgr->get_store2_manager(), reg);
-}
-
-/* Determine what is known about the condition "LHS_SVAL OP RHS_SVAL" within
-   this model.  */
-
-tristate
-region_model2::eval_condition (const svalue2 *lhs,
-			       enum tree_code op,
-			       const svalue2 *rhs) const
-{
-  /* For now, make no attempt to capture constraints on floating-point
-     values.  */
-  if ((lhs->get_type () && FLOAT_TYPE_P (lhs->get_type ()))
-      || (rhs->get_type () && FLOAT_TYPE_P (rhs->get_type ())))
-    return tristate::unknown ();
-
-  tristate ts = eval_condition_without_cm (lhs, op, rhs);
-  if (ts.is_known ())
-    return ts;
-
-  /* Otherwise, try constraints.  */
-  return m_constraints2->eval_condition (lhs, op, rhs);
-}
-
-/* Determine what is known about the condition "LHS_SVAL OP RHS_SVAL" within
-   this model, without resorting to the constraint_manager.
-
-   This is exposed so that impl_region_model2_context::on_state_leak can
-   check for equality part-way through region_model2::purge_unused_svalue2s
-   without risking creating new ECs.  */
-
-tristate
-region_model2::eval_condition_without_cm (const svalue2 *lhs,
-					  enum tree_code op,
-					  const svalue2 *rhs) const
-{
-  gcc_assert (lhs);
-  gcc_assert (rhs);
-
-  /* See what we know based on the values.  */
-
-  /* For now, make no attempt to capture constraints on floating-point
-     values.  */
-  if ((lhs->get_type () && FLOAT_TYPE_P (lhs->get_type ()))
-      || (rhs->get_type () && FLOAT_TYPE_P (rhs->get_type ())))
-    return tristate::unknown ();
-
-  /* Unwrap any unmergeable values.  */
-  lhs = lhs->unwrap_any_unmergeable ();
-  rhs = rhs->unwrap_any_unmergeable ();
-
-  if (lhs == rhs)
-    {
-      /* If we have the same svalue2, then we have equality
-	 (apart from NaN-handling).
-	 TODO: should this definitely be the case for poisoned values?  */
-      /* Poisoned and unknown values are "unknowable".  */
-      if (lhs->get_kind () == svalue2::SK_POISONED
-	  || lhs->get_kind () == svalue2::SK_UNKNOWN)
-	return tristate::TS_UNKNOWN;
-
-      switch (op)
-	{
-	case EQ_EXPR:
-	case GE_EXPR:
-	case LE_EXPR:
-	  return tristate::TS_TRUE;
-
-	case NE_EXPR:
-	case GT_EXPR:
-	case LT_EXPR:
-	  return tristate::TS_FALSE;
-
-	default:
-	  /* For other ops, use the logic below.  */
-	  break;
-	}
-    }
-
-  /* If we have a pair of region_svalue2s, compare them.  */
-  if (const region_svalue2 *lhs_ptr = lhs->dyn_cast_region_svalue2 ())
-    if (const region_svalue2 *rhs_ptr = rhs->dyn_cast_region_svalue2 ())
-      {
-	tristate res = region_svalue2::eval_condition (lhs_ptr, op, rhs_ptr);
-	if (res.is_known ())
-	  return res;
-	/* Otherwise, only known through constraints.  */
-      }
-
-  /* If we have a pair of constants, compare them.  */
-  if (const constant_svalue2 *cst_lhs = lhs->dyn_cast_constant_svalue2 ())
-    if (const constant_svalue2 *cst_rhs = rhs->dyn_cast_constant_svalue2 ())
-      return constant_svalue2::eval_condition (cst_lhs, op, cst_rhs);
-
-  /* Handle comparison of a region_svalue2 against zero.  */
-
-  if (const region_svalue2 *ptr = lhs->dyn_cast_region_svalue2 ())
-    if (const constant_svalue2 *cst_rhs = rhs->dyn_cast_constant_svalue2 ())
-      if (zerop (cst_rhs->get_constant ()))
-	{
-	  /* A region_svalue2 is a non-NULL pointer, except in certain
-	     special cases (see the comment for region2::non_null_p.  */
-	  const region2 *pointee = ptr->get_pointee ();
-	  if (pointee->non_null_p (*this))
-	    {
-	      switch (op)
-		{
-		default:
-		  gcc_unreachable ();
-
-		case EQ_EXPR:
-		case GE_EXPR:
-		case LE_EXPR:
-		  return tristate::TS_FALSE;
-
-		case NE_EXPR:
-		case GT_EXPR:
-		case LT_EXPR:
-		  return tristate::TS_TRUE;
-		}
-	    }
-	}
-
-  /* Handle rejection of equality for comparisons of the initial values of
-     "external" values (such as params) with the address of locals.  */
-  if (const initial_svalue2 *init_lhs = lhs->dyn_cast_initial_svalue2 ())
-    if (const region_svalue2 *rhs_ptr = rhs->dyn_cast_region_svalue2 ())
-      {
-	tristate res = compare_initial_and_pointer (init_lhs, rhs_ptr);
-	if (res.is_known ())
-	  return res;
-      }
-  if (const initial_svalue2 *init_rhs = rhs->dyn_cast_initial_svalue2 ())
-    if (const region_svalue2 *lhs_ptr = lhs->dyn_cast_region_svalue2 ())
-      {
-	tristate res = compare_initial_and_pointer (init_rhs, lhs_ptr);
-	if (res.is_known ())
-	  return res;
-      }
-
-  if (const widening_svalue2 *widen_lhs = lhs->dyn_cast_widening_svalue2 ())
-    if (tree rhs_cst = rhs->maybe_get_constant ())
-      {
-	tristate res = widen_lhs->eval_condition_without_cm (op, rhs_cst);
-	if (res.is_known ())
-	  return res;
-      }
-#if 0
-  if (const widening_svalue2 *widen_rhs = rhs->dyn_cast_widening_svalue2 ())
-    {
-      gcc_unreachable ();
-    }
-#endif
-
-  return tristate::TS_UNKNOWN;
-}
-
-/* Subroutine of region_model2::eval_condition_without_cm, for rejecting
-   equality of INIT_VAL(PARM) with &LOCAL.  */
-
-tristate
-region_model2::compare_initial_and_pointer (const initial_svalue2 *init,
-					    const region_svalue2 *ptr) const
-{
-  const region2 *pointee = ptr->get_pointee ();
-
-  /* If we have a pointer to something within a stack frame, it can't be the
-     initial value of a param.  */
-  if (pointee->maybe_get_frame_region ())
-    {
-      const region2 *reg = init->get_region ();
-      if (tree reg_decl = reg->maybe_get_decl ())
-	if (TREE_CODE (reg_decl) == SSA_NAME)
-	  {
-	    tree ssa_name = reg_decl;
-	    if (SSA_NAME_IS_DEFAULT_DEF (ssa_name)
-		&& SSA_NAME_VAR (ssa_name)
-		&& TREE_CODE (SSA_NAME_VAR (ssa_name)) == PARM_DECL)
-	      return tristate::TS_FALSE;
-	  }
-    }
-
-  return tristate::TS_UNKNOWN;
-}
-
-/* Attempt to add the constraint "LHS OP RHS" to this region_model2.
-   If it is consistent with existing constraints, add it, and return true.
-   Return false if it contradicts existing constraints.
-   Use CTXT for reporting any diagnostics associated with the accesses.  */
-
-bool
-region_model2::add_constraint (tree lhs, enum tree_code op, tree rhs,
-			       region_model2_context *ctxt)
-{
-  /* For now, make no attempt to capture constraints on floating-point
-     values.  */
-  if (FLOAT_TYPE_P (TREE_TYPE (lhs)) || FLOAT_TYPE_P (TREE_TYPE (rhs)))
-    return true;
-
-  const svalue2 *lhs_sval = get_rvalue (lhs, ctxt);
-  const svalue2 *rhs_sval = get_rvalue (rhs, ctxt);
-
-  tristate t_cond = eval_condition (lhs_sval, op, rhs_sval);
-
-  /* If we already have the condition, do nothing.  */
-  if (t_cond.is_true ())
-    return true;
-
-  /* Reject a constraint that would contradict existing knowledge, as
-     unsatisfiable.  */
-  if (t_cond.is_false ())
-    return false;
-
-  /* Store the constraint.  */
-  m_constraints2->add_constraint (lhs_sval, op, rhs_sval);
-
-  add_any_constraints_from_ssa_def_stmt (lhs, op, rhs, ctxt);
-
-#if 0
-  /* If we now know a symbolic_region2 is non-NULL, clear its
-     m_possibly_null.  */
-  if (zerop (rhs) && op == NE_EXPR)
-    if (region_svalue2 *ptr = get_svalue2 (lhs_sval)->dyn_cast_region_svalue2 ())
-      {
-	region2 *pointee = get_region2 (ptr->get_pointee ());
-	if (symbolic_region2 *sym_reg = pointee->dyn_cast_symbolic_region2 ())
-	  sym_reg->m_possibly_null = false;
-      }
-#endif
-
-  /* Notify the context, if any.  This exists so that the state machines
-     in a program_state can be notified about the condition, and so can
-     set sm-state for e.g. unchecked->checked, both for cfg-edges, and
-     when synthesizing constraints as above.  */
-  if (ctxt)
-    ctxt->on_condition (lhs, op, rhs);
-
-  return true;
-}
-
-/* Subroutine of region_model2::add_constraint for handling optimized
-   && and || conditionals.
-
-   If we have an SSA_NAME for a boolean compared against 0,
-   look at anything implied by the def stmt and call add_constraint
-   for it (which could recurse).
-
-   For example, if we have
-      _1 = p_6 == 0B;
-      _2 = p_8 == 0B
-      _3 = _1 | _2
-    and add the constraint
-      (_3 == 0),
-    then the def stmt for _3 implies that _1 and _2 are both false,
-    and hence we can add the constraints:
-      p_6 != 0B
-      p_8 != 0B.  */
-
-void
-region_model2::add_any_constraints_from_ssa_def_stmt (tree lhs,
-						     enum tree_code op,
-						     tree rhs,
-						     region_model2_context *ctxt)
-{
-  if (TREE_CODE (lhs) != SSA_NAME)
-    return;
-
-  if (!zerop (rhs))
-    return;
-
-  if (op != NE_EXPR && op != EQ_EXPR)
-    return;
-
-  gimple *def_stmt = SSA_NAME_DEF_STMT (lhs);
-  if (const gassign *assign = dyn_cast<gassign *> (def_stmt))
-    add_any_constraints_from_gassign (op, rhs, assign, ctxt);
-  else if (gcall *call = dyn_cast<gcall *> (def_stmt))
-    add_any_constraints_from_gcall (op, rhs, call, ctxt);
-}
-
-/* Add any constraints for an SSA_NAME defined by ASSIGN
-   where the result OP RHS.  */
-
-void
-region_model2::add_any_constraints_from_gassign (enum tree_code op,
-						tree rhs,
-						const gassign *assign,
-						region_model2_context *ctxt)
-{
-  /* We have either
-     - "LHS != false" (i.e. LHS is true), or
-     - "LHS == false" (i.e. LHS is false).  */
-  bool is_true = op == NE_EXPR;
-
-  enum tree_code rhs_code = gimple_assign_rhs_code (assign);
-
-  switch (rhs_code)
-    {
-    default:
-      break;
-
-    case NOP_EXPR:
-      {
-	add_constraint (gimple_assign_rhs1 (assign), op, rhs, ctxt);
-      }
-      break;
-
-    case BIT_AND_EXPR:
-      {
-	if (is_true)
-	  {
-	    /* ...and "LHS == (rhs1 & rhs2) i.e. "(rhs1 & rhs2)" is true
-	       then both rhs1 and rhs2 must be true.  */
-	    tree rhs1 = gimple_assign_rhs1 (assign);
-	    tree rhs2 = gimple_assign_rhs2 (assign);
-	    add_constraint (rhs1, NE_EXPR, boolean_false_node, ctxt);
-	    add_constraint (rhs2, NE_EXPR, boolean_false_node, ctxt);
-	  }
-      }
-      break;
-
-    case BIT_IOR_EXPR:
-      {
-	if (!is_true)
-	  {
-	    /* ...and "LHS == (rhs1 | rhs2)
-	       i.e. "(rhs1 | rhs2)" is false
-	       then both rhs1 and rhs2 must be false.  */
-	    tree rhs1 = gimple_assign_rhs1 (assign);
-	    tree rhs2 = gimple_assign_rhs2 (assign);
-	    add_constraint (rhs1, EQ_EXPR, boolean_false_node, ctxt);
-	    add_constraint (rhs2, EQ_EXPR, boolean_false_node, ctxt);
-	  }
-      }
-      break;
-
-    case EQ_EXPR:
-    case NE_EXPR:
-      {
-	/* ...and "LHS == (rhs1 OP rhs2)"
-	   then rhs1 OP rhs2 must have the same logical value as LHS.  */
-	tree rhs1 = gimple_assign_rhs1 (assign);
-	tree rhs2 = gimple_assign_rhs2 (assign);
-	if (!is_true)
-	  rhs_code
-	    = invert_tree_comparison (rhs_code, false /* honor_nans */);
-	add_constraint (rhs1, rhs_code, rhs2, ctxt);
-      }
-      break;
-    }
-}
-
-/* Add any constraints for an SSA_NAME defined by CALL
-   where the result OP RHS.  */
-
-void
-region_model2::add_any_constraints_from_gcall (enum tree_code op,
-					       tree rhs,
-					       const gcall *call,
-					       region_model2_context *ctxt)
-{
-  if (gimple_call_builtin_p (call, BUILT_IN_EXPECT)
-      || gimple_call_builtin_p (call, BUILT_IN_EXPECT_WITH_PROBABILITY)
-      || gimple_call_internal_p (call, IFN_BUILTIN_EXPECT))
-    {
-      /* __builtin_expect's return value is its initial argument.  */
-      add_constraint (gimple_call_arg (call, 0), op, rhs, ctxt);
-    }
-}
-
-/* Determine what is known about the condition "LHS OP RHS" within
-   this model.
-   Use CTXT for reporting any diagnostics associated with the accesses.  */
-
-tristate
-region_model2::eval_condition (tree lhs,
-			       enum tree_code op,
-			       tree rhs,
-			       region_model2_context *ctxt)
-{
-  /* For now, make no attempt to model constraints on floating-point
-     values.  */
-  if (FLOAT_TYPE_P (TREE_TYPE (lhs)) || FLOAT_TYPE_P (TREE_TYPE (rhs)))
-    return tristate::unknown ();
-
-  return eval_condition (get_rvalue (lhs, ctxt),
-			 op,
-			 get_rvalue (rhs, ctxt));
-}
-
-/* If SID is a constant value, return the underlying tree constant.
-   Otherwise, return NULL_TREE.  */
-#if 0
-tree
-region_model2::maybe_get_constant (const svalue2 *sval) const
-{
-  gcc_assert (!sid.null_p ());
-  svalue2 *sval = get_svalue2 (sid);
-  return sval->maybe_get_constant ();
-}
-#endif
-
-/* Create a new child region2 of the heap (creating the heap region2 if
-   necessary).
-   Return the region2 *of the new child region2.  */
-#if 0
-region2_id
-region_model2::add_new_malloc_region2 ()
-{
-  region2 *heap_reg
-    = get_root_region2 ()->ensure_heap_region2 (this);
-  return add_region2 (new symbolic_region2 (heap_reg, NULL_TREE, true));
-}
-#endif
-
-/* Attempt to return a path_var that represents SVAL, or return NULL_TREE.
-   Use VISITED to prevent infinite mutual recursion with the overload for
-   regions.  */
-
-path_var
-region_model2::get_representative_path_var (const svalue2 *sval,
-					    hash_set <const svalue2 *> *visited) const
-{
-  if (sval == NULL)
-    return path_var (NULL_TREE, 0);
-
-  if (const svalue2 *cast_sval = sval->maybe_undo_cast ())
-    sval = cast_sval;
-
-  /* Prevent infinite recursion.  */
-  if (visited->contains (sval))
-    return path_var (NULL_TREE, 0);
-  visited->add (sval);
-
-  auto_vec<path_var> pvs;
-  m_store.get_representative_path_vars (this, visited, sval, &pvs);
-
-  if (tree cst = sval->maybe_get_constant ())
-    pvs.safe_push (path_var (cst, 0));
-
-  /* Handle string literals and various other pointers.  */
-  if (const region_svalue2 *ptr_sval = sval->dyn_cast_region_svalue2 ())
-    {
-      const region2 *reg = ptr_sval->get_pointee ();
-      if (path_var pv = get_representative_path_var (reg, visited))
-	return path_var (build1 (ADDR_EXPR,
-				 TREE_TYPE (sval->get_type ()),
-				 pv.m_tree),
-			 pv.m_stack_depth);
-    }
-
-#if CONJURED_VALUES_FOR_UNKNOWN_FNCALLS
-  /* If we have a sub_svalue, look for ways to represent the parent.  */
-  if (const sub_svalue2 *sub_sval = sval->dyn_cast_sub_svalue2 ())
-    {
-      const svalue2 *parent_sval = sub_sval->get_parent ();
-      const region2 *subreg = sub_sval->get_subregion ();
-      if (path_var parent_pv
-	    = get_representative_path_var (parent_sval, visited))
-	if (const field_region2 *field_reg = subreg->dyn_cast_field_region2 ())
-	  return path_var (build3 (COMPONENT_REF,
-				   sval->get_type (),
-				   parent_pv.m_tree,
-				   field_reg->get_field (),
-				   NULL_TREE),
-			   parent_pv.m_stack_depth);
-    }
-#endif
-
-  if (pvs.length () < 1)
-    return path_var (NULL_TREE, 0);
-
-  pvs.qsort (readability_comparator);
-  return pvs[0];
-
-#if 0
-  /* Find the first region2 that stores the value (e.g. a local) and
-     generate a representative tree for it.  */
-  unsigned i;
-  region2 *region2;
-  FOR_EACH_VEC_ELT (m_region2s, i, region2)
-    if (sid == region2->get_value_direct ())
-      {
-	path_var pv = get_representative_path_var (region2_id::from_int (i));
-	if (pv.m_tree)
-	  return pv.m_tree;
-      }
-
-  return maybe_get_constant (sid);
-#endif
-}
-
-/* Attempt to return a tree that represents SVAL, or return NULL_TREE.  */
-
-tree
-region_model2::get_representative_tree (const svalue2 *sval) const
-{
-  hash_set <const svalue2 *> visited;
-  return get_representative_path_var (sval, &visited).m_tree;
-}
-
-/* Attempt to return a path_var that represents REG, or return
-   the NULL path_var.
-   For example, a region2 for a field of a local would be a path_var
-   wrapping a COMPONENT_REF.
-   Use VISITED to prevent infinite mutual recursion with the overload for
-   svalue2s.  */
-
-path_var
-region_model2::get_representative_path_var (const region2 *reg,
-					    hash_set <const svalue2 *> *visited) const
-{
-  switch (reg->get_kind ())
-    {
-    default:
-      gcc_unreachable ();
-
-    case region2::RK_FRAME:
-    case region2::RK_GLOBALS:
-    case region2::RK_CODE:
-    case region2::RK_HEAP:
-    case region2::RK_STACK:
-    case region2::RK_ROOT:
-       /* Regions that represent memory spaces are not expressible as trees.  */
-      return path_var (NULL_TREE, 0);
-
-    case region2::RK_FUNCTION:
-      {
-	const function_region2 *function_reg
-	  = as_a <const function_region2 *> (reg);
-	return path_var (function_reg->get_fndecl (), 0);
-      }
-    case region2::RK_LABEL:
-      gcc_unreachable (); // TODO
-
-    case region2::RK_SYMBOLIC:
-      {
-	const symbolic_region2 *symbolic_reg
-	  = as_a <const symbolic_region2 *> (reg);
-	const svalue2 *pointer = symbolic_reg->get_pointer ();
-	path_var pointer_pv = get_representative_path_var (pointer, visited);
-	if (!pointer_pv)
-	  return path_var (NULL_TREE, 0);
-	tree offset = build_int_cst (pointer->get_type (), 0);
-	return path_var (build2 (MEM_REF,
-				 reg->get_type (),
-				 pointer_pv.m_tree,
-				 offset),
-			 pointer_pv.m_stack_depth);
-      }
-    case region2::RK_DECL:
-      {
-	const decl_region2 *decl_reg = as_a <const decl_region2 *> (reg);
-	return path_var (decl_reg->get_decl (), decl_reg->get_stack_depth ());
-      }
-    case region2::RK_FIELD:
-      {
-	const field_region2 *field_reg = as_a <const field_region2 *> (reg);
-	path_var parent_pv
-	  = get_representative_path_var (reg->get_parent_region (), visited);
-	if (!parent_pv)
-	  return path_var (NULL_TREE, 0);
-	return path_var (build3 (COMPONENT_REF,
-				 reg->get_type (),
-				 parent_pv.m_tree,
-				 field_reg->get_field (),
-				 NULL_TREE),
-			 parent_pv.m_stack_depth);
-      }
-
-    case region2::RK_ELEMENT:
-      {
-	const element_region2 *element_reg
-	  = as_a <const element_region2 *> (reg);
-	path_var parent_pv
-	  = get_representative_path_var (reg->get_parent_region (), visited);
-	if (!parent_pv)
-	  return path_var (NULL_TREE, 0);
-	path_var index_pv
-	  = get_representative_path_var (element_reg->get_index (), visited);
-	if (!index_pv)
-	  return path_var (NULL_TREE, 0);
-	return path_var (build4 (ARRAY_REF,
-				 reg->get_type (),
-				 parent_pv.m_tree, index_pv.m_tree,
-				 NULL_TREE, NULL_TREE),
-			 parent_pv.m_stack_depth);
-      }
-
-    case region2::RK_OFFSET:
-      {
-	const offset_region2 *offset_reg
-	  = as_a <const offset_region2 *> (reg);
-	path_var parent_pv
-	  = get_representative_path_var (reg->get_parent_region (), visited);
-	if (!parent_pv)
-	  return path_var (NULL_TREE, 0);
-	path_var offset_pv
-	  = get_representative_path_var (offset_reg->get_byte_offset (),
-					 visited);
-	if (!offset_pv)
-	  return path_var (NULL_TREE, 0);
-	return path_var (build2 (MEM_REF,
-				 reg->get_type (),
-				 parent_pv.m_tree, offset_pv.m_tree),
-			 parent_pv.m_stack_depth);
-      }
-
-    case region2::RK_CAST:
-      {
-	path_var parent_pv
-	  = get_representative_path_var (reg->get_parent_region (), visited);
-	if (!parent_pv)
-	  return path_var (NULL_TREE, 0);
-	return path_var (build1 (NOP_EXPR,
-				 reg->get_type (),
-				 parent_pv.m_tree),
-			 parent_pv.m_stack_depth);
-      }
-
-    case region2::RK_HEAP_ALLOCATED:
-    case region2::RK_ALLOCA:
-      /* No good way to express heap-allocated/alloca regions as trees.  */
-      return path_var (NULL_TREE, 0);
-
-    case region2::RK_STRING:
-      {
-	const string_region2 *string_reg = as_a <const string_region2 *> (reg);
-	return path_var (string_reg->get_string_cst (), 0);
-      }
-
-    case region2::RK_UNKNOWN:
-      return path_var (NULL_TREE, 0);
-    }
-}
-
-/* Locate all region2s that directly have value SVAL and append representative
-   path_var instances for them into *OUT.  */
-/* FIXME: other ways of getting to the svalue e.g. for initial_svalue2s?
-   Also: this might not be the best human-readable description to use.  */
-#if 0
-void
-region_model2::get_path_vars_for_svalue2 (const svalue2 *sval,
-					  int i,
-					  auto_vec<path_var> *out) const
-{
-  return m_store.get_representative_path_vars (this, sval, out);
-
-#if 0
-  unsigned i;
-  region2 *region2;
-  FOR_EACH_VEC_ELT (m_region2s, i, region2)
-    if (sval == region2->get_value_direct ())
-      {
-	path_var pv = get_representative_path_var (sval);
-	if (pv.m_tree)
-	  out->safe_push (pv);
-      }
-#endif
-}
-#endif
-
-/* Set DST_REG value to be a new unknown value of type TYPE.  */
-#if 0
-svalue2_id
-region_model2::set_to_new_unknown_value (region2 *dst_reg, tree type,
-					region_model2_context *ctxt)
-{
-  gcc_assert (!dst_reg.null_p ());
-  const svalue2 *new_sval = add_svalue2 (new unknown_svalue2 (type));
-  set_value (dst_reg, new_sval, ctxt);
-
-  // TODO: presumably purge all child region2s too (but do this in set_value?)
-
-  return new_sval;
-}
-#endif
-
-/* Update this model for any phis in SNODE, assuming we came from
-   LAST_CFG_SUPEREDGE.  */
-
-void
-region_model2::update_for_phis (const supernode *snode,
-			       const cfg_superedge *last_cfg_superedge,
-			       region_model2_context *ctxt)
-{
-  gcc_assert (last_cfg_superedge);
-
-  for (gphi_iterator gpi = const_cast<supernode *>(snode)->start_phis ();
-       !gsi_end_p (gpi); gsi_next (&gpi))
-    {
-      gphi *phi = gpi.phi ();
-
-      tree src = last_cfg_superedge->get_phi_arg (phi);
-      tree lhs = gimple_phi_result (phi);
-
-      /* Update next_state based on phi.  */
-      bool is_back_edge = last_cfg_superedge->back_edge_p ();
-      handle_phi (phi, lhs, src, is_back_edge, ctxt);
-    }
-}
-
-/* Attempt to update this model for taking EDGE (where the last statement
-   was LAST_STMT), returning true if the edge can be taken, false
-   otherwise.
-
-   For CFG superedges where LAST_STMT is a conditional or a switch
-   statement, attempt to add the relevant conditions for EDGE to this
-   model, returning true if they are feasible, or false if they are
-   impossible.
-
-   For call superedges, push frame information and store arguments
-   into parameters.
-
-   For return superedges, pop frame information and store return
-   values into any lhs.
-
-   Rejection of call/return superedges happens elsewhere, in
-   program_point::on_edge (i.e. based on program point, rather
-   than program state).  */
-
-bool
-region_model2::maybe_update_for_edge (const superedge &edge,
-				      const gimple *last_stmt,
-				      region_model2_context *ctxt)
-{
-  /* Handle frame updates for interprocedural edges.  */
-  switch (edge.m_kind)
-    {
-    default:
-      break;
-
-    case SUPEREDGE_CALL:
-      {
-	const call_superedge *call_edge = as_a <const call_superedge *> (&edge);
-	update_for_call_superedge (*call_edge, ctxt);
-      }
-      break;
-
-    case SUPEREDGE_RETURN:
-      {
-	const return_superedge *return_edge
-	  = as_a <const return_superedge *> (&edge);
-	update_for_return_superedge (*return_edge, ctxt);
-      }
-      break;
-
-    case SUPEREDGE_INTRAPROCEDURAL_CALL:
-      {
-	const callgraph_superedge *cg_sedge
-	  = as_a <const callgraph_superedge *> (&edge);
-	update_for_call_summary (*cg_sedge, ctxt);
-      }
-      break;
-    }
-
-  if (last_stmt == NULL)
-    return true;
-
-  /* Apply any constraints for conditionals/switch statements.  */
-
-  if (const gcond *cond_stmt = dyn_cast <const gcond *> (last_stmt))
-    {
-      const cfg_superedge *cfg_sedge = as_a <const cfg_superedge *> (&edge);
-      return apply_constraints_for_gcond (*cfg_sedge, cond_stmt, ctxt);
-    }
-
-  if (const gswitch *switch_stmt = dyn_cast <const gswitch *> (last_stmt))
-    {
-      const switch_cfg_superedge *switch_sedge
-	= as_a <const switch_cfg_superedge *> (&edge);
-      return apply_constraints_for_gswitch (*switch_sedge, switch_stmt,
-					    ctxt);
-    }
-
-  return true;
-}
-
-/* Push a new frame_region2 on to the stack region2.
-   Populate the frame_region2 with child region2s for the function call's
-   parameters, using values from the arguments at the callsite in the
-   caller's frame.  */
-
-void
-region_model2::update_for_call_superedge (const call_superedge &call_edge,
-					  region_model2_context *ctxt)
-{
-  /* Build a vec of argument svalue2_id, using the current top
-     frame for resolving tree expressions.  */
-  const gcall *call_stmt = call_edge.get_call_stmt ();
-  auto_vec<const svalue2 *> arg_svals (gimple_call_num_args (call_stmt));
-
-  for (unsigned i = 0; i < gimple_call_num_args (call_stmt); i++)
-    {
-      tree arg = gimple_call_arg (call_stmt, i);
-      arg_svals.quick_push (get_rvalue (arg, ctxt));
-    }
-
-  push_frame (call_edge.get_callee_function (), &arg_svals, ctxt);
-}
-
-/* Pop the top-most frame_region2 from the stack, and copy the return
-   region2's values (if any) into the region2 for the lvalue of the LHS of
-   the call (if any).  */
-void
-region_model2::update_for_return_superedge (const return_superedge &return_edge,
-					    region_model2_context *ctxt)
-{
-  /* Get the region2 for the result of the call, within the caller frame.  */
-  const region2 *result_dst_reg = NULL;
-  const gcall *call_stmt = return_edge.get_call_stmt ();
-  tree lhs = gimple_call_lhs (call_stmt);
-  if (lhs)
-    {
-      /* Normally we access the top-level frame, which is:
-	   path_var (expr, get_stack_depth () - 1)
-	 whereas here we need the caller frame, hence "- 2" here.  */
-      gcc_assert (get_stack_depth () >= 2);
-      result_dst_reg = get_lvalue (path_var (lhs, get_stack_depth () - 2),
-				   ctxt);
-    }
-
-  purge_stats2 stats;
-  pop_frame (result_dst_reg, true, &stats, NULL, ctxt);
-
-  // TODO: do something with the stats?
-
-#if 0
-  if (!lhs)
-    {
-      /* This could be a leak; try purging again, but this time,
-	 don't special-case the result sids (as was done in pop_frame).  */
-      purge_unused_svalue2s (&stats, ctxt);
-    }
-#endif
-}
-/* Update this region_model2 with a summary of the effect of calling
-   and returning from CG_SEDGE.
-
-   TODO: Currently this is extremely simplistic: we merely set the
-   return value to "unknown".  A proper implementation would e.g. update
-   sm-state, and presumably be reworked to support multiple outcomes.  */
-void
-region_model2::update_for_call_summary (const callgraph_superedge &cg_sedge,
-					region_model2_context *ctxt)
-{
-  /* For now, set any return value to "unknown".  */
-  const gcall *call_stmt = cg_sedge.get_call_stmt ();
-  tree lhs = gimple_call_lhs (call_stmt);
-  if (lhs)
-    mark_region_as_unknown (get_lvalue (lhs, ctxt));
-
-  // TODO: actually implement some kind of summary here
-}
-
-/* Given a true or false edge guarded by conditional statement COND_STMT,
-   determine appropriate constraints for the edge to be taken.
-
-   If they are feasible, add the constraints and return true.
-
-   Return false if the constraints contradict existing knowledge
-   (and so the edge should not be taken).  */
-
-bool
-region_model2::apply_constraints_for_gcond (const cfg_superedge &sedge,
-					    const gcond *cond_stmt,
-					    region_model2_context *ctxt)
-{
-  ::edge cfg_edge = sedge.get_cfg_edge ();
-  gcc_assert (cfg_edge != NULL);
-  gcc_assert (cfg_edge->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE));
-
-  enum tree_code op = gimple_cond_code (cond_stmt);
-  tree lhs = gimple_cond_lhs (cond_stmt);
-  tree rhs = gimple_cond_rhs (cond_stmt);
-  if (cfg_edge->flags & EDGE_FALSE_VALUE)
-    op = invert_tree_comparison (op, false /* honor_nans */);
-  return add_constraint (lhs, op, rhs, ctxt);
-}
-
-/* Given an EDGE guarded by SWITCH_STMT, determine appropriate constraints
-   for the edge to be taken.
-
-   If they are feasible, add the constraints and return true.
-
-   Return false if the constraints contradict existing knowledge
-   (and so the edge should not be taken).  */
-
-bool
-region_model2::apply_constraints_for_gswitch (const switch_cfg_superedge &edge,
-					      const gswitch *switch_stmt,
-					      region_model2_context *ctxt)
-{
-  tree index  = gimple_switch_index (switch_stmt);
-  tree case_label = edge.get_case_label ();
-  gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR);
-  tree lower_bound = CASE_LOW (case_label);
-  tree upper_bound = CASE_HIGH (case_label);
-  if (lower_bound)
-    {
-      if (upper_bound)
-	{
-	  /* Range.  */
-	  if (!add_constraint (index, GE_EXPR, lower_bound, ctxt))
-	    return false;
-	  return add_constraint (index, LE_EXPR, upper_bound, ctxt);
-	}
-      else
-	/* Single-value.  */
-	return add_constraint (index, EQ_EXPR, lower_bound, ctxt);
-    }
-  else
-    {
-      /* The default case.
-	 Add exclusions based on the other cases.  */
-      for (unsigned other_idx = 1;
-	   other_idx < gimple_switch_num_labels (switch_stmt);
-	   other_idx++)
-	{
-	  tree other_label = gimple_switch_label (switch_stmt,
-						  other_idx);
-	  tree other_lower_bound = CASE_LOW (other_label);
-	  tree other_upper_bound = CASE_HIGH (other_label);
-	  gcc_assert (other_lower_bound);
-	  if (other_upper_bound)
-	    {
-	      /* Exclude this range-valued case.
-		 For now, we just exclude the boundary values.
-		 TODO: exclude the values within the region2.  */
-	      if (!add_constraint (index, NE_EXPR, other_lower_bound,
-				   ctxt))
-		return false;
-	      if (!add_constraint (index, NE_EXPR, other_upper_bound,
-				   ctxt))
-		return false;
-	    }
-	  else
-	    /* Exclude this single-valued case.  */
-	    if (!add_constraint (index, NE_EXPR, other_lower_bound, ctxt))
-	      return false;
-	}
-      return true;
-    }
-}
-
-/* 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"
-   the pointer, and thus calls to unknown functions could read/write into
-   the region.  */
-
-void
-region_model2::on_top_level_param (tree param,
-				   region_model2_context *ctxt)
-{
-  if (POINTER_TYPE_P (TREE_TYPE (param)))
-    {
-      const region2 *param_reg = get_lvalue (param, ctxt);
-      const svalue2 *init_ptr_sval
-	= m_mgr->get_or_create_initial_value (param_reg);
-      const region2 *pointee_reg = m_mgr->get_symbolic_region (init_ptr_sval);
-      m_store.mark_as_escaped (pointee_reg);
-    }
-}
-
-// FIXME:
-/* Create a new frame_region2 for a call to FUN and push it onto
-   the stack.
-
-   If ARG_SVALS is non-NULL, use it to populate the parameters
-   in the new frame.
-   Otherwise, populate them with unknown values.
-
-   Return the new frame_region2.  */
-
-const region2 *
-region_model2::push_frame (function *fun, vec<const svalue2 *> *arg_svals,
-			   region_model2_context *ctxt)
-{
-  m_current_frame = m_mgr->get_frame_region (m_current_frame, fun);
-  if (arg_svals)
-    {
-      /* Arguments supplied from a caller frame.  */
-      tree fndecl = fun->decl;
-      unsigned idx = 0;
-      for (tree iter_parm = DECL_ARGUMENTS (fndecl); iter_parm;
-	   iter_parm = DECL_CHAIN (iter_parm), ++idx)
-	{
-	  /* If there's a mismatching declaration, the call stmt might
-	     not have enough args.  Handle this case by leaving the
-	     rest of the params as uninitialized.  */
-	  if (idx >= arg_svals->length ())
-	    break;
-	  const svalue2 *arg_sval = (*arg_svals)[idx];
-	  const region2 *parm_reg = get_lvalue (iter_parm, ctxt);
-	  set_value (parm_reg, arg_sval, ctxt);
-
-	  /* Also do it for default SSA name (sharing the same value).  */
-	  tree parm_default_ssa = ssa_default_def (fun, iter_parm);
-	  if (parm_default_ssa)
-	    {
-	      const region2 *defssa_reg = get_lvalue (parm_default_ssa, ctxt);
-	      set_value (defssa_reg, arg_sval, ctxt);
-	    }
-	}
-    }
-  else
-    {
-      /* Otherwise we have a top-level call within the analysis.  The params
-	 have defined but unknown initial values.
-	 Anything they point to has escaped.  */
-      tree fndecl = fun->decl;
-      for (tree iter_parm = DECL_ARGUMENTS (fndecl); iter_parm;
-	   iter_parm = DECL_CHAIN (iter_parm))
-	{
-	  on_top_level_param (iter_parm, ctxt);
-	  /* TODO: also do it for default SSA name (have them share the
-	     same unknown initial value).  */
-	  tree parm_default_ssa = ssa_default_def (fun, iter_parm);
-	  if (parm_default_ssa)
-	    on_top_level_param (parm_default_ssa, ctxt);
-	}
-    }
-
-  return m_current_frame;
-}
-
-/* Get the function of the top-most frame in this region_model2's stack.
-   There must be such a frame.  */
-
-function *
-region_model2::get_current_function () const
-{
-  const frame_region2 *frame = get_current_frame ();
-  gcc_assert (frame);
-  return frame->get_function ();
-}
-
-/* Pop the topmost frame_region2 from this region_model2's stack;
-
-   FIXME:
-   If RESULT_DST_RID is non-null, copy any return value from the frame
-   into RESULT_DST_RID's region.
-
-   FIXME:
-   Purge the frame region and all its descendent regions.
-   Convert any pointers that point into such regions into
-   POISON_KIND_POPPED_STACK svalues.
-
-   FIXME:
-   If PURGE, then purge all unused svalues, with the exception of any
-   returned values.
-
-   FIXME:
-   Accumulate stats on purged entities into STATS.  */
-
-void
-region_model2::pop_frame (const region2 *result_dst_reg,
-			  bool purge, purge_stats2 *out,
-			  const svalue2 **out_result,
-			  region_model2_context *ctxt)
-{
-  gcc_assert (m_current_frame);
-
-  /* Evaluate the result, within the callee frame.  */
-  const frame_region2 *frame_reg = m_current_frame;
-#if 0
-  svalue2_id_set returned_svals;
-#endif
-  tree fndecl = m_current_frame->get_function ()->decl;
-  tree result = DECL_RESULT (fndecl);
-  if (result && TREE_TYPE (result) != void_type_node)
-    {
-      if (result_dst_reg)
-	{
-	  /* Copy the result to RESULT_DST_REG.  */
-	  copy_region (result_dst_reg,
-		       get_lvalue (result, ctxt),
-		       ctxt);
-	}
-      if (out_result)
-	*out_result = get_rvalue (result, ctxt);
-      if (purge)
-	{
-#if 0
-	  /* Populate returned_svals, to avoid purging them.  */
-	  region2_id return_reg = model->get_lvalue (result, NULL);
-	  region2_id_set returned_regs (model);
-	  model->get_descendents (return_reg, &returned_regs,
-				  region2_id::null ());
-	  for (unsigned i = 0; i < model->get_num_region2s (); i++)
-	    {
-	      region2_id rid = region2_id::from_int (i);
-	      if (returned_regs.region2_p (rid))
-		{
-		  const svalue2 *sval = model->get_region2 (rid)->get_value_direct ();
-		  returned_svals.add_svalue2 (sid);
-		}
-	    }
-#endif
-	}
-    }
-
-  /* Pop the frame.  */
-  m_current_frame = m_current_frame->get_calling_frame ();
-
-#if 1
-  unbind_region2_and_descendents (frame_reg,
-				  POISON_KIND_POPPED_STACK,
-				  out,
-				  ctxt ? ctxt->get_logger () : NULL);
-#endif
-
-  /* Delete unused svalue2s, but don't delete the return value(s).  */
-#if 0
-  if (purge)
-    purge_unused_svalue2s (out, ctxt, &returned_svals);
-#endif
-}
-
-
-/* Get the number of frames in this region_model2's stack.  */
-
-int
-region_model2::get_stack_depth () const
-{
-  const frame_region2 *frame = get_current_frame ();
-  if (frame)
-    return frame->get_stack_depth ();
-  else
-    return 0;
-}
-
-/* FIXME.  */
-
-const frame_region2 *
-region_model2::get_frame_at_index (int index) const
-{
-  const frame_region2 *frame = get_current_frame ();
-  if (frame)
-    {
-      gcc_assert (index >= 0);
-      gcc_assert (index <= frame->get_index ());
-      while (index != frame->get_index ())
-	{
-	  frame = frame->get_calling_frame ();
-	  gcc_assert (frame);
-	}
-      return frame;
-    }
-  else
-    return 0;
-}
-
-/* Get the function * at DEPTH within the call stack.  */
-#if 0
-function *
-region_model2::get_function_at_depth (unsigned depth) const
-{
-  stack_region2 *stack = get_root_region2 ()->get_stack_region2 (this);
-  gcc_assert (stack);
-  region2 *frame_reg = stack->get_frame_reg (depth);
-  frame_region2 *frame = get_region2 <frame_region2> (frame_reg);
-  return frame->get_function ();
-}
-#endif
-
-/* Change the meaning of SID to be NEW_SVAL
-   (e.g. when deferencing an unknown pointer, the pointer
-   becomes a pointer to a symbolic region2, so that all users
-   of the former unknown pointer are now effectively pointing
-   at the same region2).  */
-#if 0
-void
-region_model2::replace_svalue2 (const svalue2 *sval, const svalue2 *new_sval)
-{
-  gcc_assert (!sid.null_p ());
-  int idx = sid.as_int ();
-
-  gcc_assert (m_svalue2s[idx]);
-  gcc_assert (m_svalue2s[idx]->get_type () == new_sval->get_type ());
-  delete m_svalue2s[idx];
-
-  m_svalue2s[idx] = new_sval;
-}
-#endif
-
-/* Helper class for region_model2::purge_unused_svalue2s.  */
-
-#if 0
-class restrict_to_used_svalue2s : public purge_criteria
-{
-public:
-  restrict_to_used_svalue2s (const auto_sbitmap &used) : m_used (used) {}
-
-  bool should_purge_p (const svalue2 *sval) const FINAL OVERRIDE
-  {
-    gcc_assert (!sid.null_p ());
-    return !bitmap_bit_p (m_used, sid.as_int ());
-  }
-
-private:
-  const auto_sbitmap &m_used;
-};
-
-/* Remove unused svalue2s from this model, accumulating stats into STATS.
-   Unused svalue2s are deleted.  Doing so could reorder the svalue2s, and
-   thus change the meaning of svalue2_ids.
-
-   If CTXT is non-NULL, then it is notified about svalue2 *remappings,
-   and about svalue2_ids that are about to be deleted.  This allows e.g.
-   for warning about resource leaks, for the case where the svalue2
-   represents a resource handle in the user code (e.g. a FILE * or a malloc
-   buffer).
-
-   Amongst other things, removing unused svalue2s is important for ensuring
-   that the analysis of loops terminates.  Otherwise, we could generate a
-   succession of models with unreferenced "unknown" values, where the
-   number of redundant unknown values could grow without bounds, and each
-   such model would be treated as distinct.
-
-   If KNOWN_USED_SVALS is non-NULL, treat *KNOWN_USED_SVALS as used (this is for
-   handling values being returned from functions as their frame is popped,
-   since otherwise we'd have to simultaneously determine both the rvalue
-   of the return expr in the callee frame and the lvalue for the gcall's
-   assignment in the caller frame, and it seems cleaner to express all
-   lvalue and rvalue lookups implicitly relative to a "current" frame).
-   The svalue2_ids in *KNOWN_USED_SVALS are not remapped and hence this
-   call makes it invalid.  */
-
-void
-region_model2::purge_unused_svalue2s (purge_stats *stats,
-				    region_model2_context *ctxt,
-				    svalue2_id_set *known_used_svals)
-{
-  // TODO: might want to avoid a vfunc call just to do logging here:
-  logger *logger = ctxt ? ctxt->get_logger () : NULL;
-
-  LOG_SCOPE (logger);
-
-  auto_sbitmap used (m_svalue2s.length ());
-  bitmap_clear (used);
-
-  if (known_used_svals)
-    {
-      /* We can't use an sbitmap for known_used_svals as the number of
-	 svalue2s could have grown since it was created.  */
-      for (unsigned i = 0; i < get_num_svalue2s (); i++)
-	if (known_used_svals->svalue2_p (svalue2_id::from_int (i)))
-	  bitmap_set_bit (used, i);
-    }
-
-  /* Walk the region2s, marking sids that are used.  */
-  unsigned i;
-  region2 *r;
-  FOR_EACH_VEC_ELT (m_region2s, i, r)
-    {
-      const svalue2 *sval = r->get_value_direct ();
-      if (!sid.null_p ())
-	bitmap_set_bit (used, sid.as_int ());
-    }
-
-  /* Now purge any constraints involving svalue2s we don't care about.  */
-  restrict_to_used_svalue2s criterion (used);
-  m_constraints->purge (criterion, stats);
-
-  /* Mark any sids that are in constraints that survived.  */
-  {
-    equiv_class *ec;
-    FOR_EACH_VEC_ELT (m_constraints->m_equiv_classes, i, ec)
-      {
-	int j;
-	const svalue2 **sid;
-	FOR_EACH_VEC_ELT (ec->m_vars, j, sid)
-	  {
-	    gcc_assert (!sid->null_p ());
-	    bitmap_set_bit (used, sid->as_int ());
-	  }
-      }
-  }
-
-  /* Build a mapping from old-sid to new-sid so that we can preserve
-     order of the used IDs and move all redundant ones to the end.
-     Iterate though svalue2 IDs, adding used ones to the front of
-     the new list, and unused ones to the back.  */
-  svalue2_id_map map (m_svalue2s.length ());
-  int next_used_new_sval = 0;
-  int after_next_unused_new_sval = m_svalue2s.length ();
-  for (unsigned i = 0; i < m_svalue2s.length (); i++)
-    {
-      const svalue2 *src (svalue2_id::from_int (i));
-      if (bitmap_bit_p (used, i))
-	{
-	  if (logger)
-	    logger->log ("sv%i is used", i);
-	  map.put (src, svalue2_id::from_int (next_used_new_sval++));
-	}
-      else
-	{
-	  if (logger)
-	    logger->log ("sv%i is unused", i);
-	  map.put (src, svalue2_id::from_int (--after_next_unused_new_sval));
-	}
-    }
-  /* The two insertion points should have met.  */
-  gcc_assert (next_used_new_sval == after_next_unused_new_sval);
-
-  /* Now walk the region2s and the constraints, remapping sids,
-     so that all the redundant svalue2s are at the end.  */
-  remap_svalue2_ids (map);
-
-  if (logger)
-    {
-      logger->start_log_line ();
-      logger->log_partial ("map: ");
-      map.dump_to_pp (logger->get_printer ());
-      logger->end_log_line ();
-    }
-
-  /* Notify any client about the remapping and pending deletion.
-     Potentially this could trigger leak warnings.  */
-  if (ctxt)
-    {
-      ctxt->remap_svalue2_ids (map);
-      int num_client_items_purged
-	= ctxt->on_svalue2_purge (svalue2_id::from_int (next_used_new_sval), map);
-      if (stats)
-	stats->m_num_client_items += num_client_items_purged;
-    }
-
-  /* Drop the redundant svalue2s from the end of the vector.  */
-  while ((signed)m_svalue2s.length () > next_used_new_sval)
-    {
-      if (logger)
-	{
-	  const svalue2 *victim = svalue2_id::from_int (m_svalue2s.length () - 1);
-	  logger->log ("deleting sv%i (was sv%i)",
-		       victim.as_int (),
-		       map.get_src_for_dst (victim).as_int ());
-	}
-      delete m_svalue2s.pop ();
-      if (stats)
-	stats->m_num_svalue2s++;
-    }
-
-  validate ();
-}
-#endif
-
-/* Delete all region2s within SET_TO_PURGE, remapping region2 IDs for
-   other region2s.  It's required that there are no uses of the
-   region2s within the set (or the region2 IDs will become invalid).
-
-   Accumulate stats to STATS.  */
-#if 0
-void
-region_model2::purge_region2s (const region2_id_set &set_to_purge,
-			     purge_stats *stats,
-			     logger *)
-{
-  /* Build a mapping from old-rid to new-rid so that we can preserve
-     order of the used IDs and move all redundant ones to the end.
-     Iterate though region2 IDs, adding used ones to the front of
-     the new list, and unused ones to the back.  */
-  region2_id_map map (m_region2s.length ());
-  int next_used_new_reg = 0;
-  int after_next_unused_new_reg = m_region2s.length ();
-  for (unsigned i = 0; i < m_region2s.length (); i++)
-    {
-      region2 *src (region2_id::from_int (i));
-      if (set_to_purge.region2_p (src))
-	map.put (src, region2_id::from_int (--after_next_unused_new_reg));
-      else
-	map.put (src, region2_id::from_int (next_used_new_reg++));
-    }
-  /* The two insertion points should have met.  */
-  gcc_assert (next_used_new_reg == after_next_unused_new_reg);
-
-  /* Now walk the region2s and svalue2s, remapping rids,
-     so that all the redundant region2s are at the end.  */
-  remap_region2_ids (map);
-
-  /* Drop the redundant region2s from the end of the vector.  */
-  while ((signed)m_region2s.length () > next_used_new_reg)
-    {
-      delete m_region2s.pop ();
-      if (stats)
-	stats->m_num_region2s++;
-    }
-}
-#endif
-/* Populate *OUT with RID and all of its descendents.
-   If EXCLUDE_REG is non-null, then don't add it or its descendents.  */
-#if 0
-void
-region_model2::get_descendents (region2 *reg, region2_id_set *out,
-			       region2 *exclude_reg) const
-{
-  out->add_region2 (rid);
-
-  bool changed = true;
-  while (changed)
-    {
-      changed = false;
-      unsigned i;
-      region2 *r;
-      FOR_EACH_VEC_ELT (m_region2s, i, r)
-	{
-	  region2 *iter_reg = region2_id::from_int (i);
-	  if (iter_reg == exclude_reg)
-	    continue;
-	  if (!out->region2_p (iter_reg))
-	    {
-	      region2 *parent_reg = r->get_parent ();
-	      if (!parent_reg.null_p ())
-		if (out->region2_p (parent_reg))
-		  {
-		    out->add_region2 (iter_reg);
-		    changed = true;
-		  }
-	    }
-	}
-    }
-}
-#endif
-
-// FIXME
-/* Delete RID and all descendent region2s.
-   Find any pointers to such region2s; convert them to
-   poisoned values of kind PKIND.
-   Accumulate stats on purged entities into STATS.  */
-
-void
-region_model2::unbind_region2_and_descendents (const region2 *reg,
-					       enum poison_kind pkind,
-					       purge_stats2 *stats,
-					       logger *logger)
-{
-  /* Gather a set of base regions to be unbound.  */
-  hash_set<const region2 *> base_regs;
-  for (store2::cluster_map_t::iterator iter = m_store.begin ();
-       iter != m_store.end (); ++iter)
-    {
-      const region2 *iter_base_reg = (*iter).first;
-      if (iter_base_reg->descendent_of_p (reg))
-	base_regs.add (iter_base_reg);
-    }
-  for (hash_set<const region2 *>::iterator iter = base_regs.begin ();
-       iter != base_regs.end (); ++iter)
-    m_store.purge_cluster (*iter);
-
-  /* Find any pointers to REG or its descendents; convert to poisoned.  */
-  poison_any_pointers_to_descendents (reg, pkind);
-}
-
-/* Implementation of BindingVisitor.
-   Update the bound svalues for regions below REG to use poisoned
-   values instead.  */
-
-struct bad_pointer_finder
-{
-  bad_pointer_finder (const region2 *reg, enum poison_kind pkind,
-		      region_model2_manager *mgr)
-  : m_reg (reg), m_pkind (pkind), m_mgr (mgr), m_count (0)
-  {}
-
-  void on_binding (const binding_key2 *key, const svalue2 *&sval)
-  {
-    if (const region_svalue2 *ptr_sval = sval->dyn_cast_region_svalue2 ())
-      {
-	const region2 *ptr_dst = ptr_sval->get_pointee ();
-	/* Poison ptrs to descendents of REG, but not to REG itself,
-	   otherwise double-free detection doesn't work (since sm-state
-	   for "free" is stored on the original ptr svalue).  */
-	if (ptr_dst->descendent_of_p (m_reg)
-	    && ptr_dst != m_reg)
-	  {
-	    sval = m_mgr->get_or_create_poisoned_svalue2 (m_pkind,
-							  sval->get_type ());
-	    ++m_count;
-	  }
-      }
-  }
-
-  const region2 *m_reg;
-  enum poison_kind m_pkind;
-  region_model2_manager *const m_mgr;
-  int m_count;
-};
-
-/* Find any pointers to REG or its descendents; convert them to
-   poisoned values of kind PKIND.
-   Return the number of pointers that were poisoned.  */
-// FIXME: ^^ unit test for this?
-
-int
-region_model2::poison_any_pointers_to_descendents (const region2 *reg,
-						   enum poison_kind pkind)
-{
-  bad_pointer_finder bv (reg, pkind, m_mgr);
-  m_store.for_each_binding (bv);
-  return bv.m_count;
-}
-
-/* Attempt to merge THIS with OTHER_MODEL, writing the result
-   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);
-  gcc_assert (m_mgr == other_model.m_mgr);
-  gcc_assert (m_mgr == out_model->m_mgr);
-
-  if (m_current_frame != other_model.m_current_frame)
-    return false;
-  out_model->m_current_frame = m_current_frame;
-
-  model2_merger m (this, &other_model, point, out_model);
-
-  if (!store2::can_merge_p (&m_store, &other_model.m_store,
-			    &out_model->m_store, m_mgr->get_store2_manager (),
-			    &m))
-    return false;
-
-  /* Merge constraints.  */
-  constraint_manager2::merge (*m_constraints2,
-			      *other_model.m_constraints2,
-			      out_model->m_constraints2,
-			      m);
-
-  return true;
-}
-
-/* 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.  */
-#if 0
-svalue2_id
-region_model2::get_value_by_name (const char *name) const
-{
-  gcc_assert (name);
-  tree identifier = get_identifier (name);
-  return get_root_region2 ()->get_value_by_name (identifier, *this);
-}
-#endif
-/* Generate or reuse an svalue2 *within this model for an index
-   into an array of type PTR_TYPE, based on OFFSET_SVAL.  */
-#if 0
-svalue2_id
-region_model2::convert_byte_offset_to_array_index (tree ptr_type,
-						  svalue2 *offset_sval)
-{
-  gcc_assert (POINTER_TYPE_P (ptr_type));
-
-  if (tree offset_cst = maybe_get_constant (offset_sval))
-    {
-      tree elem_type = TREE_TYPE (ptr_type);
-
-      /* Arithmetic on void-pointers is a GNU C extension, treating the size
-	 of a void as 1.
-	 https://gcc.gnu.org/onlinedocs/gcc/Pointer-Arith.html  */
-      if (TREE_CODE (elem_type) == VOID_TYPE)
-	return offset_sval;
-
-      /* First, use int_size_in_bytes, to reject the case where we have an
-	 incomplete type, or a non-constant value.  */
-      HOST_WIDE_INT hwi_byte_size = int_size_in_bytes (elem_type);
-      if (hwi_byte_size > 0)
-	{
-	  /* Now call size_in_bytes to get the answer in tree form.  */
-	  tree byte_size = size_in_bytes (elem_type);
-	  gcc_assert (byte_size);
-	  /* Try to get a constant by dividing, ensuring that we're in a
-	     signed representation first.  */
-	  tree index
-	    = fold_binary (TRUNC_DIV_EXPR, ssizetype,
-			   fold_convert (ssizetype, offset_cst),
-			   fold_convert (ssizetype, byte_size));
-	  if (index && TREE_CODE (index) == INTEGER_CST)
-	    return get_or_create_constant_svalue2 (index);
-	}
-    }
-
-  /* Otherwise, we don't know the array index; generate a new unknown value.
-     TODO: do we need to capture the relationship between two unknown
-     values (the offset and the index)?  */
-  return add_svalue2 (new unknown_svalue2 (integer_type_node));
-}
-
-/* Get a region2 of type TYPE for PTR_SVAL[OFFSET_SVAL/sizeof (*PTR_SVAL)].
-
-   If OFFSET_SVAL is known to be zero, then dereference PTR_SVAL.
-   Otherwise, impose a view of "typeof(*PTR_SVAL)[]" on *PTR_SVAL,
-   and then get a view of type TYPE on the relevant array element.  */
-
-region2_id
-region_model2::get_or_create_mem_ref (tree type,
-				     const svalue2 *ptr_sval,
-				     const svalue2 *offset_sval,
-				     region_model2_context *ctxt)
-{
-  const svalue2 *ptr_sval = get_svalue2 (ptr_sval);
-  tree ptr_type = ptr_sval->get_type ();
-  gcc_assert (ptr_type);
-
-  region2 *raw_reg = deref_rvalue (ptr_sval, ctxt);
-
-  const svalue2 *offset_sval = get_svalue2 (offset_sval);
-  tree offset_type = offset_sval->get_type ();
-  gcc_assert (offset_type);
-
-  if (constant_svalue2 *cst_sval = offset_sval->dyn_cast_constant_svalue2 ())
-    {
-      if (zerop (cst_sval->get_constant ()))
-	{
-	  /* Handle the zero offset case.  */
-	  return get_or_create_view (raw_reg, type, ctxt);
-	}
-
-      /* If we're already within an array of the correct type,
-	 then we want to reuse that array, rather than starting
-	 a new view.
-	 If so, figure out our raw_reg's offset from its parent,
-	 if we can, and use that to offset OFFSET_SVAL, and create
-	 the element within the parent region2.  */
-      region2 *raw_reg = get_region2 (raw_reg);
-      region2 *parent_reg = raw_reg->get_parent ();
-      tree parent_type = get_region2 (parent_reg)->get_type ();
-      if (parent_type
-	  && TREE_CODE (parent_type) == ARRAY_TYPE)
-	{
-	  // TODO: check we have the correct parent type
-	  array_region2 *parent_array = get_region2 <array_region2> (parent_reg);
-	  array_region2::key_t key_for_raw_reg;
-	  if (parent_array->get_key_for_child_region2 (raw_reg,
-						      &key_for_raw_reg))
-	    {
-	      /* Convert from offset to index.  */
-	      const svalue2 *index_sval
-		= convert_byte_offset_to_array_index (ptr_type, offset_sval);
-	      if (tree index_cst
-		    = get_svalue2 (index_sval)->maybe_get_constant ())
-		{
-		  array_region2::key_t index_offset
-		    = array_region2::key_from_constant (index_cst);
-		  array_region2::key_t index_rel_to_parent
-		    = key_for_raw_reg + index_offset;
-		  tree index_rel_to_parent_cst
-		    = wide_int_to_tree (integer_type_node,
-					index_rel_to_parent);
-		  const svalue2 *index_sval
-		    = get_or_create_constant_svalue2 (index_rel_to_parent_cst);
-
-		  /* Carry on, using the parent region2 and adjusted index.  */
-		  region2 *element_reg
-		    = parent_array->get_element (this, raw_reg, index_sval,
-						 ctxt);
-		  return get_or_create_view (element_reg, type, ctxt);
-		}
-	    }
-	}
-    }
-
-  tree array_type = build_array_type (TREE_TYPE (ptr_type),
-				      integer_type_node);
-  region2 *array_view_reg = get_or_create_view (raw_reg, array_type, ctxt);
-  array_region2 *array_reg = get_region2 <array_region2> (array_view_reg);
-
-  const svalue2 *index_sval
-    = convert_byte_offset_to_array_index (ptr_type, offset_sval);
-
-  region2 *element_reg
-    = array_reg->get_element (this, array_view_reg, index_sval, ctxt);
-
-  return get_or_create_view (element_reg, type, ctxt);
-}
-
-/* Get a region2 of type TYPE for PTR_SVAL + OFFSET_SVAL.
-
-   If OFFSET_SVAL is known to be zero, then dereference PTR_SVAL.
-   Otherwise, impose a view of "typeof(*PTR_SVAL)[]" on *PTR_SVAL,
-   and then get a view of type TYPE on the relevant array element.  */
-
-region2_id
-region_model2::get_or_create_pointer_plus_expr (tree type,
-					       const svalue2 *ptr_sval,
-					       const svalue2 *offset_in_bytes_sval,
-					       region_model2_context *ctxt)
-{
-  return get_or_create_mem_ref (type,
-				ptr_sval,
-				offset_in_bytes_sval,
-				ctxt);
-}
-
-/* Get or create a view of type TYPE of the region2 with id RAW_ID.
-   Return the id of the view (or RAW *if it of the same type).  */
-
-region2_id
-region_model2::get_or_create_view (region2 *raw_reg, tree type,
-				  region_model2_context *ctxt)
-{
-  region2 *raw_region2 = get_region2 (raw_reg);
-
-  gcc_assert (TYPE_P (type));
-  if (type != raw_region2->get_type ())
-    {
-      /* If the region2 already has a view of the requested type,
-	 reuse it.  */
-      region2 *existing_view_reg = raw_region2->get_view (type, this);
-      if (!existing_view_reg.null_p ())
-	return existing_view_reg;
-
-      /* Otherwise, make one (adding it to the region_model2 and
-	 to the viewed region2).  */
-      region2 *view_reg = add_region2_for_type (raw_reg, type, ctxt);
-      raw_region2->add_view (view_reg, this);
-      // TODO: something to signify that this is a "view"
-      return view_reg;
-    }
-
-  return raw_reg;
-}
-#endif
-
-/* Attempt to get the fndecl used at CALL, if known, or NULL_TREE
-   otherwise.  */
-
-tree
-region_model2::get_fndecl_for_call (const gcall *call,
-				    region_model2_context *ctxt)
-{
-  tree fn_ptr = gimple_call_fn (call);
-  if (fn_ptr == NULL_TREE)
-    return NULL_TREE;
-  const svalue2 *fn_ptr_sval = get_rvalue (fn_ptr, ctxt);
-  if (const region_svalue2 *fn_ptr_ptr
-	= fn_ptr_sval->dyn_cast_region_svalue2 ())
-    {
-      const region2 *reg = fn_ptr_ptr->get_pointee ();
-      if (const function_region2 *fn_reg = reg->dyn_cast_function_region2 ())
-	{
-	  tree fn_decl = fn_reg->get_fndecl ();
-	  cgraph_node *node = cgraph_node::get (fn_decl);
-	  if (!node)
-	    return NULL_TREE;
-	  const cgraph_node *ultimate_node = node->ultimate_alias_target ();
-	  if (ultimate_node)
-	    return ultimate_node->decl;
-	}
-    }
-
-  return NULL_TREE;
-}
-
-/* Would be much simpler to use a lambda here, if it were supported.  */
-
-struct append_ssa_names_cb_data
-{
-  const region_model2 *model;
-  auto_vec<const decl_region2 *> *out;
-};
-
-/* FIXME.  */
-
-void
-region_model2::
-get_ssa_name_regions_for_current_frame (auto_vec<const decl_region2 *> *out)
-  const
-{
-  append_ssa_names_cb_data data;
-  data.model = this;
-  data.out = out;
-  m_store.for_each_cluster (append_ssa_names_cb, &data);
-}
-
-/* FIXME.  */
-
-void
-region_model2::append_ssa_names_cb (const region2 *base_reg,
-				    append_ssa_names_cb_data *cb_data)
-{
-  if (base_reg->get_parent_region () != cb_data->model->m_current_frame)
-    return;
-  if (const decl_region2 *decl_reg = base_reg->dyn_cast_decl_region2 ())
-    {
-      if (TREE_CODE (decl_reg->get_decl ()) == SSA_NAME)
-	cb_data->out->safe_push (decl_reg);
-    }
-}
-
-/* FIXME.  */
-
-const region2 *
-region_model2::create_region_for_heap_alloc (const svalue2 *size_in_bytes)
-{
-  const region2 *reg = m_mgr->create_region_for_heap_alloc ();
-  record_dynamic_extents (reg, size_in_bytes);
-  return reg;
-}
-
-/* FIXME.  */
-
-const region2 *
-region_model2::create_region_for_alloca (const svalue2 *size_in_bytes)
-{
-  const region2 *reg = m_mgr->create_region_for_alloca (m_current_frame);
-  record_dynamic_extents (reg, size_in_bytes);
-  return reg;
-}
-
-/* FIXME.  */
-
-void
-region_model2::record_dynamic_extents (const region2 *reg,
-				       const svalue2 *size_in_bytes)
-{
-#if 0
-  const svalue2 *extents_sval = m_mgr->get_or_create_extents_svalue2 (reg);
-  add_constraint (extents_sval, EQ_EXPR, size_in_bytes);
-  /* TODO: This needs the ability to add constraints in the model based on an
-     svalue2.  */
-  /* TODO: should be capture where we got the extents from?  (so that we can
-     display that to the user)  Perhaps we can just walk backwards along
-     the exploded_path to find where the constraint was added.  */
-  /* FIXME: or should the dynamic extents instead be recorded in a special
-     data map? */
-#endif
-}
-
-/* struct model2_merger.  */
-
-/* Dump a multiline representation of this merger to PP.  */
-
-void
-model2_merger::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  pp_string (pp, "model A:");
-  pp_newline (pp);
-  m_model_a->dump_to_pp (pp, simple, true);
-  pp_newline (pp);
-
-  pp_string (pp, "model B:");
-  pp_newline (pp);
-  m_model_b->dump_to_pp (pp, simple, true);
-  pp_newline (pp);
-
-  pp_string (pp, "merged model:");
-  pp_newline (pp);
-  m_merged_model->dump_to_pp (pp, simple, true);
-  pp_newline (pp);
-
-#if ITERATED_BINOPS
-  pp_string (pp, "iterated binops:");
-  pp_newline (pp);
-  unsigned i;
-  const binop_svalue2 *binop;
-  FOR_EACH_VEC_ELT (m_iterated_binops, i, binop)
-    {
-      pp_string (pp, "  ");
-      binop->dump_to_pp (pp, simple);
-      pp_newline (pp);
-    }
-#endif
-}
-
-/* Dump a multiline representation of this merger to FILE.  */
-
-void
-model2_merger::dump (FILE *fp, 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 = fp;
-  dump_to_pp (&pp, simple);
-  pp_flush (&pp);
-}
-
-/* Dump a multiline representation of this merger to stderr.  */
-
-DEBUG_FUNCTION void
-model2_merger::dump (bool simple) const
-{
-  dump (stderr, simple);
-}
-
-/* FIXME.  */
-#if ITERATED_BINOPS
-void
-model2_merger::add_iterating_widening_binop_thing (const binop_svalue2 *binop_sval)
-{
-  // TODO!
-  m_iterated_binops.safe_push (binop_sval);
-}
-#endif
-
-/* struct svalue2_id_merger_mapping.  */
-
-#if 0
-/* svalue2_id_merger_mapping's ctor.  */
-
-svalue2_id_merger_mapping::svalue2_id_merger_mapping (const region_model2 &a,
-						    const region_model2 &b)
-: m_map_from_a_to_m (a.get_num_svalue2s ()),
-  m_map_from_b_to_m (b.get_num_svalue2s ())
-{
-}
-
-/* Dump a multiline representation of this to PP.  */
-
-void
-svalue2_id_merger_mapping::dump_to_pp (pretty_printer *pp) const
-{
-  pp_string (pp, "svalue2 *map: model A to merged model:");
-  pp_newline (pp);
-  m_map_from_a_to_m.dump_to_pp (pp);
-  pp_newline (pp);
-
-  pp_string (pp, "svalue2 *map: model B to merged model:");
-  pp_newline (pp);
-  m_map_from_b_to_m.dump_to_pp (pp);
-  pp_newline (pp);
-}
-
-/* Dump a multiline representation of this to FILE.  */
-
-void
-svalue2_id_merger_mapping::dump (FILE *fp) const
-{
-  pretty_printer pp;
-  pp_format_decoder (&pp) = default_tree_printer;
-  pp_show_color (&pp) = pp_show_color (global_dc->printer);
-  pp.buffer->stream = fp;
-  dump_to_pp (&pp);
-  pp_flush (&pp);
-}
-
-/* Dump a multiline representation of this to stderr.  */
-
-DEBUG_FUNCTION void
-svalue2_id_merger_mapping::dump () const
-{
-  dump (stderr);
-}
-#endif
-
-} // namespace ana
-
-/* Dump RMODEL fully to stderr (i.e. without summarization).  */
-
-DEBUG_FUNCTION void
-debug (const region_model2 &rmodel)
-{
-  rmodel.dump (false);
-}
-
-/* class engine2.  */
-
-/* FIXME.  */
-
-void
-engine2::log_stats (logger *logger) const
-{
-  m_mgr.log_stats (logger, true);
-}
-
-namespace ana {
-
-#if CHECKING_P
-
-namespace selftest {
-
-/* Build a constant tree of the given type from STR.  */
-
-static tree
-build_real_cst_from_string (tree type, const char *str)
-{
-  REAL_VALUE_TYPE real;
-  real_from_string (&real, str);
-  return build_real (type, real);
-}
-
-/* Append various "interesting" constants to OUT (e.g. NaN).  */
-
-static void
-append_interesting_constants (auto_vec<tree> *out)
-{
-  out->safe_push (build_int_cst (integer_type_node, 0));
-  out->safe_push (build_int_cst (integer_type_node, 42));
-  out->safe_push (build_int_cst (unsigned_type_node, 0));
-  out->safe_push (build_int_cst (unsigned_type_node, 42));
-  out->safe_push (build_real_cst_from_string (float_type_node, "QNaN"));
-  out->safe_push (build_real_cst_from_string (float_type_node, "-QNaN"));
-  out->safe_push (build_real_cst_from_string (float_type_node, "SNaN"));
-  out->safe_push (build_real_cst_from_string (float_type_node, "-SNaN"));
-  out->safe_push (build_real_cst_from_string (float_type_node, "0.0"));
-  out->safe_push (build_real_cst_from_string (float_type_node, "-0.0"));
-  out->safe_push (build_real_cst_from_string (float_type_node, "Inf"));
-  out->safe_push (build_real_cst_from_string (float_type_node, "-Inf"));
-}
-
-/* Verify that tree_cmp is a well-behaved comparator for qsort, even
-   if the underlying constants aren't comparable.  */
-
-static void
-test_tree_cmp_on_constants ()
-{
-  auto_vec<tree> csts;
-  append_interesting_constants (&csts);
-
-  /* Try sorting every triple. */
-  const unsigned num = csts.length ();
-  for (unsigned i = 0; i < num; i++)
-    for (unsigned j = 0; j < num; j++)
-      for (unsigned k = 0; k < num; k++)
-	{
-	  auto_vec<tree> v (3);
-	  v.quick_push (csts[i]);
-	  v.quick_push (csts[j]);
-	  v.quick_push (csts[k]);
-	  v.qsort (tree_cmp);
-	}
-}
-
-/* Implementation detail of the ASSERT_CONDITION_*2 macros.  */
-
-void
-assert_condition2 (const location &loc,
-		   region_model2 &model,
-		   const svalue2 *lhs, tree_code op, const svalue2 *rhs,
-		   tristate expected)
-{
-  tristate actual = model.eval_condition (lhs, op, rhs);
-  ASSERT_EQ_AT (loc, actual, expected);
-}
-
-/* Implementation detail of the ASSERT_CONDITION_*2 macros.  */
-
-void
-assert_condition2 (const location &loc,
-		   region_model2 &model,
-		   tree lhs, tree_code op, tree rhs,
-		   tristate expected)
-{
-  tristate actual = model.eval_condition (lhs, op, rhs, NULL);
-  ASSERT_EQ_AT (loc, actual, expected);
-}
-
-/* Implementation detail of ASSERT_DUMP_TREE_EQ.  */
-
-static void
-assert_dump_tree_eq (const location &loc, tree t, const char *expected)
-{
-  auto_fix_quotes sentinel;
-  pretty_printer pp;
-  pp_format_decoder (&pp) = default_tree_printer;
-  dump_tree (&pp, t);
-  ASSERT_STREQ_AT (loc, pp_formatted_text (&pp), expected);
-}
-
-/* Assert that dump_tree (T) is EXPECTED.  */
-
-#define ASSERT_DUMP_TREE_EQ(T, EXPECTED) \
-  SELFTEST_BEGIN_STMT							\
-  assert_dump_tree_eq ((SELFTEST_LOCATION), (T), (EXPECTED)); \
-  SELFTEST_END_STMT
-
-/* Implementation detail of ASSERT_DUMP_EQ.  */
-
-static void
-assert_dump_eq (const location &loc,
-		const region_model2 &model,
-		bool summarize,
-		const char *expected)
-{
-  auto_fix_quotes sentinel;
-  pretty_printer pp;
-  pp_format_decoder (&pp) = default_tree_printer;
-
-  model.dump_to_pp (&pp, summarize, true);
-  ASSERT_STREQ_AT (loc, pp_formatted_text (&pp), expected);
-}
-
-/* Assert that MODEL.dump_to_pp (SUMMARIZE) is EXPECTED.  */
-
-#define ASSERT_DUMP_EQ(MODEL, SUMMARIZE, EXPECTED) \
-  SELFTEST_BEGIN_STMT							\
-  assert_dump_eq ((SELFTEST_LOCATION), (MODEL), (SUMMARIZE), (EXPECTED)); \
-  SELFTEST_END_STMT
-
-/* Smoketest for region_model2::dump_to_pp.  */
-
-static void
-test_dump ()
-{
-  region_model2_manager mgr;
-  region_model2 model (&mgr);
-
-  ASSERT_DUMP_EQ (model, false,
-		  "stack depth: 0\n"
-		  "m_called_unknown_fn: FALSE\n"
-		  "constraint_manager2:\n"
-		  "  equiv classes:\n"
-		  "  constraints:\n");
-  ASSERT_DUMP_EQ (model, true,
-		  "stack depth: 0\n"
-		  "m_called_unknown_fn: FALSE\n"
-		  "constraint_manager2:\n"
-		  "  equiv classes:\n"
-		  "  constraints:\n");
-}
-
-/* Helper function for selftests.  Create a struct or union type named NAME,
-   with the fields given by the FIELD_DECLS in FIELDS.
-   If IS_STRUCT is true create a RECORD_TYPE (aka a struct), otherwise
-   create a UNION_TYPE.  */
-
-static tree
-make_test_compound_type (const char *name, bool is_struct,
-			 const auto_vec<tree> *fields)
-{
-  tree t = make_node (is_struct ? RECORD_TYPE : UNION_TYPE);
-  TYPE_SIZE (t) = 0;
-
-  tree fieldlist = NULL;
-  int i;
-  tree field;
-  FOR_EACH_VEC_ELT (*fields, i, field)
-    {
-      gcc_assert (TREE_CODE (field) == FIELD_DECL);
-      DECL_CONTEXT (field) = t;
-      fieldlist = chainon (field, fieldlist);
-    }
-  fieldlist = nreverse (fieldlist);
-  TYPE_FIELDS (t) = fieldlist;
-
-  layout_type (t);
-
-#if 1
-  TYPE_NAME (t) = get_identifier (name);
-#else
-  tree id = get_identifier (name);
-  tree implicit_decl = build_decl (UNKNOWN_LOCATION, TYPE_DECL, id, t);
-  DECL_ARTIFICIAL (implicit_decl) = 1;
-  //SET_DECL_IMPLICIT_TYPEDEF_P (implicit_decl);
-  TYPE_NAME (t) = implicit_decl;
-  TYPE_STUB_DECL (t) = implicit_decl;
-
-  /* FIXME: ^^^ this needs to be a decl, or selftest-c++ crashes
-     printing types, treating the TYPE_NAME as a decl.
-     C++ FE uses create_implicit_typedef and makes a  TYPE_DECL.  */
-#endif
-
-  return t;
-}
-
-/* Selftest fixture for creating the type "struct coord {int x; int y; };".  */
-
-struct coord_test2
-{
-  coord_test2 ()
-  {
-    auto_vec<tree> fields;
-    m_x_field = build_decl (UNKNOWN_LOCATION, FIELD_DECL,
-			       get_identifier ("x"), integer_type_node);
-    fields.safe_push (m_x_field);
-    m_y_field = build_decl (UNKNOWN_LOCATION, FIELD_DECL,
-			       get_identifier ("y"), integer_type_node);
-    fields.safe_push (m_y_field);
-    m_coord_type = make_test_compound_type ("coord", true, &fields);
-  }
-
-  tree m_x_field;
-  tree m_y_field;
-  tree m_coord_type;
-};
-
-/* Verify that dumps can show struct fields.  */
-
-static void
-test_dump_2 ()
-{
-  coord_test2 ct;
-
-  tree c = build_global_decl ("c", ct.m_coord_type);
-  tree c_x = build3 (COMPONENT_REF, TREE_TYPE (ct.m_x_field),
-		     c, ct.m_x_field, NULL_TREE);
-  tree c_y = build3 (COMPONENT_REF, TREE_TYPE (ct.m_y_field),
-		     c, ct.m_y_field, NULL_TREE);
-
-  tree int_17 = build_int_cst (integer_type_node, 17);
-  tree int_m3 = build_int_cst (integer_type_node, -3);
-
-  region_model2_manager mgr;
-  region_model2 model (&mgr);
-  model.set_value (c_x, int_17, NULL);
-  model.set_value (c_y, int_m3, NULL);
-
-  // TODO: test get_value
-
-  /* Verify get_offset for "c.x".  */
-  {
-    const region2 *c_x_reg = model.get_lvalue (c_x, NULL);
-    region_offset offset = c_x_reg->get_offset ();
-    ASSERT_EQ (offset.get_base_region (), model.get_lvalue (c, NULL));
-    ASSERT_EQ (offset.get_bit_offset (), 0);
-  }
-
-  /* Verify get_offset for "c.y".  */
-  {
-    const region2 *c_y_reg = model.get_lvalue (c_y, NULL);
-    region_offset offset = c_y_reg->get_offset ();
-    ASSERT_EQ (offset.get_base_region (), model.get_lvalue (c, NULL));
-    ASSERT_EQ (offset.get_bit_offset (), INT_TYPE_SIZE);
-  }
-
-  // FIXME: test of casting to an "int *"
-
-  // FIXME: rename this test; it's doing much more that just dumps
-
-  /* Simplified dump.  */
-#if 0
-  ASSERT_DUMP_EQ (model, true,
-		  "base region: {c} has cluster: {"
-		  "binding key: {region: c, kind: direct, offset: 0},"
-		  " value: {17},"
-		  " binding key: {region: c, kind: direct, offset: 32},"
-		  " value: {-3}"
-		  "}");
-#endif
-
-  /* Full dump.  */
-#if 0
-  ASSERT_DUMP_EQ
-    (model, false,
-     "base region: {decl_region2(globals, `struct coord', `c')} has cluster: {binding key: {region: field_region2(decl_region2(globals, `struct coord', `c'), `int', `x'), kind: direct, offset: 0}, value: {constant_svalue2(`int', 17)}, binding key: {region: field_region2(decl_region2(globals, `struct coord', `c'), `int', `y'), kind: direct, offset: 0}, value: {constant_svalue2(`int', -3)}}");
-#endif
-}
-
-#if 0
-
-/* Verify that dumps can show array elements.  */
-
-static void
-test_dump_3 ()
-{
-  tree tlen = size_int (10);
-  tree arr_type = build_array_type (char_type_node, build_index_type (tlen));
-
-  tree a = build_global_decl ("a", arr_type);
-
-  region_model2 model;
-  tree int_0 = build_int_cst (integer_type_node, 0);
-  tree a_0 = build4 (ARRAY_REF, char_type_node,
-		     a, int_0, NULL_TREE, NULL_TREE);
-  tree char_A = build_int_cst (char_type_node, 'A');
-  model.set_value (a_0, char_A, NULL);
-
-  /* Simplified dump.  */
-  ASSERT_DUMP_EQ (model, true, "a[0]: 65");
-
-  /* Full dump.  */
-  ASSERT_DUMP_EQ
-    (model, false,
-     "r0: {kind: `root', parent: null, sval: null}\n"
-     "`-globals: r1: {kind: `globals', parent: r0, sval: null, map: {`a': r2}}\n"
-     "  `-`a': r2: {kind: `array', parent: r1, sval: null, type: `char[11]', array: {[0]: r3}}\n"
-     "    |: type: `char[11]'\n"
-     "    `-[0]: r3: {kind: `primitive', parent: r2, sval: sv1, type: `char'}\n"
-     "      |: sval: sv1: {type: `char', `65'}\n"
-     "      |: type: `char'\n"
-     "svalue2s:\n"
-     "  sv0: {type: `int', `0'}\n"
-     "  sv1: {type: `char', `65'}\n"
-     "constraint manager:\n"
-     "  equiv classes:\n"
-     "  constraints:\n");
-}
-
-#endif
-/* Verify that region_model2::get_representative_tree works as expected.  */
-
-static void
-test_get_representative_tree ()
-{
-  region_model2_manager mgr;
-
-  /* STRING_CST.  */
-  {
-    tree string_cst = build_string (4, "foo");
-    region_model2 m (&mgr);
-    const svalue2 *str_sval = m.get_rvalue (string_cst, NULL);
-    tree rep = m.get_representative_tree (str_sval);
-    ASSERT_EQ (rep, string_cst);
-  }
-
-  /* String literal.  */
-  {
-    tree string_cst_ptr = build_string_literal (4, "foo");
-    region_model2 m (&mgr);
-    const svalue2 *str_sval = m.get_rvalue (string_cst_ptr, NULL);
-    tree rep = m.get_representative_tree (str_sval);
-    ASSERT_DUMP_TREE_EQ (rep, "&\"foo\"[0]");
-  }
-
-  /* Value of a primitive local.  */
-  {
-    // TODO
-  }
-
-  /* Value of an element within an array.  */
-  {
-    tree tlen = size_int (10);
-    tree arr_type = build_array_type (char_type_node, build_index_type (tlen));
-    tree a = build_global_decl ("a", arr_type);
-    placeholder_svalue2 test_sval (char_type_node, "test value");
-
-    /* Value of a[3].  */
-    {
-      test_region_model2_context ctxt;
-      region_model2 model (&mgr);
-      tree int_3 = build_int_cst (integer_type_node, 3);
-      tree a_3 = build4 (ARRAY_REF, char_type_node,
-			 a, int_3, NULL_TREE, NULL_TREE);
-      const region2 *a_3_reg = model.get_lvalue (a_3, &ctxt);
-      model.set_value (a_3_reg, &test_sval, &ctxt);
-      tree rep = model.get_representative_tree (&test_sval);
-      ASSERT_DUMP_TREE_EQ (rep, "a[3]");
-    }
-
-    /* Value of a[0].  */
-    {
-      test_region_model2_context ctxt;
-      region_model2 model (&mgr);
-      tree idx = build_int_cst (integer_type_node, 0);
-      tree a_0 = build4 (ARRAY_REF, char_type_node,
-			 a, idx, NULL_TREE, NULL_TREE);
-      const region2 *a_0_reg = model.get_lvalue (a_0, &ctxt);
-      model.set_value (a_0_reg, &test_sval, &ctxt);
-      tree rep = model.get_representative_tree (&test_sval);
-      ASSERT_DUMP_TREE_EQ (rep, "a[0]");
-    }
-  }
-
-  /* Value of a field within a struct.  */
-  {
-    coord_test2 ct;
-
-    tree c = build_global_decl ("c", ct.m_coord_type);
-    tree c_x = build3 (COMPONENT_REF, TREE_TYPE (ct.m_x_field),
-		       c, ct.m_x_field, NULL_TREE);
-    tree c_y = build3 (COMPONENT_REF, TREE_TYPE (ct.m_y_field),
-		       c, ct.m_y_field, NULL_TREE);
-
-    test_region_model2_context ctxt;
-
-    /* Value of initial field.  */
-    {
-      region_model2 m (&mgr);
-      const region2 *c_x_reg = m.get_lvalue (c_x, &ctxt);
-      placeholder_svalue2 test_sval_x (integer_type_node, "test x val");
-      m.set_value (c_x_reg, &test_sval_x, &ctxt);
-      tree rep = m.get_representative_tree (&test_sval_x);
-      ASSERT_DUMP_TREE_EQ (rep, "c.x");
-    }
-
-    /* Value of non-initial field.  */
-    {
-      region_model2 m (&mgr);
-      const region2 *c_y_reg = m.get_lvalue (c_y, &ctxt);
-      placeholder_svalue2 test_sval_y (integer_type_node, "test y val");
-      m.set_value (c_y_reg, &test_sval_y, &ctxt);
-      tree rep = m.get_representative_tree (&test_sval_y);
-      ASSERT_DUMP_TREE_EQ (rep, "c.y");
-    }
-  }
-
-  /* Value of a field within a union.  */
-  {
-    // TODO
-  }
-}
-
-/* Verify that calling region_model2::get_rvalue repeatedly on the same
-   tree constant retrieves the same svalue2 *.  */
-
-static void
-test_unique_constants ()
-{
-  tree int_0 = build_int_cst (integer_type_node, 0);
-  tree int_42 = build_int_cst (integer_type_node, 42);
-
-  test_region_model2_context ctxt;
-  region_model2_manager mgr;
-  region_model2 model (&mgr);
-  ASSERT_EQ (model.get_rvalue (int_0, &ctxt), model.get_rvalue (int_0, &ctxt));
-  ASSERT_EQ (model.get_rvalue (int_42, &ctxt),
-	     model.get_rvalue (int_42, &ctxt));
-  ASSERT_NE (model.get_rvalue (int_0, &ctxt), model.get_rvalue (int_42, &ctxt));
-  ASSERT_EQ (ctxt.get_num_diagnostics (), 0);
-
-  /* A "(const int)42" will be a different tree from "(int)42)"...  */
-  tree const_int_type_node
-    = build_qualified_type (integer_type_node, TYPE_QUAL_CONST);
-  tree const_int_42 = build_int_cst (const_int_type_node, 42);
-  ASSERT_NE (int_42, const_int_42);
-  /* It should have a different const_svalue2.  */
-  const svalue2 *int_42_sval = model.get_rvalue (int_42, &ctxt);
-  const svalue2 *const_int_42_sval = model.get_rvalue (const_int_42, &ctxt);
-  ASSERT_NE (int_42_sval, const_int_42_sval);
-  /* But they should compare as equal.  */
-  ASSERT_CONDITION_TRUE2 (model, int_42_sval, EQ_EXPR, const_int_42_sval);
-  ASSERT_CONDITION_FALSE2 (model, int_42_sval, NE_EXPR, const_int_42_sval);
-}
-
-/* Verify that each type gets its own singleton unknown_svalue2 within a
-   region_model2_manager, and that NULL_TREE gets its own singleton.  */
-
-static void
-test_unique_unknowns ()
-{
-  region_model2_manager mgr;
-  const svalue2 *unknown_int
-    = mgr.get_or_create_unknown_svalue2 (integer_type_node);
-  /* Repeated calls with the same type should get the same "unknown"
-     svalue2.  */
-  const svalue2 *unknown_int_2
-    = mgr.get_or_create_unknown_svalue2 (integer_type_node);
-  ASSERT_EQ (unknown_int, unknown_int_2);
-
-  /* Different types (or the NULL type) should have different
-     unknown_svalue2s.  */
-  const svalue2 *unknown_NULL_type = mgr.get_or_create_unknown_svalue2 (NULL);
-  ASSERT_NE (unknown_NULL_type, unknown_int);
-
-  /* Repeated calls with NULL for the type should get the same "unknown"
-     svalue2.  */
-  const svalue2 *unknown_NULL_type_2 = mgr.get_or_create_unknown_svalue2 (NULL);
-  ASSERT_EQ (unknown_NULL_type, unknown_NULL_type_2);
-}
-
-#if 0
-/* Check that operator== and hashing works as expected for the
-   various svalue2 subclasses.  */
-
-static void
-test_svalue2_equality ()
-{
-  tree int_42 = build_int_cst (integer_type_node, 42);
-  tree int_0 = build_int_cst (integer_type_node, 0);
-
-  /* Create pairs instances of the various subclasses of svalue2,
-     testing for hash and equality between (this, this) and
-     (this, other of same subclass). */
-  const svalue2 *ptr_to_r0
-    = new region_svalue2 (ptr_type_node, region2_id::from_int (0));
-  const svalue2 *ptr_to_r1
-    = new region_svalue2 (ptr_type_node, region2_id::from_int (1));
-
-  ASSERT_EQ (ptr_to_r0->hash (), ptr_to_r0->hash ());
-  ASSERT_EQ (*ptr_to_r0, *ptr_to_r0);
-
-  ASSERT_NE (ptr_to_r0->hash (), ptr_to_r1->hash ());
-  ASSERT_NE (*ptr_to_r0, *ptr_to_r1);
-
-  const svalue2 *cst_int_42 = new constant_svalue2 (int_42);
-  const svalue2 *cst_int_0 = new constant_svalue2 (int_0);
-
-  ASSERT_EQ (cst_int_42->hash (), cst_int_42->hash ());
-  ASSERT_EQ (*cst_int_42, *cst_int_42);
-
-  ASSERT_NE (cst_int_42->hash (), cst_int_0->hash ());
-  ASSERT_NE (*cst_int_42, *cst_int_0);
-
-  const svalue2 *uninit = new poisoned_svalue2 (POISON_KIND_UNINIT, NULL_TREE);
-  const svalue2 *freed = new poisoned_svalue2 (POISON_KIND_FREED, NULL_TREE);
-
-  ASSERT_EQ (uninit->hash (), uninit->hash ());
-  ASSERT_EQ (*uninit, *uninit);
-
-  ASSERT_NE (uninit->hash (), freed->hash ());
-  ASSERT_NE (*uninit, *freed);
-
-  const svalue2 *unknown_0 = new unknown_svalue2 (ptr_type_node);
-  const svalue2 *unknown_1 = new unknown_svalue2 (ptr_type_node);
-  ASSERT_EQ (unknown_0->hash (), unknown_0->hash ());
-  ASSERT_EQ (*unknown_0, *unknown_0);
-  ASSERT_EQ (*unknown_1, *unknown_1);
-
-  /* Comparisons between different kinds of svalue2.  */
-  ASSERT_NE (*ptr_to_r0, *cst_int_42);
-  ASSERT_NE (*ptr_to_r0, *uninit);
-  ASSERT_NE (*ptr_to_r0, *unknown_0);
-  ASSERT_NE (*cst_int_42, *ptr_to_r0);
-  ASSERT_NE (*cst_int_42, *uninit);
-  ASSERT_NE (*cst_int_42, *unknown_0);
-  ASSERT_NE (*uninit, *ptr_to_r0);
-  ASSERT_NE (*uninit, *cst_int_42);
-  ASSERT_NE (*uninit, *unknown_0);
-  ASSERT_NE (*unknown_0, *ptr_to_r0);
-  ASSERT_NE (*unknown_0, *cst_int_42);
-  ASSERT_NE (*unknown_0, *uninit);
-
-  delete ptr_to_r0;
-  delete ptr_to_r1;
-  delete cst_int_42;
-  delete cst_int_0;
-  delete uninit;
-  delete freed;
-  delete unknown_0;
-  delete unknown_1;
-}
-
-/* Check that operator== and hashing works as expected for the
-   various region2 subclasses.  */
-
-static void
-test_region2_equality ()
-{
-  region2 *r0
-    = new primitive_region2 (region2_id::from_int (3), integer_type_node);
-  region2 *r1
-    = new primitive_region2 (region2_id::from_int (4), integer_type_node);
-
-  ASSERT_EQ (*r0, *r0);
-  ASSERT_EQ (r0->hash (), r0->hash ());
-  ASSERT_NE (*r0, *r1);
-  ASSERT_NE (r0->hash (), r1->hash ());
-
-  delete r0;
-  delete r1;
-
-  // TODO: test coverage for the map within a map_region2
-}
-#endif
-
-/* Verify that initial_svalue2 are handled as expected.  */
-
-static void
-test_initial_svalue2_folding ()
-{
-  region_model2_manager mgr;
-  tree x = build_global_decl ("x", integer_type_node);
-  tree y = build_global_decl ("y", integer_type_node);
-
-  test_region_model2_context ctxt;
-  region_model2 model (&mgr);
-  const svalue2 *x_init = model.get_rvalue (x, &ctxt);
-  const svalue2 *y_init = model.get_rvalue (y, &ctxt);
-  ASSERT_NE (x_init, y_init);
-  const region2 *x_reg = model.get_lvalue (x, &ctxt);
-  ASSERT_EQ (x_init, mgr.get_or_create_initial_value (x_reg));
-
-}
-
-/* Verify that unary ops are folded as expected.  */
-
-static void
-test_unaryop_svalue2_folding ()
-{
-  region_model2_manager mgr;
-  tree x = build_global_decl ("x", integer_type_node);
-  tree y = build_global_decl ("y", integer_type_node);
-
-  test_region_model2_context ctxt;
-  region_model2 model (&mgr);
-  const svalue2 *x_init = model.get_rvalue (x, &ctxt);
-  const svalue2 *y_init = model.get_rvalue (y, &ctxt);
-  const region2 *x_reg = model.get_lvalue (x, &ctxt);
-  ASSERT_EQ (x_init, mgr.get_or_create_initial_value (x_reg));
-
-  /* "(int)x" -> "x".  */
-  ASSERT_EQ (x_init, mgr.get_or_create_cast (integer_type_node, x_init));
-
-  /* "(void *)x" -> something other than "x".  */
-  ASSERT_NE (x_init, mgr.get_or_create_cast (ptr_type_node, x_init));
-
-  /* "!(x == y)" -> "x != y".  */
-  ASSERT_EQ (mgr.get_or_create_unaryop
-	       (boolean_type_node, TRUTH_NOT_EXPR,
-		mgr.get_or_create_binop (boolean_type_node, EQ_EXPR,
-					 x_init, y_init)),
-	     mgr.get_or_create_binop (boolean_type_node, NE_EXPR,
-				      x_init, y_init));
-  /* "!(x > y)" -> "x <= y".  */
-  ASSERT_EQ (mgr.get_or_create_unaryop
-	       (boolean_type_node, TRUTH_NOT_EXPR,
-		mgr.get_or_create_binop (boolean_type_node, GT_EXPR,
-					 x_init, y_init)),
-	     mgr.get_or_create_binop (boolean_type_node, LE_EXPR,
-				      x_init, y_init));
-}
-
-/* Verify that binops on constant svalues are folded.  */
-
-static void
-test_binop_svalue2_folding ()
-{
-#define NUM_CSTS 10
-  tree cst_int[NUM_CSTS];
-  region_model2_manager mgr;
-  const svalue2 *cst_sval[NUM_CSTS];
-  for (int i = 0; i < NUM_CSTS; i++)
-    {
-      cst_int[i] = build_int_cst (integer_type_node, i);
-      cst_sval[i] = mgr.get_or_create_constant_svalue2 (cst_int[i]);
-      ASSERT_EQ (cst_sval[i]->get_kind (), svalue2::SK_CONSTANT);
-      ASSERT_EQ (cst_sval[i]->maybe_get_constant (), cst_int[i]);
-    }
-
-  for (int i = 0; i < NUM_CSTS; i++)
-    for (int j = 0; j < NUM_CSTS; j++)
-      {
-	if (i != j)
-	  ASSERT_NE (cst_sval[i], cst_sval[j]);
-	if (i + j < NUM_CSTS)
-	  {
-	    const svalue2 *sum
-	      = mgr.get_or_create_binop (integer_type_node, PLUS_EXPR,
-					 cst_sval[i], cst_sval[j]);
-	    ASSERT_EQ (sum, cst_sval[i + j]);
-	  }
-	if (i - j >= 0)
-	  {
-	    const svalue2 *difference
-	      = mgr.get_or_create_binop (integer_type_node, MINUS_EXPR,
-					 cst_sval[i], cst_sval[j]);
-	    ASSERT_EQ (difference, cst_sval[i - j]);
-	  }
-	if (i * j < NUM_CSTS)
-	  {
-	    const svalue2 *product
-	      = mgr.get_or_create_binop (integer_type_node, MULT_EXPR,
-					 cst_sval[i], cst_sval[j]);
-	    ASSERT_EQ (product, cst_sval[i * j]);
-	  }
-	const svalue2 *eq = mgr.get_or_create_binop (integer_type_node, EQ_EXPR,
-					       cst_sval[i], cst_sval[j]);
-	ASSERT_EQ (eq, i == j ? cst_sval[1] : cst_sval [0]);
-	const svalue2 *neq = mgr.get_or_create_binop (integer_type_node, NE_EXPR,
-						cst_sval[i], cst_sval[j]);
-	ASSERT_EQ (neq, i != j ? cst_sval[1] : cst_sval [0]);
-	// etc
-      }
-
-  tree x = build_global_decl ("x", integer_type_node);
-
-  test_region_model2_context ctxt;
-  region_model2 model (&mgr);
-  const svalue2 *x_init = model.get_rvalue (x, &ctxt);
-
-  /* PLUS_EXPR folding.  */
-  const svalue2 *x_init_plus_zero
-    = mgr.get_or_create_binop (integer_type_node, PLUS_EXPR,
-			       x_init, cst_sval[0]);
-  ASSERT_EQ (x_init_plus_zero, x_init);
-  const svalue2 *zero_plus_x_init
-    = mgr.get_or_create_binop (integer_type_node, PLUS_EXPR,
-			       cst_sval[0], x_init);
-  ASSERT_EQ (zero_plus_x_init, x_init);
-
-  /* MULT_EXPR folding.  */
-  const svalue2 *x_init_times_zero
-    = mgr.get_or_create_binop (integer_type_node, MULT_EXPR,
-			       x_init, cst_sval[0]);
-  ASSERT_EQ (x_init_times_zero, cst_sval[0]);
-  const svalue2 *zero_times_x_init
-    = mgr.get_or_create_binop (integer_type_node, MULT_EXPR,
-			       cst_sval[0], x_init);
-  ASSERT_EQ (zero_times_x_init, cst_sval[0]);
-
-  const svalue2 *x_init_times_one
-    = mgr.get_or_create_binop (integer_type_node, MULT_EXPR,
-			       x_init, cst_sval[1]);
-  ASSERT_EQ (x_init_times_one, x_init);
-  const svalue2 *one_times_x_init
-    = mgr.get_or_create_binop (integer_type_node, MULT_EXPR,
-			       cst_sval[1], x_init);
-  ASSERT_EQ (one_times_x_init, x_init);
-
-  // etc
-  // TODO: do we want to use the match-and-simplify DSL for this?
-
-  /* Verify that binops put any constants on the RHS.  */
-  const svalue2 *four_times_x_init
-    = mgr.get_or_create_binop (integer_type_node, MULT_EXPR,
-			       cst_sval[4], x_init);
-  const svalue2 *x_init_times_four
-    = mgr.get_or_create_binop (integer_type_node, MULT_EXPR,
-			       x_init, cst_sval[4]);
-  ASSERT_EQ (four_times_x_init, x_init_times_four);
-  const binop_svalue2 *binop = four_times_x_init->dyn_cast_binop_svalue2 ();
-  ASSERT_EQ (binop->get_op (), MULT_EXPR);
-  ASSERT_EQ (binop->get_arg0 (), x_init);
-  ASSERT_EQ (binop->get_arg1 (), cst_sval[4]);
-
-  /* Verify that ((x + 1) + 1) == (x + 2).  */
-  const svalue2 *x_init_plus_one
-    = mgr.get_or_create_binop (integer_type_node, PLUS_EXPR,
-			       x_init, cst_sval[1]);
-  const svalue2 *x_init_plus_two
-    = mgr.get_or_create_binop (integer_type_node, PLUS_EXPR,
-			       x_init, cst_sval[2]);
-  const svalue2 *x_init_plus_one_plus_one
-    = mgr.get_or_create_binop (integer_type_node, PLUS_EXPR,
-			       x_init_plus_one, cst_sval[1]);
-  ASSERT_EQ (x_init_plus_one_plus_one, x_init_plus_two);
-}
-
-/* Verify that sub_svalue2s are folded as expected.  */
-
-static void
-test_sub_svalue2_folding ()
-{
-  coord_test2 ct;
-  tree c = build_global_decl ("c", ct.m_coord_type);
-  tree c_x = build3 (COMPONENT_REF, TREE_TYPE (ct.m_x_field),
-		     c, ct.m_x_field, NULL_TREE);
-
-  region_model2_manager mgr;
-  region_model2 model (&mgr);
-  test_region_model2_context ctxt;
-  const region2 *c_x_reg = model.get_lvalue (c_x, &ctxt);
-
-  /* Verify that sub_svalue2 of "unknown" simply
-     yields an unknown.  */
-
-  const svalue2 *unknown = mgr.get_or_create_unknown_svalue2 (ct.m_coord_type);
-  const svalue2 *sub = mgr.get_or_create_sub_svalue2 (TREE_TYPE (ct.m_x_field),
-						      unknown, c_x_reg);
-  ASSERT_EQ (sub->get_kind (), svalue2::SK_UNKNOWN);
-  ASSERT_EQ (sub->get_type (), TREE_TYPE (ct.m_x_field));
-}
-
-/* Test that region2::descendent_of_p works as expected.  */
-
-static void
-test_descendent_of_p ()
-{
-  region_model2_manager mgr;
-  const region2 *stack = mgr.get_stack_region ();
-  const region2 *heap = mgr.get_heap_region ();
-  const region2 *code = mgr.get_code_region ();
-  const region2 *globals = mgr.get_globals_region ();
-
-  /* descendent_of_p should return true when used on the region itself.  */
-  ASSERT_TRUE (stack->descendent_of_p (stack));
-  ASSERT_FALSE (stack->descendent_of_p (heap));
-  ASSERT_FALSE (stack->descendent_of_p (code));
-  ASSERT_FALSE (stack->descendent_of_p (globals));
-
-  tree x = build_global_decl ("x", integer_type_node);
-  const region2 *x_reg = mgr.get_region_for_global (x);
-  ASSERT_TRUE (x_reg->descendent_of_p (globals));
-
-  /* A cast_region2 should be a descendent of the original region.  */
-  const region2 *cast_reg = mgr.get_cast_region (x_reg, ptr_type_node);
-  ASSERT_TRUE (cast_reg->descendent_of_p (x_reg));
-}
-
-#if 0
-/* A subclass of purge_criteria for selftests: purge all const svalue2 *instances.  */
-
-class purge_all_svalue2_ids : public purge_criteria
-{
-public:
-  bool should_purge_p (svalue2_id) const FINAL OVERRIDE
-  {
-    return true;
-  }
-};
-
-/* A subclass of purge_criteria: purge a specific svalue2_id.  */
-
-class purge_one_svalue2 *: public purge_criteria
-{
-public:
-  purge_one_svalue2 *(svalue2 *victim) : m_victim (victim) {}
-
-  purge_one_svalue2 *(region_model2 model, tree expr)
-  : m_victim (model.get_rvalue (expr, NULL)) {}
-
-  bool should_purge_p (svalue2 *sval) const FINAL OVERRIDE
-  {
-    return sid == m_victim;
-  }
-
-private:
-  svalue2 *m_victim;
-};
-
-/* Check that constraint_manager::purge works for individual svalue2_ids.  */
-
-static void
-test_purging_by_criteria ()
-{
-  tree int_42 = build_int_cst (integer_type_node, 42);
-  tree int_0 = build_int_cst (integer_type_node, 0);
-
-  tree x = build_global_decl ("x", integer_type_node);
-  tree y = build_global_decl ("y", integer_type_node);
-
-  {
-    region_model2 model0;
-    region_model2 model1;
-
-    ADD_SAT_CONSTRAINT (model1, x, EQ_EXPR, y);
-    ASSERT_NE (model0, model1);
-
-    purge_stats stats_for_px;
-    purge_one_svalue2 *px (model1, x);
-    model1.get_constraints ()->purge (px, &stats_for_px);
-    ASSERT_EQ (stats_for_px.m_num_equiv_classes, 0);
-
-    purge_stats stats_for_py;
-    purge_one_svalue2 *py (model1.get_rvalue (y, NULL));
-    model1.get_constraints ()->purge (py, &stats_for_py);
-    ASSERT_EQ (stats_for_py.m_num_equiv_classes, 1);
-
-    ASSERT_EQ (*model0.get_constraints (), *model1.get_constraints ());
-  }
-
-  {
-    region_model2 model0;
-    region_model2 model1;
-
-    ADD_SAT_CONSTRAINT (model1, x, EQ_EXPR, int_42);
-    ASSERT_NE (model0, model1);
-    ASSERT_CONDITION_TRUE (model1, x, EQ_EXPR, int_42);
-
-    purge_stats stats;
-    model1.get_constraints ()->purge (purge_one_svalue2 *(model1, x), &stats);
-
-    ASSERT_CONDITION_UNKNOWN (model1, x, EQ_EXPR, int_42);
-  }
-
-  {
-    region_model2 model0;
-    region_model2 model1;
-
-    ADD_SAT_CONSTRAINT (model1, x, GE_EXPR, int_0);
-    ADD_SAT_CONSTRAINT (model1, x, LE_EXPR, int_42);
-    ASSERT_NE (model0, model1);
-
-    ASSERT_CONDITION_TRUE (model1, x, GE_EXPR, int_0);
-    ASSERT_CONDITION_TRUE (model1, x, LE_EXPR, int_42);
-
-    purge_stats stats;
-    model1.get_constraints ()->purge (purge_one_svalue2 *(model1, x), &stats);
-
-    ASSERT_CONDITION_UNKNOWN (model1, x, GE_EXPR, int_0);
-    ASSERT_CONDITION_UNKNOWN (model1, x, LE_EXPR, int_42);
-  }
-
-  {
-    region_model2 model0;
-    region_model2 model1;
-
-    ADD_SAT_CONSTRAINT (model1, x, NE_EXPR, int_42);
-    ADD_SAT_CONSTRAINT (model1, y, NE_EXPR, int_0);
-    ASSERT_NE (model0, model1);
-    ASSERT_CONDITION_TRUE (model1, x, NE_EXPR, int_42);
-    ASSERT_CONDITION_TRUE (model1, y, NE_EXPR, int_0);
-
-    purge_stats stats;
-    model1.get_constraints ()->purge (purge_one_svalue2 *(model1, x), &stats);
-    ASSERT_NE (model0, model1);
-
-    ASSERT_CONDITION_UNKNOWN (model1, x, NE_EXPR, int_42);
-    ASSERT_CONDITION_TRUE (model1, y, NE_EXPR, int_0);
-  }
-
-  {
-    region_model2 model0;
-    region_model2 model1;
-
-    ADD_SAT_CONSTRAINT (model1, x, NE_EXPR, int_42);
-    ADD_SAT_CONSTRAINT (model1, y, NE_EXPR, int_0);
-    ASSERT_NE (model0, model1);
-    ASSERT_CONDITION_TRUE (model1, x, NE_EXPR, int_42);
-    ASSERT_CONDITION_TRUE (model1, y, NE_EXPR, int_0);
-
-    purge_stats stats;
-    model1.get_constraints ()->purge (purge_all_svalue2_ids (), &stats);
-    ASSERT_CONDITION_UNKNOWN (model1, x, NE_EXPR, int_42);
-    ASSERT_CONDITION_UNKNOWN (model1, y, NE_EXPR, int_0);
-  }
-
-}
-
-/* Test that region_model2::purge_unused_svalue2s works as expected.  */
-
-static void
-test_purge_unused_svalue2s ()
-{
-  tree int_42 = build_int_cst (integer_type_node, 42);
-  tree int_0 = build_int_cst (integer_type_node, 0);
-  tree x = build_global_decl ("x", integer_type_node);
-  tree y = build_global_decl ("y", integer_type_node);
-
-  test_region_model2_context ctxt;
-  region_model2 model;
-  model.set_to_new_unknown_value (model.get_lvalue (x, &ctxt), TREE_TYPE (x),
-				  &ctxt);
-  model.set_to_new_unknown_value (model.get_lvalue (x, &ctxt), TREE_TYPE (x),
-				  &ctxt);
-  model.set_to_new_unknown_value (model.get_lvalue (x, &ctxt), TREE_TYPE (x),
-				  &ctxt);
-  model.add_constraint (x, NE_EXPR, int_42, &ctxt);
-
-  model.set_value (model.get_lvalue (x, &ctxt),
-		   model.get_rvalue (int_42, &ctxt),
-		   &ctxt);
-  model.add_constraint (y, GT_EXPR, int_0, &ctxt);
-
-  /* The redundant unknown values should have been purged.  */
-  purge_stats purged;
-  model.purge_unused_svalue2s (&purged, NULL);
-  ASSERT_EQ (purged.m_num_svalue2s, 3);
-
-  /* and the redundant constraint on an old, unknown value for x should
-     have been purged.  */
-  ASSERT_EQ (purged.m_num_equiv_classes, 1);
-  ASSERT_EQ (purged.m_num_constraints, 1);
-  ASSERT_EQ (model.get_constraints ()->m_constraints.length (), 2);
-
-  /* ...but we should still have x == 42.  */
-  ASSERT_EQ (model.eval_condition (x, EQ_EXPR, int_42, &ctxt),
-	     tristate::TS_TRUE);
-
-  /* ...and we should still have the constraint on y.  */
-  ASSERT_EQ (model.eval_condition (y, GT_EXPR, int_0, &ctxt),
-	     tristate::TS_TRUE);
-
-  ASSERT_EQ (ctxt.get_num_diagnostics (), 0);
-}
-#endif
-
-/* Verify that simple assignments work as expected.  */
-
-static void
-test_assignment ()
-{
-  tree int_0 = build_int_cst (integer_type_node, 0);
-  tree x = build_global_decl ("x", integer_type_node);
-  tree y = build_global_decl ("y", integer_type_node);
-
-  /* "x == 0", then use of y, then "y = 0;".  */
-  region_model2_manager mgr;
-  region_model2 model (&mgr);
-  ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, int_0);
-  ASSERT_CONDITION_UNKNOWN2 (model, y, EQ_EXPR, int_0);
-  model.set_value (model.get_lvalue (y, NULL),
-		   model.get_rvalue (int_0, NULL),
-		   NULL);
-  ASSERT_CONDITION_TRUE2 (model, y, EQ_EXPR, int_0);
-  ASSERT_CONDITION_TRUE2 (model, y, EQ_EXPR, x);
-
-#if 0
-  ASSERT_DUMP_EQ (model, true, "y: 0, {x}: unknown, x == y");
-#endif
-}
-
-/* Verify that compound assignments work as expected.  */
-
-static void
-test_compound_assignment ()
-{
-  coord_test2 ct;
-
-  tree c = build_global_decl ("c", ct.m_coord_type);
-  tree c_x = build3 (COMPONENT_REF, TREE_TYPE (ct.m_x_field),
-		     c, ct.m_x_field, NULL_TREE);
-  tree c_y = build3 (COMPONENT_REF, TREE_TYPE (ct.m_y_field),
-		     c, ct.m_y_field, NULL_TREE);
-  tree d = build_global_decl ("d", ct.m_coord_type);
-  tree d_x = build3 (COMPONENT_REF, TREE_TYPE (ct.m_x_field),
-		     d, ct.m_x_field, NULL_TREE);
-  tree d_y = build3 (COMPONENT_REF, TREE_TYPE (ct.m_y_field),
-		     d, ct.m_y_field, NULL_TREE);
-
-  tree int_17 = build_int_cst (integer_type_node, 17);
-  tree int_m3 = build_int_cst (integer_type_node, -3);
-
-  region_model2_manager mgr;
-  region_model2 model (&mgr);
-  model.set_value (c_x, int_17, NULL);
-  model.set_value (c_y, int_m3, NULL);
-
-#if 0
-  ASSERT_DUMP_EQ (model, true, "c.x: 17, c.y: -3");
-#endif
-
-  /* Copy c to d.  */
-  model.copy_region (model.get_lvalue (d, NULL), model.get_lvalue (c, NULL),
-		     NULL);
-  /* Check that the fields have the same svalue2s.  */
-  ASSERT_EQ (model.get_rvalue (c_x, NULL), model.get_rvalue (d_x, NULL));
-  ASSERT_EQ (model.get_rvalue (c_y, NULL), model.get_rvalue (d_y, NULL));
-}
-
-/* Verify the details of pushing and popping stack frames.  */
-
-static void
-test_stack_frames ()
-{
-  tree int_42 = build_int_cst (integer_type_node, 42);
-  tree int_10 = build_int_cst (integer_type_node, 10);
-  tree int_5 = build_int_cst (integer_type_node, 5);
-  tree int_0 = build_int_cst (integer_type_node, 0);
-
-  auto_vec <tree> param_types;
-  tree parent_fndecl = make_fndecl (integer_type_node,
-				    "parent_fn",
-				    param_types);
-  allocate_struct_function (parent_fndecl, true);
-
-  tree child_fndecl = make_fndecl (integer_type_node,
-				   "child_fn",
-				   param_types);
-  allocate_struct_function (child_fndecl, true);
-
-  /* "a" and "b" in the parent frame.  */
-  tree a = build_decl (UNKNOWN_LOCATION, PARM_DECL,
-		       get_identifier ("a"),
-		       integer_type_node);
-  tree b = build_decl (UNKNOWN_LOCATION, PARM_DECL,
-		       get_identifier ("b"),
-		       integer_type_node);
-  /* "x" and "y" in a child frame.  */
-  tree x = build_decl (UNKNOWN_LOCATION, PARM_DECL,
-		       get_identifier ("x"),
-		       integer_type_node);
-  tree y = build_decl (UNKNOWN_LOCATION, PARM_DECL,
-		       get_identifier ("y"),
-		       integer_type_node);
-
-  /* "p" global.  */
-  tree p = build_global_decl ("p", ptr_type_node);
-
-  /* "q" global.  */
-  tree q = build_global_decl ("q", ptr_type_node);
-
-  region_model2_manager mgr;
-  test_region_model2_context ctxt;
-  region_model2 model (&mgr);
-
-  /* Push stack frame for "parent_fn".  */
-  const region2 *parent_frame_reg
-    = model.push_frame (DECL_STRUCT_FUNCTION (parent_fndecl),
-			NULL, &ctxt);
-  ASSERT_EQ (model.get_current_frame (), parent_frame_reg);
-  ASSERT_TRUE (model.region_exists_p (parent_frame_reg));
-  const region2 *a_in_parent_reg = model.get_lvalue (a, &ctxt);
-  model.set_value (a_in_parent_reg,
-		   model.get_rvalue (int_42, &ctxt),
-		   &ctxt);
-  ASSERT_EQ (a_in_parent_reg->maybe_get_frame_region (), parent_frame_reg);
-
-#if 0
-  model.set_to_new_unknown_value (model.get_lvalue (b, &ctxt),
-				  integer_type_node, &ctxt);
-#endif
-  model.add_constraint (b, LT_EXPR, int_10, &ctxt);
-  ASSERT_EQ (model.eval_condition (b, LT_EXPR, int_10, &ctxt),
-	     tristate (tristate::TS_TRUE));
-
-  /* Push stack frame for "child_fn".  */
-  const region2 *child_frame_reg
-    = model.push_frame (DECL_STRUCT_FUNCTION (child_fndecl), NULL, &ctxt);
-  ASSERT_EQ (model.get_current_frame (), child_frame_reg);
-  ASSERT_TRUE (model.region_exists_p (child_frame_reg));
-  const region2 *x_in_child_reg = model.get_lvalue (x, &ctxt);
-  model.set_value (x_in_child_reg,
-		   model.get_rvalue (int_0, &ctxt),
-		   &ctxt);
-  ASSERT_EQ (x_in_child_reg->maybe_get_frame_region (), child_frame_reg);
-#if 0
-  model.set_to_new_unknown_value (model.get_lvalue (y, &ctxt),
-				  integer_type_node, &ctxt);
-#endif
-  model.add_constraint (y, NE_EXPR, int_5, &ctxt);
-  ASSERT_EQ (model.eval_condition (y, NE_EXPR, int_5, &ctxt),
-	     tristate (tristate::TS_TRUE));
-
-  /* Point a global pointer at a local in the child frame:  p = &x.  */
-  const region2 *p_in_globals_reg = model.get_lvalue (p, &ctxt);
-  model.set_value (p_in_globals_reg,
-		   mgr.get_ptr_svalue2 (ptr_type_node, x_in_child_reg),
-		   &ctxt);
-  ASSERT_EQ (p_in_globals_reg->maybe_get_frame_region (), NULL);
-
-  /* Point another global pointer at p: q = &p.  */
-  const region2 *q_in_globals_reg = model.get_lvalue (q, &ctxt);
-  model.set_value (q_in_globals_reg,
-		   mgr.get_ptr_svalue2 (ptr_type_node, p_in_globals_reg),
-		   &ctxt);
-
-  /* Test region2::descendent_of_p.  */
-  ASSERT_TRUE (child_frame_reg->descendent_of_p (child_frame_reg));
-  ASSERT_TRUE (x_in_child_reg->descendent_of_p (child_frame_reg));
-  ASSERT_FALSE (a_in_parent_reg->descendent_of_p (child_frame_reg));
-#if 0
-  auto_vec<region2_id> test_vec;
-  for (region2_id_set::iterator_t iter = descendents.begin ();
-       iter != descendents.end ();
-       ++iter)
-    test_vec.safe_push (*iter);
-  gcc_unreachable (); // TODO
-  //ASSERT_EQ ();
-#endif
-
-#if 0
-  ASSERT_DUMP_EQ (model, true,
-		  "a: 42, x: 0, p: &x, q: &p, {b, y}: unknown, b < 10, y != 5");
-#endif
-
-  /* Pop the "child_fn" frame from the stack.  */
-  purge_stats2 purged;
-  model.pop_frame (NULL, true, &purged, NULL, &ctxt);
-  ASSERT_FALSE (model.region_exists_p (child_frame_reg));
-  ASSERT_TRUE (model.region_exists_p (parent_frame_reg));
-
-#if 0
-  /* We should have purged the unknown values for x and y. */
-  ASSERT_EQ (purged.m_num_svalue2s, 2);
-
-  /* We should have purged the frame region2 and the region2s for x and y. */
-  ASSERT_EQ (purged.m_num_region2s, 3);
-
-  /* We should have purged the constraint on y.  */
-  ASSERT_EQ (purged.m_num_equiv_classes, 1);
-  ASSERT_EQ (purged.m_num_constraints, 1);
-#endif
-
-  /* Verify that p (which was pointing at the local "x" in the popped
-     frame) has been poisoned.  */
-  const svalue2 *new_p_sval = model.get_rvalue (p, &ctxt);
-  ASSERT_EQ (new_p_sval->get_kind (), svalue2::SK_POISONED);
-  ASSERT_EQ (new_p_sval->dyn_cast_poisoned_svalue2 ()->get_poison_kind (),
-	     POISON_KIND_POPPED_STACK);
-
-  /* Verify that q still points to p, in spite of the region2
-     renumbering.  */
-  const svalue2 *new_q_sval = model.get_rvalue (q, &ctxt);
-  ASSERT_EQ (new_q_sval->get_kind (), svalue2::SK_REGION);
-  ASSERT_EQ (new_q_sval->dyn_cast_region_svalue2 ()->get_pointee (),
-	     model.get_lvalue (p, &ctxt));
-
-  /* Verify that top of stack has been updated.  */
-  ASSERT_EQ (model.get_current_frame (), parent_frame_reg);
-
-  /* Verify locals in parent frame.  */
-  /* Verify "a" still has its value.  */
-  const svalue2 *new_a_sval = model.get_rvalue (a, &ctxt);
-  ASSERT_EQ (new_a_sval->get_kind (), svalue2::SK_CONSTANT);
-  ASSERT_EQ (new_a_sval->dyn_cast_constant_svalue2 ()->get_constant (),
-	     int_42);
-  /* Verify "b" still has its constraint.  */
-  ASSERT_EQ (model.eval_condition (b, LT_EXPR, int_10, &ctxt),
-	     tristate (tristate::TS_TRUE));
-}
-
-/* Verify that get_representative_path_var works as expected, that
-   we can map from region2s to parms and back within a recursive call
-   stack.  */
-
-static void
-test_get_representative_path_var ()
-{
-  auto_vec <tree> param_types;
-  tree fndecl = make_fndecl (integer_type_node,
-			     "factorial",
-			     param_types);
-  allocate_struct_function (fndecl, true);
-
-  /* Parm "n".  */
-  tree n = build_decl (UNKNOWN_LOCATION, PARM_DECL,
-		       get_identifier ("n"),
-		       integer_type_node);
-
-  region_model2_manager mgr;
-  test_region_model2_context ctxt;
-  region_model2 model (&mgr);
-
-  /* Push 5 stack frames for "factorial", each with a param  */
-  auto_vec<const region2 *> parm_regs;
-  auto_vec<const svalue2 *> parm_svals;
-  for (int depth = 0; depth < 5; depth++)
-    {
-      const region2 *frame_n_reg
-	= model.push_frame (DECL_STRUCT_FUNCTION (fndecl), NULL, &ctxt);
-      const region2 *parm_n_reg = model.get_lvalue (path_var (n, depth), &ctxt);
-      parm_regs.safe_push (parm_n_reg);
-
-      ASSERT_EQ (parm_n_reg->get_parent_region (), frame_n_reg);
-      const svalue2 *sval_n = mgr.get_or_create_initial_value (parm_n_reg);
-      parm_svals.safe_push (sval_n);
-    }
-
-  /* Verify that we can recognize that the region2s are the parms,
-     at every depth.  */
-  for (int depth = 0; depth < 5; depth++)
-    {
-      {
-	hash_set <const svalue2 *> visited;
-	ASSERT_EQ (model.get_representative_path_var (parm_regs[depth],
-						      &visited),
-		   path_var (n, depth + 1));
-      }
-      /* ...and that we can lookup lvalues for locals for all frames,
-	 not just the top.  */
-      ASSERT_EQ (model.get_lvalue (path_var (n, depth), NULL),
-		 parm_regs[depth]);
-      /* ...and that we can locate the svalue2s.  */
-      {
-	hash_set <const svalue2 *> visited;
-	ASSERT_EQ (model.get_representative_path_var (parm_svals[depth],
-						      &visited),
-		   path_var (n, depth + 1));
-      }
-    }
-}
-
-/* Verify that "empty" region_model2 instances are equal to each other.  */
-
-static void
-test_model_equality_1 ()
-{
-  tree int_42 = build_int_cst (integer_type_node, 42);
-  tree int_17 = build_int_cst (integer_type_node, 17);
-
-  region_model2_manager mgr;
-  region_model2 model0 (&mgr);
-  region_model2 model1 (&mgr);
-  ASSERT_EQ (model0, model1);
-
-  /* Verify that setting state in model1 makes the models non-equal.  */
-  tree x = build_global_decl ("x", integer_type_node);
-  model0.set_value (x, int_42, NULL);
-  ASSERT_EQ (model0.get_rvalue (x, NULL)->maybe_get_constant (), int_42);
-  ASSERT_NE (model0, model1);
-
-  /* Verify the copy-ctor.  */
-  region_model2 model2 (model0);
-  ASSERT_EQ (model0, model2);
-  ASSERT_EQ (model2.get_rvalue (x, NULL)->maybe_get_constant (), int_42);
-  ASSERT_NE (model1, model2);
-
-  /* Verify that models obtained from copy-ctor are independently editable
-     w/o affecting the original model.  */
-  model2.set_value (x, int_17, NULL);
-  ASSERT_NE (model0, model2);
-  ASSERT_EQ (model2.get_rvalue (x, NULL)->maybe_get_constant (), int_17);
-  ASSERT_EQ (model0.get_rvalue (x, NULL)->maybe_get_constant (), int_42);
-}
-
-/* Verify that region2 models for
-      x = 42; y = 113;
-   and
-      y = 113; x = 42;
-   are equal.  */
-
-static void
-test_model_equality_2 ()
-{
-  tree int_42 = build_int_cst (integer_type_node, 42);
-  tree int_113 = build_int_cst (integer_type_node, 113);
-  tree x = build_global_decl ("x", integer_type_node);
-  tree y = build_global_decl ("y", integer_type_node);
-
-  region_model2_manager mgr;
-  region_model2 model0 (&mgr);
-  model0.set_value (model0.get_lvalue (x, NULL),
-		    model0.get_rvalue (int_42, NULL),
-		    NULL);
-  model0.set_value (model0.get_lvalue (y, NULL),
-		    model0.get_rvalue (int_113, NULL),
-		    NULL);
-
-  region_model2 model1 (&mgr);
-  model1.set_value (model1.get_lvalue (y, NULL),
-		    model1.get_rvalue (int_113, NULL),
-		    NULL);
-  model1.set_value (model1.get_lvalue (x, NULL),
-		    model1.get_rvalue (int_42, NULL),
-		    NULL);
-
-  ASSERT_EQ (model0, model1);
-}
-
-/* Verify that constraints for
-     x > 3 && y > 42
-   and
-     y > 42 && x > 3
-   are equal (after canonicalization).  */
-
-static void
-test_model_equality_3 ()
-{
-  tree int_3 = build_int_cst (integer_type_node, 3);
-  tree int_42 = build_int_cst (integer_type_node, 42);
-  tree x = build_global_decl ("x", integer_type_node);
-  tree y = build_global_decl ("y", integer_type_node);
-
-  region_model2_manager mgr;
-  region_model2 model0 (&mgr);
-  model0.add_constraint (x, GT_EXPR, int_3, NULL);
-  model0.add_constraint (y, GT_EXPR, int_42, NULL);
-
-  region_model2 model1 (&mgr);
-  model1.add_constraint (y, GT_EXPR, int_42, NULL);
-  model1.add_constraint (x, GT_EXPR, int_3, NULL);
-
-  model0.canonicalize ();
-  model1.canonicalize ();
-  ASSERT_EQ (model0, model1);
-}
-
-#if 0
-/* Verify that we can canonicalize a model containing NaN and other real
-   constants.  */
-
-static void
-test_model_equality_4 ()
-{
-  auto_vec<tree> csts;
-  append_interesting_constants (&csts);
-
-  region_model2 model;
-
-  unsigned i;
-  tree cst;
-  FOR_EACH_VEC_ELT (csts, i, cst)
-    model.get_rvalue (cst, NULL);
-
-  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
-   mergable.  Write the merged model to *OUT_MERGED_MODEL,
-   and the merged svalue2 ptr to *OUT_MERGED_SVALUE2.
-   If VAL_A or VAL_B are NULL_TREE, don't populate EXPR
-   for that region_model2.  */
-
-static void
-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);
-  region_model2 model1 (mgr);
-  if (val_a)
-    model0.set_value (model0.get_lvalue (expr, &ctxt),
-		      model0.get_rvalue (val_a, &ctxt),
-		      &ctxt);
-  if (val_b)
-    model1.set_value (model1.get_lvalue (expr, &ctxt),
-		      model1.get_rvalue (val_b, &ctxt),
-		      &ctxt);
-
-  /* They should be mergeable.  */
-  ASSERT_TRUE (model0.can_merge_with_p (model1, point, out_merged_model));
-  *out_merged_svalue2 = out_merged_model->get_rvalue (expr, &ctxt);
-}
-
-/* Verify that we can merge region_model2 instances.  */
-
-static void
-test_state_merging ()
-{
-  tree int_42 = build_int_cst (integer_type_node, 42);
-  tree int_113 = build_int_cst (integer_type_node, 113);
-  tree x = build_global_decl ("x", integer_type_node);
-  tree y = build_global_decl ("y", integer_type_node);
-  tree z = build_global_decl ("z", integer_type_node);
-  tree p = build_global_decl ("p", ptr_type_node);
-
-  tree addr_of_y = build1 (ADDR_EXPR, ptr_type_node, y);
-  tree addr_of_z = build1 (ADDR_EXPR, ptr_type_node, z);
-
-  auto_vec <tree> param_types;
-  tree test_fndecl = make_fndecl (integer_type_node, "test_fn", param_types);
-  allocate_struct_function (test_fndecl, true);
-
-  /* Param "a".  */
-  tree a = build_decl (UNKNOWN_LOCATION, PARM_DECL,
-		       get_identifier ("a"),
-		       integer_type_node);
-  tree addr_of_a = build1 (ADDR_EXPR, ptr_type_node, a);
-
-  /* Param "q", a pointer.  */
-  tree q = build_decl (UNKNOWN_LOCATION, PARM_DECL,
-		       get_identifier ("q"),
-		       ptr_type_node);
-
-  program_point point (program_point::origin ());
-  region_model2_manager mgr;
-
-  {
-    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, point, &merged));
-    ASSERT_EQ (model0, merged);
-  }
-
-  /* Verify that we can merge two contradictory constraints on the
-     value for a global.  */
-  /* TODO: verify that the merged model doesn't have a value for
-     the global  */
-  {
-    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);
-    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &merged));
-    ASSERT_NE (model0, merged);
-    ASSERT_NE (model1, merged);
-  }
-
-  /* Verify handling of a PARM_DECL.  */
-  {
-    test_region_model2_context ctxt;
-    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);
-
-    placeholder_svalue2 test_sval (integer_type_node, "test sval");
-    model0.set_value (model0.get_lvalue (a, &ctxt), &test_sval, &ctxt);
-    model1.set_value (model1.get_lvalue (a, &ctxt), &test_sval, &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 (&mgr);
-    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);
-  }
-
-  // FIXME: uses "set_to_new_unknown_value"
-  /* Verify handling of a global.  */
-  {
-    test_region_model2_context ctxt;
-    region_model2 model0 (&mgr);
-    region_model2 model1 (&mgr);
-
-    placeholder_svalue2 test_sval (integer_type_node, "test sval");
-    model0.set_value (model0.get_lvalue (x, &ctxt), &test_sval, &ctxt);
-    model1.set_value (model1.get_lvalue (x, &ctxt), &test_sval, &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 (&mgr);
-    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);
-  }
-
-  /* Use global-handling to verify various combinations of values.  */
-
-  /* Two equal constant values.  */
-  {
-    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 (), svalue2::SK_CONSTANT);
-    ASSERT_EQ (merged_x_sval->dyn_cast_constant_svalue2 ()->get_constant (),
-	       int_42);
-  }
-
-  /* Two non-equal constant values.  */
-  {
-    region_model2 merged (&mgr);
-    const svalue2 *merged_x_sval;
-    assert_region_model2s_merge (x, int_42, int_113, &merged, &merged_x_sval);
-
-    /* In particular, there should be an unknown value for "x".  */
-    // FIXME
-#if WIDENING
-    ASSERT_EQ (merged_x_sval->get_kind (), svalue2::SK_WIDENING);
-#else
-    ASSERT_EQ (merged_x_sval->get_kind (), svalue2::SK_UNKNOWN);
-#endif
-  }
-
-  /* Uninit and constant.  */
-  {
-    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 (), svalue2::SK_UNKNOWN);
-#endif
-  }
-
-  /* Constant and uninit.  */
-  {
-    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 (), svalue2::SK_UNKNOWN);
-#endif
-  }
-
-  /* Unknown and constant.  */
-  // TODO
-
-  /* Pointers: NULL and NULL.  */
-  // TODO
-
-  /* Pointers: NULL and non-NULL.  */
-  // TODO
-
-  /* Pointers: non-NULL and non-NULL: ptr to a local.  */
-  {
-    region_model2 model0 (&mgr);
-    model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL);
-    model0.set_value (model0.get_lvalue (p, NULL),
-		      model0.get_rvalue (addr_of_a, NULL), NULL);
-
-    region_model2 model1 (model0);
-    ASSERT_EQ (model0, model1);
-
-    /* They should be mergeable, and the result should be the same.  */
-    region_model2 merged (&mgr);
-    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &merged));
-    ASSERT_EQ (model0, merged);
-  }
-
-  /* Pointers: non-NULL and non-NULL: ptr to a global.  */
-  {
-    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 (), 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 (&mgr);
-    /* x == &y vs x == &z in the input models; these are actually casts
-       of the ptrs to "int".  */
-    const svalue2 *merged_x_sval;
-    // TODO:
-    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 (), svalue2::SK_UNKNOWN);
-  }
-
-  /* Pointers: non-NULL and non-NULL: ptr to a heap region2.  */
-  {
-    test_region_model2_context ctxt;
-    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);
-
-    region_model2 model1 (model0);
-
-    ASSERT_EQ (model0, model1);
-
-    region_model2 merged (&mgr);
-    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &merged));
-
-    /* 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 (&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), sval, &ctxt);
-    region_model2 model1 (model0);
-
-    /* They should be mergeable, and the result should be the same.  */
-    region_model2 merged (&mgr);
-    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &merged));
-    ASSERT_EQ (model0, merged);
-
-    /* In particular, we should have x == y.  */
-    ASSERT_EQ (merged.eval_condition (x, EQ_EXPR, y, &ctxt),
-	       tristate (tristate::TS_TRUE));
-  }
-#endif
-
-  {
-    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);
-    region_model2 merged (&mgr);
-    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &merged));
-  }
-
-  {
-    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);
-    model1.add_constraint (x, EQ_EXPR, int_113, &ctxt);
-    region_model2 merged (&mgr);
-    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &merged));
-  }
-
-  // TODO: what can't we merge? need at least one such test
-
-  /* TODO: various things
-     - heap region2s
-     - value merging:
-       - every combination, but in particular
-          - pairs of region2s
-   */
-
-  /* Views.  */
-  {
-#if 0
-    test_region_model2_context ctxt;
-    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,
-							 &ctxt);
-    model0.set_value (x_as_ptr, model0.get_rvalue (addr_of_y, &ctxt), &ctxt);
-
-    region_model2 model1 (model0);
-    ASSERT_EQ (model1, model0);
-
-    /* They should be mergeable, and the result should be the same.  */
-    region_model2 merged (&mgr);
-    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &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.  */
-  {
-#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);
-
-    /* Push a second frame.  */
-    const region2 *reg_2nd_frame
-      = model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL);
-
-    /* Have a pointer in the older frame point to a local in the
-       more recent frame.  */
-    const svalue2 *sval_ptr = model0.get_rvalue (addr_of_a, NULL);
-    model0.set_value (q_in_first_frame, sid_ptr, NULL);
-
-    /* Verify that it's pointing at the newer frame.  */
-    const region2 *reg_pointee
-      = model0.get_svalue2 (sid_ptr)->dyn_cast_region_svalue2 ()->get_pointee ();
-    ASSERT_EQ (model0.get_region2 (rid_pointee)->get_parent (), rid_2nd_frame);
-
-    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 (&mgr);
-    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &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 (&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);
-
-    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 (&mgr);
-    ASSERT_TRUE (model0.can_merge_with_p (model1, point, &merged));
-    ASSERT_EQ (model0, merged);
-  }
-}
-
-/* Verify that constraints are correctly merged when merging region_model2
-   instances.  */
-
-static void
-test_constraint_merging ()
-{
-  tree int_0 = build_int_cst (integer_type_node, 0);
-  tree int_5 = build_int_cst (integer_type_node, 5);
-  tree x = build_global_decl ("x", integer_type_node);
-  tree y = build_global_decl ("y", integer_type_node);
-  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 (&mgr);
-  model0.add_constraint (x, EQ_EXPR, y, &ctxt);
-  model0.add_constraint (x, GE_EXPR, int_0, NULL);
-  model0.add_constraint (x, LT_EXPR, n, NULL);
-
-  /* model1: z != 5 && (0 <= x < n).  */
-  region_model2 model1 (&mgr);
-  model1.add_constraint (z, NE_EXPR, int_5, NULL);
-  model1.add_constraint (x, GE_EXPR, int_0, NULL);
-  model1.add_constraint (x, LT_EXPR, n, NULL);
-
-  /* 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, point, &merged));
-
-  ASSERT_EQ (merged.eval_condition (x, GE_EXPR, int_0, &ctxt),
-	     tristate (tristate::TS_TRUE));
-  ASSERT_EQ (merged.eval_condition (x, LT_EXPR, n, &ctxt),
-	     tristate (tristate::TS_TRUE));
-
-  ASSERT_EQ (merged.eval_condition (z, NE_EXPR, int_5, &ctxt),
-	     tristate (tristate::TS_UNKNOWN));
-  ASSERT_EQ (merged.eval_condition (x, LT_EXPR, y, &ctxt),
-	     tristate (tristate::TS_UNKNOWN));
-}
-
-/* FIXME.  */
-
-static void
-test_widening_constraints ()
-{
-  program_point point (program_point::origin ());
-  tree int_0 = build_int_cst (integer_type_node, 0);
-  tree int_m1 = build_int_cst (integer_type_node, -1);
-  tree int_1 = build_int_cst (integer_type_node, 1);
-  tree int_256 = build_int_cst (integer_type_node, 256);
-  region_model2_manager mgr;
-  test_region_model2_context ctxt;
-  const svalue2 *int_0_sval = mgr.get_or_create_constant_svalue2 (int_0);
-  const svalue2 *int_1_sval = mgr.get_or_create_constant_svalue2 (int_1);
-  const svalue2 *w_zero_then_one_sval
-    = mgr.get_or_create_widening_svalue2 (integer_type_node, point,
-					  int_0_sval, int_1_sval);
-  const widening_svalue2 *w_zero_then_one
-    = w_zero_then_one_sval->dyn_cast_widening_svalue2 ();
-  ASSERT_EQ (w_zero_then_one->get_direction (),
-	     widening_svalue2::DIR_ASCENDING);
-  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (LT_EXPR, int_m1),
-	     tristate::TS_FALSE);
-  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (LT_EXPR, int_0),
-	     tristate::TS_FALSE);
-  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (LT_EXPR, int_1),
-	     tristate::TS_UNKNOWN);
-  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (LT_EXPR, int_256),
-	     tristate::TS_UNKNOWN);
-
-  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (LE_EXPR, int_m1),
-	     tristate::TS_FALSE);
-  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (LE_EXPR, int_0),
-	     tristate::TS_UNKNOWN);
-  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (LE_EXPR, int_1),
-	     tristate::TS_UNKNOWN);
-  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (LE_EXPR, int_256),
-	     tristate::TS_UNKNOWN);
-
-  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (GT_EXPR, int_m1),
-	     tristate::TS_TRUE);
-  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (GT_EXPR, int_0),
-	     tristate::TS_UNKNOWN);
-  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (GT_EXPR, int_1),
-	     tristate::TS_UNKNOWN);
-  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (GT_EXPR, int_256),
-	     tristate::TS_UNKNOWN);
-
-  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (GE_EXPR, int_m1),
-	     tristate::TS_TRUE);
-  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (GE_EXPR, int_0),
-	     tristate::TS_TRUE);
-  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (GE_EXPR, int_1),
-	     tristate::TS_UNKNOWN);
-  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (GE_EXPR, int_256),
-	     tristate::TS_UNKNOWN);
-
-  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (EQ_EXPR, int_m1),
-	     tristate::TS_FALSE);
-  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (EQ_EXPR, int_0),
-	     tristate::TS_UNKNOWN);
-  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (EQ_EXPR, int_1),
-	     tristate::TS_UNKNOWN);
-  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (EQ_EXPR, int_256),
-	     tristate::TS_UNKNOWN);
-
-  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (NE_EXPR, int_m1),
-	     tristate::TS_TRUE);
-  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (NE_EXPR, int_0),
-	     tristate::TS_UNKNOWN);
-  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (NE_EXPR, int_1),
-	     tristate::TS_UNKNOWN);
-  ASSERT_EQ (w_zero_then_one->eval_condition_without_cm (NE_EXPR, int_256),
-	     tristate::TS_UNKNOWN);
-
-  // TODO: a descending value
-}
-
-/* Verify merging constraints for states simulating successive iterations
-   of a loop.
-   Simulate:
-     for (i = 0; i < 256; i++)
-       [...body...]
-   i.e. this gimple:.
-     i_15 = 0;
-     goto <bb 4>;
-
-   <bb 4> :
-     i_11 = PHI <i_15(2), i_23(3)>
-     if (i_11 <= 255)
-       goto <bb 3>;
-     else
-       goto [AFTER LOOP]
-
-   <bb 3> :
-     [LOOP BODY]
-     i_23 = i_11 + 1;
-
-   and thus these ops (and resultant states):
-     i_11 = PHI()
-       {i_11: 0}
-     add_constraint (i_11 <= 255) [for the true edge]
-       {i_11: 0}  [constraint was a no-op]
-     i_23 = i_11 + 1;
-       {i_22: 1}
-     i_11 = PHI()
-       {i_11: WIDENED (at phi, 0, 1)}
-     add_constraint (i_11 <= 255) [for the true edge]
-       {i_11: WIDENED (at phi, 0, 1); WIDENED <= 255}
-     i_23 = i_11 + 1;
-       {i_23: (WIDENED (at phi, 0, 1) + 1); WIDENED <= 255}
-     i_11 = PHI(); merge with state at phi above
-       {i_11: WIDENED (at phi, 0, 1); WIDENED <= 256}
-         [changing meaning of "WIDENED" here]
-     if (i_11 <= 255)
-        T: {i_11: WIDENED (at phi, 0, 1); WIDENED <= 255}; cache hit
-        F: {i_11: 256}
-
-	FIXME.
-	TODO: rename?
- */
-
-static void
-test_iteration_1 ()
-{
-#if WIDENING
-  program_point point (program_point::origin ());
-
-  tree int_0 = build_int_cst (integer_type_node, 0);
-  tree int_1 = build_int_cst (integer_type_node, 1);
-  tree int_256 = build_int_cst (integer_type_node, 256);
-  tree int_257 = build_int_cst (integer_type_node, 257);
-  tree i = build_global_decl ("i", integer_type_node);
-
-  region_model2_manager mgr;
-  test_region_model2_context ctxt;
-
-  /* model0: i: 0.  */
-  region_model2 model0 (&mgr);
-  model0.set_value (i, int_0, &ctxt);
-
-  /* model1: i: 1.  */
-  region_model2 model1 (&mgr);
-  model1.set_value (i, int_1, &ctxt);
-
-  /* FIXME: should merge to a widened value.  */
-  region_model2 model2 (&mgr);
-  ASSERT_TRUE (model1.can_merge_with_p (model0, point, &model2));
-  const svalue2 *merged_i = model2.get_rvalue (i, &ctxt);
-  ASSERT_EQ (merged_i->get_kind (), svalue2::SK_WIDENING);
-  const widening_svalue2 *w = merged_i->dyn_cast_widening_svalue2 ();
-  ASSERT_EQ (w->get_direction (), widening_svalue2::DIR_ASCENDING);
-
-  /* Add constraint: i < 256  */
-  model2.add_constraint (i, LT_EXPR, int_256, &ctxt);
-  ASSERT_EQ (model2.eval_condition (i, LT_EXPR, int_256, &ctxt),
-	     tristate (tristate::TS_TRUE));
-  ASSERT_EQ (model2.eval_condition (i, GE_EXPR, int_0, &ctxt),
-	     tristate (tristate::TS_TRUE));
-
-  /* Try merging with the initial state.  */
-  region_model2 model3 (&mgr);
-  ASSERT_TRUE (model2.can_merge_with_p (model0, point, &model3));
-  /* Merging the merged value with the initial value should be idempotent,
-     so that the analysis converges.  */
-  ASSERT_EQ (model3.get_rvalue (i, &ctxt), merged_i);
-  /* Merger of 0 and a widening value with constraint < CST
-     should retain the constraint, even though it was implicit
-     for the 0 case.  */
-  ASSERT_EQ (model3.eval_condition (i, LT_EXPR, int_256, &ctxt),
-	     tristate (tristate::TS_TRUE));
-  /* ...and we should have equality: the analysis should have converged.  */
-  ASSERT_EQ (model3, model2);
-
-  /* "i_23 = i_11 + 1;"  */
-  region_model2 model4 (model3);
-  ASSERT_EQ (model4, model2);
-  model4.set_value (i, build2 (PLUS_EXPR, integer_type_node, i, int_1), &ctxt);
-  const svalue2 *plus_one = model4.get_rvalue (i, &ctxt);
-  /* TODO: what should this be?
-     Currently it's the widening(0, +ve) plus one.
-     Should it be a new widening(1, +ve), and apply a reworked version
-     of the constraints on the other widening value?  */
-  ASSERT_EQ (plus_one->get_kind (), svalue2::SK_BINOP);
-
-  /* Try merging with the "i: 1" state.  */
-  region_model2 model5 (&mgr);
-  ASSERT_TRUE (model4.can_merge_with_p (model1, point, &model5));
-  ASSERT_EQ (model5.get_rvalue (i, &ctxt), plus_one);
-  ASSERT_EQ (model5, model4);
-
-  /*  "i_11 = PHI();" merge with state at phi above.
-      For i, we should have a merger of WIDENING with WIDENING + 1,
-      and this should be WIDENING again.  */
-  region_model2 model6 (&mgr);
-  ASSERT_TRUE (model5.can_merge_with_p (model2, point, &model6));
-  const svalue2 *merged_widening = model6.get_rvalue (i, &ctxt);
-  ASSERT_EQ (merged_widening->get_kind (), svalue2::SK_WIDENING);
-  // FIXME: what constraints should we have here?
-
-  ASSERT_CONDITION_TRUE2 (model6, i, LT_EXPR, int_257);
-
-  /* This is a cache miss (we have i < 257 rather than i < 256).
-     However, on applying the loop-bound condition...
-     FIXME.  */
-
-  region_model2 model7_t (model6);
-  ADD_SAT_CONSTRAINT (model7_t, i, LT_EXPR, int_256);
-
-  region_model2 model7_f (model6);
-#if ITERATED_BINOPS
-  ADD_SAT_CONSTRAINT (model7_f, i, GE_EXPR, int_256);
-  ASSERT_CONDITION_TRUE2 (model7_f, i, EQ_EXPR, int_256);
-#endif /* ITERATED_BINOPS */
-
-  //ASSERT_EQ (model6, model3);
-
-  // TODO: constraints at end of loop (with i >= 256; should get i == 256)
-  /* TODO: unittests for widening_svalue2::eval_condition_without_cm
-     (separately).  */
-#endif
-}
-
-// TODO: as above, but with comparison against "n", rather than a constant
-
-static void
-test_iteration_2 ()
-{
-  // TODO
-}
-
-#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.  */
-
-static void
-test_malloc_constraints ()
-{
-  region_model2 model;
-  tree p = build_global_decl ("p", ptr_type_node);
-  tree char_star = build_pointer_type (char_type_node);
-  tree q = build_global_decl ("q", char_star);
-  tree null_ptr = build_int_cst (ptr_type_node, 0);
-
-  const region2 *reg = model.add_new_malloc_region2 ();
-  const svalue2 *sval = mgr.get_ptr_svalue2 (ptr_type_node, rid);
-  model.set_value (model.get_lvalue (p, NULL), sid, NULL);
-  model.set_value (q, p, NULL);
-
-  /* We should have a symbolic_region2 with m_possibly_null: true.  */
-  const region2 *pointee = model.get_region2 (rid);
-  const symbolic_region2 *sym_reg = pointee->dyn_cast_symbolic_region2 ();
-  ASSERT_NE (sym_reg, NULL);
-  ASSERT_TRUE (sym_reg->m_possibly_null);
-
-  ASSERT_CONDITION_UNKNOWN (model, p, NE_EXPR, null_ptr);
-  ASSERT_CONDITION_UNKNOWN (model, p, EQ_EXPR, null_ptr);
-  ASSERT_CONDITION_UNKNOWN (model, q, NE_EXPR, null_ptr);
-  ASSERT_CONDITION_UNKNOWN (model, q, EQ_EXPR, null_ptr);
-
-  model.add_constraint (p, NE_EXPR, null_ptr, NULL);
-
-  /* Adding the constraint should have cleared m_possibly_null.  */
-  ASSERT_FALSE (sym_reg->m_possibly_null);
-
-  ASSERT_CONDITION_TRUE (model, p, NE_EXPR, null_ptr);
-  ASSERT_CONDITION_FALSE (model, p, EQ_EXPR, null_ptr);
-  ASSERT_CONDITION_TRUE (model, q, NE_EXPR, null_ptr);
-  ASSERT_CONDITION_FALSE (model, q, EQ_EXPR, null_ptr);
-}
-#endif
-
-static void
-test_var ()
-{
-  /* "int i;"  */
-  tree i = build_global_decl ("i", integer_type_node);
-
-  tree int_17 = build_int_cst (integer_type_node, 17);
-  tree int_m3 = build_int_cst (integer_type_node, -3);
-
-  region_model2_manager mgr;
-  region_model2 model (&mgr);
-
-  const region2 *i_reg = model.get_lvalue (i, NULL);
-  ASSERT_EQ (i_reg->get_kind (), region2::RK_DECL);
-
-  /* Reading "i" should give a symbolic "initial value".  */
-  const svalue2 *sval_init = model.get_rvalue (i, NULL);
-  ASSERT_EQ (sval_init->get_kind (), svalue2::SK_INITIAL);
-  ASSERT_EQ (sval_init->dyn_cast_initial_svalue2 ()->get_region (), i_reg);
-  /* ..and doing it again should give the same "initial value".  */
-  ASSERT_EQ (model.get_rvalue (i, NULL), sval_init);
-
-  /* "i = 17;".  */
-  model.set_value (i, int_17, NULL);
-  ASSERT_EQ (model.get_rvalue (i, NULL),
-	     model.get_rvalue (int_17, NULL));
-
-  /* "i = -3;".  */
-  model.set_value (i, int_m3, NULL);
-  ASSERT_EQ (model.get_rvalue (i, NULL),
-	     model.get_rvalue (int_m3, NULL));
-
-  /* Verify get_offset for "i".  */
-  {
-    region_offset offset = i_reg->get_offset ();
-    ASSERT_EQ (offset.get_base_region (), i_reg);
-    ASSERT_EQ (offset.get_bit_offset (), 0);
-  }
-
-  /* Simplified dump.  */
-#if 0
-  ASSERT_DUMP_EQ (model, true,
-		  "base region: {c} has cluster: {"
-		  "binding key: {region: c.x, kind: direct, offset: 0},"
-		  " value: {17},"
-		  " binding key: {region: c.y, kind: direct, offset: 0},"
-		  " value: {-3}"
-		  "}");
-#endif
-
-}
-
-static void
-test_array ()
-{
-  /* "int arr[10];"  */
-  tree tlen = size_int (10);
-  tree arr_type
-    = build_array_type (integer_type_node, build_index_type (tlen));
-  tree arr = build_global_decl ("arr", arr_type);
-
-  /* "int i;"  */
-  tree i = build_global_decl ("i", integer_type_node);
-
-  tree int_0 = build_int_cst (integer_type_node, 0);
-  tree int_1 = build_int_cst (integer_type_node, 1);
-
-  tree arr_0 = build4 (ARRAY_REF, integer_type_node,
-		       arr, int_0, NULL_TREE, NULL_TREE);
-  tree arr_1 = build4 (ARRAY_REF, integer_type_node,
-		       arr, int_1, NULL_TREE, NULL_TREE);
-  tree arr_i = build4 (ARRAY_REF, integer_type_node,
-		       arr, i, NULL_TREE, NULL_TREE);
-
-  tree int_17 = build_int_cst (integer_type_node, 17);
-  tree int_42 = build_int_cst (integer_type_node, 42);
-  tree int_m3 = build_int_cst (integer_type_node, -3);
-
-  region_model2_manager mgr;
-  region_model2 model (&mgr);
-  /* "arr[0] = 17;".  */
-  model.set_value (arr_0, int_17, NULL);
-  /* "arr[1] = -3;".  */
-  model.set_value (arr_1, int_m3, NULL);
-
-  ASSERT_EQ (model.get_rvalue (arr_0, NULL), model.get_rvalue (int_17, NULL));
-  ASSERT_EQ (model.get_rvalue (arr_1, NULL), model.get_rvalue (int_m3, NULL));
-
-  /* Overwrite a pre-existing binding: "arr[1] = 42;".  */
-  model.set_value (arr_1, int_42, NULL);
-  ASSERT_EQ (model.get_rvalue (arr_1, NULL), model.get_rvalue (int_42, NULL));
-
-  /* Verify get_offset for "arr[0]".  */
-  {
-    const region2 *arr_0_reg = model.get_lvalue (arr_0, NULL);
-    region_offset offset = arr_0_reg->get_offset ();
-    ASSERT_EQ (offset.get_base_region (), model.get_lvalue (arr, NULL));
-    ASSERT_EQ (offset.get_bit_offset (), 0);
-  }
-
-  /* Verify get_offset for "arr[1]".  */
-  {
-    const region2 *arr_1_reg = model.get_lvalue (arr_1, NULL);
-    region_offset offset = arr_1_reg->get_offset ();
-    ASSERT_EQ (offset.get_base_region (), model.get_lvalue (arr, NULL));
-    ASSERT_EQ (offset.get_bit_offset (), INT_TYPE_SIZE);
-  }
-
-  /* "arr[i] = i;" - this should remove the earlier bindings.  */
-  model.set_value (arr_i, i, NULL);
-  ASSERT_EQ (model.get_rvalue (arr_i, NULL), model.get_rvalue (i, NULL));
-  ASSERT_EQ (model.get_rvalue (arr_0, NULL)->get_kind (), svalue2::SK_UNKNOWN);
-
-  /* "arr[0] = 17;" - this should remove the arr[i] binding.  */
-  model.set_value (arr_0, int_17, NULL);
-  ASSERT_EQ (model.get_rvalue (arr_0, NULL), model.get_rvalue (int_17, NULL));
-  ASSERT_EQ (model.get_rvalue (arr_i, NULL)->get_kind (), svalue2::SK_UNKNOWN);
-}
-
-/* FIXME.  */
-
-static void
-test_mem_ref ()
-{
-  /*
-    x = 17;
-    p = &x;
-    *p;
-   */
-  tree x = build_global_decl ("x", integer_type_node);
-  tree int_star = build_pointer_type (integer_type_node);
-  tree p = build_global_decl ("p", int_star);
-
-  tree int_17 = build_int_cst (integer_type_node, 17);
-  tree addr_of_x = build1 (ADDR_EXPR, int_star, x);
-  tree offset_0 = build_int_cst (integer_type_node, 0);
-  tree star_p = build2 (MEM_REF, integer_type_node, p, offset_0);
-
-  region_model2_manager mgr;
-  region_model2 model (&mgr);
-
-  /* "x = 17;".  */
-  model.set_value (x, int_17, NULL);
-
-  /* "p = &x;".  */
-  model.set_value (p, addr_of_x, NULL);
-
-  const svalue2 *sval = model.get_rvalue (star_p, NULL);
-  ASSERT_EQ (sval->maybe_get_constant (), int_17);
-}
-
-/* Test for a POINTER_PLUS_EXPR followed by a MEM_REF.
-   Analogous to this code:
-     void test_6 (int a[10])
-     {
-       __analyzer_eval (a[3] == 42); [should be UNKNOWN]
-       a[3] = 42;
-       __analyzer_eval (a[3] == 42); [should be TRUE]
-     }
-   from data-model-1.c, which looks like this at the gimple level:
-       # __analyzer_eval (a[3] == 42); [should be UNKNOWN]
-       int *_1 = a_10(D) + 12;   # POINTER_PLUS_EXPR
-       int _2 = *_1;             # MEM_REF
-       _Bool _3 = _2 == 42;
-       int _4 = (int) _3;
-       __analyzer_eval (_4);
-
-       # a[3] = 42;
-       int *_5 = a_10(D) + 12;   # POINTER_PLUS_EXPR
-       *_5 = 42;                 # MEM_REF
-
-       # __analyzer_eval (a[3] == 42); [should be TRUE]
-       int *_6 = a_10(D) + 12;   # POINTER_PLUS_EXPR
-       int _7 = *_6;             # MEM_REF
-       _Bool _8 = _7 == 42;
-       int _9 = (int) _8;
-       __analyzer_eval (_9);  */
-
-static void
-test_POINTER_PLUS_EXPR_then_MEM_REF ()
-{
-  tree int_star = build_pointer_type (integer_type_node);
-  tree a = build_global_decl ("a", int_star);
-  tree offset_12 = build_int_cst (size_type_node, 12);
-  tree pointer_plus_expr = build2 (POINTER_PLUS_EXPR, int_star, a, offset_12);
-  tree offset_0 = build_int_cst (integer_type_node, 0);
-  tree mem_ref = build2 (MEM_REF, integer_type_node,
-			 pointer_plus_expr, offset_0);
-  region_model2_manager mgr;
-  region_model2 m (&mgr);
-  const svalue2 *tmp_2 = m.get_rvalue (mem_ref, NULL);
-  const region2 *tmp_5 = m.get_lvalue (mem_ref, NULL);
-
-  tree int_42 = build_int_cst (integer_type_node, 42);
-  m.set_value (mem_ref, int_42, NULL);
-  ASSERT_EQ (m.get_rvalue (mem_ref, NULL)->maybe_get_constant (), int_42);
-  //ASSERT_EQ (m.get_store_value (tmp_5)->maybe_get_constant (), int_42);
-  // TODO: verify value of tmp_2 and tmp_5
-}
-
-/* FIXME.  */
-
-static void
-test_malloc ()
-{
-  tree int_star = build_pointer_type (integer_type_node);
-  tree p = build_global_decl ("p", int_star);
-  tree n = build_global_decl ("n", integer_type_node);
-  tree n_times_4 = build2 (MULT_EXPR, size_type_node,
-			   n, build_int_cst (size_type_node, 4));
-
-  region_model2_manager mgr;
-  test_region_model2_context ctxt;
-  region_model2 model (&mgr);
-
-  /* "p = malloc (n * 4);".  */
-  const svalue2 *size_sval = model.get_rvalue (n_times_4, &ctxt);
-  const region2 *reg = model.create_region_for_heap_alloc (size_sval);
-  const svalue2 *ptr = mgr.get_ptr_svalue2 (int_star, reg);
-  model.set_value (model.get_lvalue (p, &ctxt), ptr, &ctxt);
-  // TODO: verify dynamic extents
-
-}
-
-/* FIXME.  */
-
-static void
-test_alloca ()
-{
-  auto_vec <tree> param_types;
-  tree fndecl = make_fndecl (integer_type_node,
-			     "test_fn",
-			     param_types);
-  allocate_struct_function (fndecl, true);
-
-
-  tree int_star = build_pointer_type (integer_type_node);
-  tree p = build_global_decl ("p", int_star);
-  tree n = build_global_decl ("n", integer_type_node);
-  tree n_times_4 = build2 (MULT_EXPR, size_type_node,
-			   n, build_int_cst (size_type_node, 4));
-
-  region_model2_manager mgr;
-  test_region_model2_context ctxt;
-  region_model2 model (&mgr);
-
-  /* Push stack frame.  */
-  const region2 *frame_reg
-    = model.push_frame (DECL_STRUCT_FUNCTION (fndecl),
-			NULL, &ctxt);
-  /* "p = alloca (n * 4);".  */
-  const svalue2 *size_sval = model.get_rvalue (n_times_4, &ctxt);
-  const region2 *reg = model.create_region_for_alloca (size_sval);
-  const svalue2 *ptr = mgr.get_ptr_svalue2 (int_star, reg);
-  model.set_value (model.get_lvalue (p, &ctxt), ptr, &ctxt);
-  // TODO: verify dynamic extents
-
-  /* Verify that the pointers to the alloca region are replaced by
-     poisoned values when the frame is popped.  */
-  purge_stats2 purged;
-  model.pop_frame (NULL, true, &purged, NULL, &ctxt);
-  ASSERT_EQ (model.get_rvalue (p, &ctxt)->get_kind (), svalue2::SK_POISONED);
-}
-
-/* Run all of the selftests within this file.  */
-
-void
-analyzer_region_model2_cc_tests ()
-{
-  test_tree_cmp_on_constants ();
-  test_dump ();
-  test_dump_2 ();
-#if 0
-  test_dump_3 ();
-#endif
-  test_get_representative_tree ();
-  test_unique_constants ();
-  test_unique_unknowns ();
-#if 0
-  test_svalue2_equality ();
-  test_region2_equality ();
-#endif
-  test_initial_svalue2_folding ();
-  test_unaryop_svalue2_folding ();
-  test_binop_svalue2_folding ();
-  test_sub_svalue2_folding ();
-  test_descendent_of_p ();
-#if 0
-  test_purging_by_criteria ();
-  test_purge_unused_svalue2s ();
-#endif
-  test_assignment ();
-  test_compound_assignment ();
-  test_stack_frames ();
-  test_get_representative_path_var ();
-  test_model_equality_1 ();
-  test_model_equality_2 ();
-  test_model_equality_3 ();
-#if 0
-  test_model_equality_4 ();
-#endif
-  test_state_merging ();
-  test_constraint_merging ();
-  test_widening_constraints ();
-  test_iteration_1 ();
-  test_iteration_2 ();
-#if 0
-  test_malloc_constraints ();
-#endif
-  test_var ();
-  test_array ();
-  test_mem_ref ();
-  test_POINTER_PLUS_EXPR_then_MEM_REF ();
-  test_malloc ();
-  test_alloca ();
-}
-
-} // namespace selftest
-
-#endif /* CHECKING_P */
-
-} // namespace ana
-
-#endif /* #if ENABLE_ANALYZER */
diff --git a/gcc/analyzer/region-model2.h b/gcc/analyzer/region-model2.h
deleted file mode 100644
index c066cb507ae..00000000000
--- a/gcc/analyzer/region-model2.h
+++ /dev/null
@@ -1,3146 +0,0 @@
-/* Classes for modeling the state of memory.
-   Copyright (C) 2019-2020 Free Software Foundation, Inc.
-   Contributed by David Malcolm <dmalcolm@redhat.com>.
-
-This file is part of GCC.
-
-GCC is free software; you can redistribute it and/or modify it
-under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 3, or (at your option)
-any later version.
-
-GCC is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING3.  If not see
-<http://www.gnu.org/licenses/>.  */
-
-#ifndef GCC_ANALYZER_REGION_MODEL_2_H
-#define GCC_ANALYZER_REGION_MODEL_2_H
-
-#include "analyzer/uniq-manager.h" // FIXME
-#include "analyzer/store2.h" // FIXME
-#include "analyzer/program-point.h" // FIXME
-
-#define WIDENING 1
-
-/* Implementation of the region-based ternary model described in:
-     "A Memory Model for Static Analysis of C Programs"
-      (Zhongxing Xu, Ted Kremenek, and Jian Zhang)
-     http://lcs.ios.ac.cn/~xuzb/canalyze/memmodel.pdf  */
-
-
-/* FIXME:
-   This prototype has an alternate implementation of region_model.
-   Idea:
-   - no region_id or svalue_id, just pointers.
-   - regions are shared throughout the analysis, via a region_manager.
-   - should svalues also be shared?  Probably
-   - regions and values don't have state; region_model2 instances do
-     (though potentially we want to use the regions to store information
-     about the shared region structure e.g. subregions of map_region)
-   - no canonicalization or ID remapping should be needed
-   - uniqueness of regions: comparison by pointer rather than by fields
-     (but maybe we need to keep the compare_fields for implementing the
-     uniquifying?)
-   - not yet sure if we need validation
-   - the concept of what the "current stack" is needs to move from the
-     regions to the region_model2 (maybe a region *m_current_frame)
-   - do we still need region cloning?
-   - should there still be a root_region?  or should those things go in the
-     ext_state (or something owned by ext_state: region_model2_manager)
-   Hopefully this is a huge simplification, and (I hope) avoids lots of heap
-   churn as states are copied.
-
-   Other ideas:
-   - region subclasses no longer represent internal structure, but instead
-     represent how the regions are reached.  So e.g. a global
-     "struct coord c;" is now a decl_region, rather than a struct_region.
-
-   TODO:
-   - some selftests depend on bit sizes of types, hopefully via the TYPE_SIZE
-     macros (see e.g. INT_TYPE_SIZE in defaults.h).  Remember to test on
-     some other targets.  Similarly, big-endian vs little-endian.  */
-
-using namespace ana;
-
-namespace inchash
-{
-  extern void add_path_var (path_var pv, hash &hstate);
-} // namespace inchash
-
-
-namespace ana {
-
-/* A hash traits class that uses member functions to implement
-   the various required ops.  */
-
-template <typename Type>
-struct member_function_hash_traits : public typed_noop_remove<Type>
-{
-  typedef Type value_type;
-  typedef Type compare_type;
-  static inline hashval_t hash (value_type v) { return v.hash (); }
-  static inline bool equal (const value_type &existing,
-			    const value_type &candidate)
-  {
-    return existing == candidate;
-  }
-  static inline void mark_deleted (Type &t) { t.mark_deleted (); }
-  static inline void mark_empty (Type &t) { t.mark_empty (); }
-  static inline bool is_deleted (Type t) { return t.is_deleted (); }
-  static inline bool is_empty (Type t) { return t.is_empty (); }
-};
-
-/* A region_model2 is effectively a graph of regions and symbolic values.
-   FIXME: we're eliminating IDs in this prototype.
-   We store per-model IDs rather than pointers to make it easier to clone
-   and to compare graphs.  */
-
-/* Various operations delete information from a region_model2.
-
-   This struct tracks how many of each kind of entity were purged (e.g.
-   for selftests, and for debugging).  */
-
-struct purge_stats2
-{
-  purge_stats2 ()
-  : m_num_svalues (0),
-    m_num_regions (0),
-    m_num_equiv_classes (0),
-    m_num_constraints (0),
-    m_num_client_items (0)
-  {}
-
-  int m_num_svalues;
-  int m_num_regions;
-  int m_num_equiv_classes;
-  int m_num_constraints;
-  int m_num_client_items;
-};
-
-/* A measurement of the complexity of an svalue2 or region2, so that
-   we can avoid these tree-like structures growing without bounds and
-   thus leading to infinite chains of analysis.  */
-
-struct complexity
-{
-  complexity (unsigned num_nodes, unsigned max_depth)
-  : m_num_nodes (num_nodes), m_max_depth (max_depth)
-  {}
-
-  complexity (const region2 *reg);
-  complexity (const svalue2 *sval);
-  static complexity from_pair (const complexity &c1, const complexity &c2);
-
-  /* The total number of svalue2s and region2s in the tree of this
-     entity, including the entity itself.  */
-  unsigned m_num_nodes;
-
-  /* The maximum depth of the tree of this entity, including the
-     entity itself.  */
-  unsigned m_max_depth;
-};
-
-class visitor
-{
-public:
-  virtual void visit_region_svalue2 (const region_svalue2 *) {}
-  virtual void visit_constant_svalue2 (const constant_svalue2 *) {}
-  virtual void visit_unknown_svalue2 (const unknown_svalue2 *) {}
-  virtual void visit_poisoned_svalue2 (const poisoned_svalue2 *) {}
-  virtual void visit_setjmp_svalue2 (const setjmp_svalue2 *) {}
-  virtual void visit_initial_svalue2 (const initial_svalue2 *) {}
-  virtual void visit_unaryop_svalue2 (const unaryop_svalue2 *) {}
-  virtual void visit_binop_svalue2 (const binop_svalue2 *) {}
-  virtual void visit_sub_svalue2 (const sub_svalue2 *) {}
-  virtual void visit_unmergeable_svalue2 (const unmergeable_svalue2 *) {}
-  virtual void visit_placeholder_svalue2 (const placeholder_svalue2 *) {}
-  virtual void visit_widening_svalue2 (const widening_svalue2 *) {}
-  virtual void visit_compound_svalue2 (const compound_svalue2 *) {}
-  virtual void visit_conjured_svalue2 (const conjured_svalue2 *) {}
-
-  virtual void visit_region (const region2 *) {}
-};
-
-/* svalue and its subclasses.
-
-   The class hierarchy looks like this (using indentation to show
-   inheritance, and with kinds shown for the concrete subclasses):
-
-   svalue2
-     region_svalue2 (SK_REGION): a pointer to a region2
-     constant_svalue2 (SK_CONSTANT): a constant
-     unknown_svalue2 (SK_UNKNOWN): an unknowable value
-     poisoned_svalue2 (SK_POISONED): a unusable value (undefined)
-     setjmp_svalue2 (SK_SETJMP): a setjmp/longjmp buffer
-     initial2_svalue2 (SK_INITIAL): the initial value of a region2
-     unaryop_svalue2 (SK_UNARYOP): unary operation on another svalue2
-     binop_svalue2 (SK_BINOP): binary operation on two svalue2s
-     sub_svalue2 (SK_SUB): the result of accessing a subregion
-     unmergeable_svalue2 (SK_UNMERGEABLE): a value that is so interesting
-       from a control-flow perspective that it can inhibit state-merging
-     placeholder_svalue2 (SK_PLACEHOLDER): for use in selftests.
-     widening_svalue2 (SK_WIDENING): a merger of two svalues (possibly
-       in an iteration).
-     compound_svalue2 (SK_COMPOUND): a mapping of bit-ranges to svalues
-     conjured_svalue2 (SK_CONJURED): a value arising from a stmt.  */
-
-/* An abstract base class representing a value held by a region of memory.  */
-
-class svalue2
-{
-public:
-  /* An enum for discriminating between the different concrete subclasses
-   of svalue.  */
-  enum kind
-  {
-   SK_REGION,
-   SK_CONSTANT,
-   SK_UNKNOWN,
-   SK_POISONED,
-   SK_SETJMP,
-   SK_INITIAL,
-   SK_UNARYOP,
-   SK_BINOP,
-   SK_SUB,
-   SK_UNMERGEABLE,
-   SK_PLACEHOLDER,
-   SK_WIDENING,
-   SK_COMPOUND,
-   SK_CONJURED
-  };
-
-  virtual ~svalue2 () {}
-
-#if 0
-  bool operator== (const svalue2 &other) const;
-  bool operator!= (const svalue2 &other) const { return !(*this == other); }
-#endif
-
-  tree get_type () const { return m_type; }
-
-  virtual enum kind get_kind () const = 0;
-
-  void print (const region_model2 &model,
-	      pretty_printer *pp) const;
-
-#if 0
-  virtual void dump_dot_to_pp (const region_model2 &model,
-			       pretty_printer *pp) const;
-#endif
-
-  virtual void dump_to_pp (pretty_printer *pp, bool simple) const = 0;
-  void dump (bool simple=true) const;
-  label_text get_desc (bool simple=true) const;
-
-  virtual const region_svalue2 *
-  dyn_cast_region_svalue2 () const { return NULL; }
-  virtual const constant_svalue2 *
-  dyn_cast_constant_svalue2 () const { return NULL; }
-  virtual const poisoned_svalue2 *
-  dyn_cast_poisoned_svalue2 () const { return NULL; }
-  virtual const setjmp_svalue2 *
-  dyn_cast_setjmp_svalue2 () const { return NULL; }
-  virtual const initial_svalue2 *
-  dyn_cast_initial_svalue2 () const { return NULL; }
-  virtual const unaryop_svalue2 *
-  dyn_cast_unaryop_svalue2 () const { return NULL; }
-  virtual const binop_svalue2 *
-  dyn_cast_binop_svalue2 () const { return NULL; }
-  virtual const sub_svalue2 *
-  dyn_cast_sub_svalue2 () const { return NULL; }
-  virtual const unmergeable_svalue2 *
-  dyn_cast_unmergeable_svalue2 () const { return NULL; }
-  virtual const widening_svalue2 *
-  dyn_cast_widening_svalue2 () const { return NULL; }
-  virtual const compound_svalue2 *
-  dyn_cast_compound_svalue2 () const { return NULL; }
-  virtual const conjured_svalue2 *
-  dyn_cast_conjured_svalue2 () const { return NULL; }
-
-  tree maybe_get_constant () const;
-  const svalue2 *maybe_undo_cast () const;
-  const svalue2 *unwrap_any_unmergeable () const;
-
-  const svalue2 *can_merge_p (const svalue2 *other,
-			      region_model2_manager *mgr,
-			      model2_merger *merger) const;
-
-  const complexity &get_complexity () const { return m_complexity; }
-
-  virtual void accept (visitor *v) const  = 0;
-
-  bool live_p (const svalue2_set &live_svalues,
-	       const region_model2 *model) const;
-  virtual bool implicitly_live_p (const svalue2_set &live_svalues,
-				  const region_model2 *model) const;
-
- protected:
-  svalue2 (complexity c, tree type)
-  : m_complexity (c), m_type (type)
-  {}
-
- private:
-  complexity m_complexity;
-  tree m_type;
-};
-
-/* Concrete subclass of svalue2 representing a pointer value that points to
-   a known region  */
-
-class region_svalue2 : public svalue2
-{
-public:
-  region_svalue2 (tree type, const region2 *reg)
-  : svalue2 (complexity (reg), type),
-    m_reg (reg)
-  {
-    gcc_assert (m_reg != NULL);
-  }
-
-  enum kind get_kind () const FINAL OVERRIDE { return SK_REGION; }
-
-#if 0
-  void dump_dot_to_pp (const region_model2 &model,
-		       pretty_printer *pp) const
-    FINAL OVERRIDE;
-#endif
-
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-
-  const region_svalue2 *
-  dyn_cast_region_svalue2 () const FINAL OVERRIDE { return this; }
-
-  void accept (visitor *v) const FINAL OVERRIDE;
-
-  const region2 * get_pointee () const { return m_reg; }
-
-#if 0
-  static void merge_values (const region_svalue2 &region_sval_a,
-			    const region_svalue2 &region_sval_b,
-			    svalue2_id *merged_sid,
-			    tree type,
-			    model_merger *merger);
-#endif
-
-  static tristate eval_condition (const region_svalue2 *lhs_ptr,
-				  enum tree_code op,
-				  const region_svalue2 *rhs_ptr);
-
- private:
-  const region2 *m_reg;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const region_svalue2 *>::test (const svalue2 *sval)
-{
-  return sval->get_kind () == svalue2::SK_REGION;
-}
-
-namespace ana {
-
-/* Concrete subclass of svalue2 representing a specific constant value.  */
-
-class constant_svalue2 : public svalue2
-{
-public:
-  constant_svalue2 (tree cst_expr)
-  : svalue2 (complexity (1, 1), TREE_TYPE (cst_expr)), m_cst_expr (cst_expr)
-  {
-    gcc_assert (cst_expr);
-    gcc_assert (CONSTANT_CLASS_P (cst_expr));
-  }
-
-  enum kind get_kind () const FINAL OVERRIDE { return SK_CONSTANT; }
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-
-  const constant_svalue2 *
-  dyn_cast_constant_svalue2 () const FINAL OVERRIDE { return this; }
-
-  void accept (visitor *v) const FINAL OVERRIDE;
-
-  bool implicitly_live_p (const svalue2_set &,
-			  const region_model2 *) const FINAL OVERRIDE;
-
-  tree get_constant () const { return m_cst_expr; }
-
-#if 0
-  static void merge_values (const constant_svalue2 &cst_sval_a,
-			    const constant_svalue2 &cst_sval_b,
-			    svalue2_id *merged_sid,
-			    model_merger *merger);
-#endif
-
-  static tristate eval_condition (const constant_svalue2 *lhs,
-				  enum tree_code op,
-				  const constant_svalue2 *rhs);
-
- private:
-  tree m_cst_expr;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const constant_svalue2 *>::test (const svalue2 *sval)
-{
-  return sval->get_kind () == svalue2::SK_CONSTANT;
-}
-
-namespace ana {
-
-/* Concrete subclass of svalue2 representing an unknowable value, the bottom
-   value when thinking of svalue2s as a lattice.
-   This is a singleton (w.r.t. its manager): there is a single unknown_svalue2
-   per type.  Self-comparisons of such instances yield "unknown".  */
-
-class unknown_svalue2 : public svalue2
-{
-public:
-  unknown_svalue2 (tree type)
-  : svalue2 (complexity (1, 1), type)
-  {}
-
-  enum kind get_kind () const FINAL OVERRIDE { return SK_UNKNOWN; }
-  void accept (visitor *v) const FINAL OVERRIDE;
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-};
-
-#if 0
-/* An enum describing a particular kind of "poisoned" value.  */
-
-enum poison_kind
-{
-  /* For use to describe uninitialized memory.  */
-  POISON_KIND_UNINIT,
-
-  /* For use to describe freed memory.  */
-  POISON_KIND_FREED,
-
-  /* For use on pointers to regions within popped stack frames.  */
-  POISON_KIND_POPPED_STACK
-};
-#endif
-
-extern const char *poison_kind_to_str (enum poison_kind);
-
-/* Concrete subclass of svalue2 representing a value that should not
-   be used (e.g. uninitialized memory, freed memory).  */
-
-class poisoned_svalue2 : public svalue2
-{
-public:
-  /* A support class for uniquifying instances of poisoned_svalue2.  */
-  struct key_t
-  {
-    key_t (enum poison_kind kind, tree type)
-    : m_kind (kind), m_type (type)
-    {}
-
-    hashval_t hash () const
-    {
-      inchash::hash hstate;
-      hstate.add_int (m_kind);
-      hstate.add_ptr (m_type);
-      return hstate.end ();
-    }
-
-    bool operator== (const key_t &other) const
-    {
-      return (m_kind == other.m_kind && m_type == other.m_type);
-    }
-
-    void mark_deleted () { m_type = reinterpret_cast<tree> (1); }
-    void mark_empty () { m_type = NULL_TREE; }
-    bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); }
-    bool is_empty () const { return m_type == NULL_TREE; }
-
-    enum poison_kind m_kind;
-    tree m_type;
-  };
-
-  poisoned_svalue2 (enum poison_kind kind, tree type)
-  : svalue2 (complexity (1, 1), type), m_kind (kind) {}
-
-  enum kind get_kind () const FINAL OVERRIDE { return SK_POISONED; }
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-
-  const poisoned_svalue2 *
-  dyn_cast_poisoned_svalue2 () const FINAL OVERRIDE { return this; }
-
-  void accept (visitor *v) const FINAL OVERRIDE;
-
-  enum poison_kind get_poison_kind () const { return m_kind; }
-
- private:
-  enum poison_kind m_kind;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const poisoned_svalue2 *>::test (const svalue2 *sval)
-{
-  return sval->get_kind () == svalue2::SK_POISONED;
-}
-
-template <> struct default_hash_traits<poisoned_svalue2::key_t>
-: public member_function_hash_traits<poisoned_svalue2::key_t>
-{
-  static const bool empty_zero_p = true;
-};
-
-namespace ana {
-
-/* Concrete subclass of svalue2 representing buffers for setjmp/sigsetjmp,
-   so that longjmp/siglongjmp can potentially "return" to an entirely
-   different function.  */
-
-class setjmp_svalue2 : public svalue2
-{
-public:
-  /* A support class for uniquifying instances of poisoned_svalue2.  */
-  struct key_t
-  {
-    key_t (const setjmp_record &record, tree type)
-    : m_record (record), m_type (type)
-    {}
-
-    hashval_t hash () const
-    {
-      inchash::hash hstate;
-      m_record.add_to_hash (&hstate);
-      hstate.add_ptr (m_type);
-      return hstate.end ();
-    }
-
-    bool operator== (const key_t &other) const
-    {
-      return (m_record == other.m_record && m_type == other.m_type);
-    }
-
-    void mark_deleted () { m_type = reinterpret_cast<tree> (1); }
-    void mark_empty () { m_type = NULL_TREE; }
-    bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); }
-    bool is_empty () const { return m_type == NULL_TREE; }
-
-    setjmp_record m_record;
-    tree m_type;
-  };
-
-  setjmp_svalue2 (const setjmp_record &setjmp_record,
-		  tree type)
-  : svalue2 (complexity (1, 1), type), m_setjmp_record (setjmp_record)
-  {}
-
-  enum kind get_kind () const FINAL OVERRIDE { return SK_SETJMP; }
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-
-  const setjmp_svalue2 *
-  dyn_cast_setjmp_svalue2 () const FINAL OVERRIDE { return this; }
-
-  void accept (visitor *v) const FINAL OVERRIDE;
-
-  int get_enode_index () const;
-
-  const setjmp_record &get_setjmp_record () const { return m_setjmp_record; }
-
- private:
-  setjmp_record m_setjmp_record;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const setjmp_svalue2 *>::test (const svalue2 *sval)
-{
-  return sval->get_kind () == svalue2::SK_SETJMP;
-}
-
-template <> struct default_hash_traits<setjmp_svalue2::key_t>
-: public member_function_hash_traits<setjmp_svalue2::key_t>
-{
-  static const bool empty_zero_p = true;
-};
-
-namespace ana {
-
-/* Concrete subclass of svalue2 representing the initial value of a
-   specific region.
-
-   FIXME: trying this approach:
-   This represents the initial value at the start of the analysis path,
-   as opposed to the first time the region is accessed during the path.
-   Hence as soon as we have a call to an unknown function, all previously
-   unmodelled globals become implicitly "unknown" rathen than "initial".  */
-
-class initial_svalue2 : public svalue2
-{
-public:
-  initial_svalue2 (tree type, const region2 *reg)
-  : svalue2 (complexity (reg), type), m_reg (reg)
-  {
-    gcc_assert (m_reg != NULL);
-  }
-
-  enum kind get_kind () const FINAL OVERRIDE { return SK_INITIAL; }
-
-#if 0
-  void dump_dot_to_pp (const region_model2 &model,
-		       pretty_printer *pp) const
-    FINAL OVERRIDE;
-#endif
-
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-
-  const initial_svalue2 *
-  dyn_cast_initial_svalue2 () const FINAL OVERRIDE { return this; }
-
-  void accept (visitor *v) const FINAL OVERRIDE;
-
-  bool implicitly_live_p (const svalue2_set &,
-			  const region_model2 *) const FINAL OVERRIDE;
-
-  const region2 * get_region () const { return m_reg; }
-
-#if 0
-  static void merge_values (const initial_svalue2 &region_sval_a,
-			    const initial_svalue2 &region_sval_b,
-			    svalue2_id *merged_sid,
-			    tree type,
-			    model_merger *merger);
-#endif
-
- private:
-  const region2 *m_reg;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const initial_svalue2 *>::test (const svalue2 *sval)
-{
-  return sval->get_kind () == svalue2::SK_INITIAL;
-}
-
-namespace ana {
-
-/* Concrete subclass of svalue2 representing a unary operation on
-   another svalue2s (e.g. a cast).  */
-
-class unaryop_svalue2 : public svalue2
-{
-public:
-  /* A support class for uniquifying instances of unaryop_svalue2.  */
-  struct key_t
-  {
-    key_t (tree type, enum tree_code op, const svalue2 *arg)
-    : m_type (type), m_op (op), m_arg (arg)
-    {}
-
-    hashval_t hash () const
-    {
-      inchash::hash hstate;
-      hstate.add_ptr (m_type);
-      hstate.add_int (m_op);
-      hstate.add_ptr (m_arg);
-      return hstate.end ();
-    }
-
-    bool operator== (const key_t &other) const
-    {
-      return (m_type == other.m_type
-	      && m_op == other.m_op
-	      && m_arg == other.m_arg);
-    }
-
-    void mark_deleted () { m_type = reinterpret_cast<tree> (1); }
-    void mark_empty () { m_type = NULL_TREE; }
-    bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); }
-    bool is_empty () const { return m_type == NULL_TREE; }
-
-    tree m_type;
-    enum tree_code m_op;
-    const svalue2 *m_arg;
-  };
-
-  unaryop_svalue2 (tree type, enum tree_code op, const svalue2 *arg)
-  : svalue2 (complexity (arg), type), m_op (op), m_arg (arg)
-  {
-  }
-
-  enum kind get_kind () const FINAL OVERRIDE { return SK_UNARYOP; }
-  const unaryop_svalue2 *
-  dyn_cast_unaryop_svalue2 () const FINAL OVERRIDE { return this; }
-
-  void accept (visitor *v) const FINAL OVERRIDE;
-
-  bool implicitly_live_p (const svalue2_set &,
-			  const region_model2 *) const FINAL OVERRIDE;
-
-#if 0
-  void dump_dot_to_pp (const region_model2 &model,
-		       pretty_printer *pp) const
-    FINAL OVERRIDE;
-#endif
-
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-
-
-  enum tree_code get_op () const { return m_op; }
-  const svalue2 *get_arg () const { return m_arg; }
-
- private:
-  enum tree_code m_op;
-  const svalue2 *m_arg;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const unaryop_svalue2 *>::test (const svalue2 *sval)
-{
-  return sval->get_kind () == svalue2::SK_UNARYOP;
-}
-
-template <> struct default_hash_traits<unaryop_svalue2::key_t>
-: public member_function_hash_traits<unaryop_svalue2::key_t>
-{
-  static const bool empty_zero_p = true;
-};
-
-namespace ana {
-
-/* Concrete subclass of svalue2 representing a binary operation of
-   two svalue2s.  */
-
-class binop_svalue2 : public svalue2
-{
-public:
-  /* A support class for uniquifying instances of binop_svalue2.  */
-  struct key_t
-  {
-    key_t (tree type, enum tree_code op,
-	   const svalue2 *arg0, const svalue2 *arg1)
-    : m_type (type), m_op (op), m_arg0 (arg0), m_arg1 (arg1)
-    {}
-
-    hashval_t hash () const
-    {
-      inchash::hash hstate;
-      hstate.add_ptr (m_type);
-      hstate.add_int (m_op);
-      hstate.add_ptr (m_arg0);
-      hstate.add_ptr (m_arg1);
-      return hstate.end ();
-    }
-
-    bool operator== (const key_t &other) const
-    {
-      return (m_type == other.m_type
-	      && m_op == other.m_op
-	      && m_arg0 == other.m_arg0
-	      && m_arg1 == other.m_arg1);
-    }
-
-    void mark_deleted () { m_type = reinterpret_cast<tree> (1); }
-    void mark_empty () { m_type = NULL_TREE; }
-    bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); }
-    bool is_empty () const { return m_type == NULL_TREE; }
-
-    tree m_type;
-    enum tree_code m_op;
-    const svalue2 *m_arg0;
-    const svalue2 *m_arg1;
-  };
-
-  binop_svalue2 (tree type, enum tree_code op,
-		 const svalue2 *arg0, const svalue2 *arg1)
-  : svalue2 (complexity::from_pair (arg0->get_complexity (),
-				    arg1->get_complexity ()),
-	     type),
-    m_op (op), m_arg0 (arg0), m_arg1 (arg1)
-  {
-  }
-
-  enum kind get_kind () const FINAL OVERRIDE { return SK_BINOP; }
-  virtual const binop_svalue2 *dyn_cast_binop_svalue2 () const { return this; }
-
-  void accept (visitor *v) const FINAL OVERRIDE;
-
-  bool implicitly_live_p (const svalue2_set &,
-			  const region_model2 *) const FINAL OVERRIDE;
-
-  enum tree_code get_op () const { return m_op; }
-  const svalue2 *get_arg0 () const { return m_arg0; }
-  const svalue2 *get_arg1 () const { return m_arg1; }
-
-#if 0
-  void dump_dot_to_pp (const region_model2 &model,
-		       pretty_printer *pp) const
-    FINAL OVERRIDE;
-#endif
-
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-
-#if 0
-  const binop_svalue2 *
-  dyn_cast_binop_svalue2 () const FINAL OVERRIDE { return this; }
-#endif
-
- private:
-  enum tree_code m_op;
-  const svalue2 *m_arg0;
-  const svalue2 *m_arg1;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const binop_svalue2 *>::test (const svalue2 *sval)
-{
-  return sval->get_kind () == svalue2::SK_BINOP;
-}
-
-template <> struct default_hash_traits<binop_svalue2::key_t>
-: public member_function_hash_traits<binop_svalue2::key_t>
-{
-  static const bool empty_zero_p = true;
-};
-
-namespace ana {
-
-/* Concrete subclass of svalue2 representing the result of accessing a subregion
-   of another svalue2 (the value of a component/field of a struct, or an element
-   from an array).
-   FIXME: do we want to split this into two different classes for that?  */
-
-class sub_svalue2 : public svalue2
-{
-public:
-  /* A support class for uniquifying instances of sub_svalue2.  */
-  struct key_t
-  {
-    key_t (tree type, const svalue2 *parent_svalue, const region2 *subregion)
-    : m_type (type), m_parent_svalue (parent_svalue), m_subregion (subregion)
-    {}
-
-    hashval_t hash () const
-    {
-      inchash::hash hstate;
-      hstate.add_ptr (m_type);
-      hstate.add_ptr (m_parent_svalue);
-      hstate.add_ptr (m_subregion);
-      return hstate.end ();
-    }
-
-    bool operator== (const key_t &other) const
-    {
-      return (m_type == other.m_type
-	      && m_parent_svalue == other.m_parent_svalue
-	      && m_subregion == other.m_subregion);
-    }
-
-    void mark_deleted () { m_type = reinterpret_cast<tree> (1); }
-    void mark_empty () { m_type = NULL_TREE; }
-    bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); }
-    bool is_empty () const { return m_type == NULL_TREE; }
-
-    tree m_type;
-    const svalue2 *m_parent_svalue;
-    const region2 *m_subregion;
-  };
-  sub_svalue2 (tree type, const svalue2 *parent_svalue,
-	       const region2 *subregion);
-
-  enum kind get_kind () const FINAL OVERRIDE { return SK_SUB; }
-  const sub_svalue2 *dyn_cast_sub_svalue2 () const FINAL OVERRIDE
-  {
-    return this;
-  }
-  void accept (visitor *v) const FINAL OVERRIDE;
-  bool implicitly_live_p (const svalue2_set &,
-			  const region_model2 *) const FINAL OVERRIDE;
-
-#if 0
-  void dump_dot_to_pp (const region_model2 &model,
-		       pretty_printer *pp) const
-    FINAL OVERRIDE;
-#endif
-
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-
-  const svalue2 *get_parent () const { return m_parent_svalue; }
-  const region2 *get_subregion () const { return m_subregion; }
-
- private:
-  const svalue2 *m_parent_svalue;
-  const region2 *m_subregion;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const sub_svalue2 *>::test (const svalue2 *sval)
-{
-  return sval->get_kind () == svalue2::SK_SUB;
-}
-
-template <> struct default_hash_traits<sub_svalue2::key_t>
-: public member_function_hash_traits<sub_svalue2::key_t>
-{
-  static const bool empty_zero_p = true;
-};
-
-namespace ana {
-
-/* Concrete subclass of svalue2: decorate another svalue2,
-   so that the resulting svalue2 can be identified as being
-   "interesting to control flow".
-   For example, consider the return value from setjmp.  We
-   don't want to merge states in which the result is 0 with
-   those in which the result is non-zero.  By using an
-   unmergeable_svalue2 for the result, we can inhibit such merges
-   and have separate exploded nodes for those states, keeping
-   the first and second returns from setjmp distinct in the exploded
-   graph.
-   FIXME: possibly rename to "significant_svalue2"?
-   FIXME: possibly use this to capture "significant" conditions in
-   control flow, and to use this to tweak descriptions of conditions
-   in paths.  Specifically, could this be used by
-     start_cfg_edge_event::maybe_describe_condition
-   to emit things like
-     following 'true' branch (when value returned from 'setjmp' is non-zero)
-   or somesuch.  */
-
-class unmergeable_svalue2 : public svalue2
-{
-public:
-  unmergeable_svalue2 (const svalue2 *arg)
-  : svalue2 (complexity (arg), arg->get_type ()), m_arg (arg)
-  {
-  }
-
-  enum kind get_kind () const FINAL OVERRIDE { return SK_UNMERGEABLE; }
-  const unmergeable_svalue2 *
-  dyn_cast_unmergeable_svalue2 () const FINAL OVERRIDE { return this; }
-  void accept (visitor *v) const FINAL OVERRIDE;
-  bool implicitly_live_p (const svalue2_set &,
-			  const region_model2 *) const FINAL OVERRIDE;
-
-#if 0
-  void dump_dot_to_pp (const region_model2 &model,
-		       pretty_printer *pp) const
-    FINAL OVERRIDE;
-#endif
-
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-
-  const svalue2 *get_arg () const { return m_arg; }
-
- private:
-  const svalue2 *m_arg;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const unmergeable_svalue2 *>::test (const svalue2 *sval)
-{
-  return sval->get_kind () == svalue2::SK_UNMERGEABLE;
-}
-
-namespace ana {
-
-/* Concrete subclass of svalue2 for use in selftests, where
-   we want a specific but unknown svalue2.
-   Unlike other svalue2 subclasses these aren't managed by
-   region_model2_manager.  */
-
-class placeholder_svalue2 : public svalue2
-{
-public:
-  placeholder_svalue2 (tree type, const char *name)
-  : svalue2 (complexity (1, 1), type), m_name (name)
-  {
-  }
-
-  enum kind get_kind () const FINAL OVERRIDE { return SK_PLACEHOLDER; }
-  void accept (visitor *v) const FINAL OVERRIDE;
-
-#if 0
-  void dump_dot_to_pp (const region_model2 &model,
-		       pretty_printer *pp) const
-    FINAL OVERRIDE;
-#endif
-
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-
-#if 0
-  binop_svalue2 *dyn_cast_placeholder_svalue2 () FINAL OVERRIDE { return this; }
-#endif
-
- private:
-  const char *m_name;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <placeholder_svalue2 *>::test (svalue2 *sval)
-{
-  return sval->get_kind () == svalue2::SK_PLACEHOLDER;
-}
-
-namespace ana {
-
-/* Concrete subclass of svalue2 representing a "widening" seen when merging
-   states, widening from a base value to {base value, iter value} and thus
-   representing a possible fixed point in an iteration from the base to
-   +ve infinity, or -ve infinity, and thus useful for representing a value
-   within a loop.
-   We also need to capture the program_point at which the merger happens,
-   so that distinguish between different iterators, and thus handle
-   nested loops.
-
-   FIXME: firm this up; clarify the above.  */
-
-class widening_svalue2 : public svalue2
-{
-public:
-  /* A support class for uniquifying instances of widening_svalue2.  */
-  struct key_t
-  {
-#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
-    {
-      inchash::hash hstate;
-      hstate.add_ptr (m_base_sval);
-      hstate.add_ptr (m_iter_sval);
-      return hstate.end ();
-    }
-
-    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);
-    }
-
-    void mark_deleted () { m_type = reinterpret_cast<tree> (1); }
-    void mark_empty () { m_type = NULL_TREE; }
-    bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); }
-    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;
-  };
-
-  enum direction_t
-    {
-     DIR_ASCENDING,
-     DIR_DESCENDING,
-     DIR_UNKNOWN
-    };
-  widening_svalue2 (tree type, const program_point &point,
-		    const svalue2 *base_sval, const svalue2 *iter_sval)
-  : svalue2 (complexity::from_pair (base_sval->get_complexity (),
-				    iter_sval->get_complexity ()),
-	     type),
-    m_point (point.get_function_point ()),
-    m_base_sval (base_sval), m_iter_sval (iter_sval)
-  {
-  }
-
-  enum kind get_kind () const FINAL OVERRIDE { return SK_WIDENING; }
-  const widening_svalue2 *dyn_cast_widening_svalue2 () const { return this; }
-  void accept (visitor *v) const FINAL OVERRIDE;
-
-#if 0
-  void dump_dot_to_pp (const region_model2 &model,
-		       pretty_printer *pp) const
-    FINAL OVERRIDE;
-#endif
-
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-
-  const svalue2 *get_base_svalue2 () const { return m_base_sval; }
-  const svalue2 *get_iter_svalue2 () const { return m_iter_sval; }
-
-  enum direction_t get_direction () const;
-
-  tristate eval_condition_without_cm (enum tree_code op,
-				      tree rhs_cst) const;
-
- private:
-#if 0
-  program_point m_point;
-#else
-  function_point m_point;
-#endif
-  const svalue2 *m_base_sval;
-  const svalue2 *m_iter_sval;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <widening_svalue2 *>::test (svalue2 *sval)
-{
-  return sval->get_kind () == svalue2::SK_WIDENING;
-}
-
-#if 1
-template <> struct default_hash_traits<widening_svalue2::key_t>
-: public member_function_hash_traits<widening_svalue2::key_t>
-{
-  static const bool empty_zero_p = true;
-};
-#endif
-
-namespace ana {
-
-/* Concrete subclass of svalue2 representing a mapping of bit-ranges
-   to svalues, analogous to a cluster within the store.
-
-   This is for use in places where we want to represent a store-like
-   mapping, but are required to use an svalue2, such as when handling
-   compound assignments and compound return values.
-
-   Instances of this class shouldn't be bound as-is into the store;
-   instead they should be unpacked.  Similarly, they should not be
-   nested.  */
-
-class compound_svalue2 : public svalue2
-{
-public:
-  typedef binding_map::iterator_t iterator_t;
-
-  /* A support class for uniquifying instances of compound_svalue2.
-     Note that to avoid copies, keys store pointers to binding_maps,
-     rather than the maps themselves.  */
-  struct key_t
-  {
-    key_t (tree type, const binding_map *map_ptr)
-    : m_type (type), m_map_ptr (map_ptr)
-    {}
-
-    hashval_t hash () const
-    {
-      inchash::hash hstate;
-      hstate.add_ptr (m_type);
-      //hstate.add_ptr (m_map_ptr); // TODO
-      return hstate.end ();
-    }
-
-    bool operator== (const key_t &other) const
-    {
-      return (m_type == other.m_type
-	      && *m_map_ptr == *other.m_map_ptr);
-    }
-
-    void mark_deleted () { m_type = reinterpret_cast<tree> (1); }
-    void mark_empty () { m_type = NULL_TREE; }
-    bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); }
-    bool is_empty () const { return m_type == NULL_TREE; }
-
-    tree m_type;
-    const binding_map *m_map_ptr;
-  };
-
-  compound_svalue2 (tree type, const binding_map &map)
-  : svalue2 (calc_complexity (map), type),
-    m_map (map)
-  {
-  }
-
-  enum kind get_kind () const FINAL OVERRIDE { return SK_COMPOUND; }
-  const compound_svalue2 *dyn_cast_compound_svalue2 () const { return this; }
-  void accept (visitor *v) const FINAL OVERRIDE;
-
-  iterator_t begin () const { return m_map.begin (); }
-  iterator_t end () const { return m_map.end (); }
-
-#if 0
-  void dump_dot_to_pp (const region_model2 &model,
-		       pretty_printer *pp) const
-    FINAL OVERRIDE;
-#endif
-
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-
-  struct key_t make_key () const
-  {
-    return key_t (get_type (), &m_map);
-  }
-
- private:
-  static complexity calc_complexity (const binding_map &map);
-
-  binding_map m_map;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <compound_svalue2 *>::test (svalue2 *sval)
-{
-  return sval->get_kind () == svalue2::SK_COMPOUND;
-}
-
-template <> struct default_hash_traits<compound_svalue2::key_t>
-: public member_function_hash_traits<compound_svalue2::key_t>
-{
-  static const bool empty_zero_p = true;
-};
-
-namespace ana {
-
-/* A defined value arising from a statement, where we want to identify a
-   particular unknown value, rather than resorting to the unknown_value
-   singleton, so that the value can have sm-state.
-
-   Comparisons of variables that share the same conjured_svalue2 are known
-   to be equal, even if we don't know what the value is.
-
-   For example, this is used for the values of regions that may have been
-   touched when calling an unknown function.
-
-   The value captures a region as well as a stmt in order to avoid falsely
-   aliasing the various values that could arise in one statement.  For
-   example, after:
-      unknown_fn (&a, &b);
-   we want values to clobber a and b with, but we don't want to use the
-   same value, or it would falsely implicitly assume that a == b.  */
-
-class conjured_svalue2 : public svalue2
-{
-public:
-  typedef binding_map::iterator_t iterator_t;
-
-  /* A support class for uniquifying instances of conjured_svalue2.  */
-  struct key_t
-  {
-    key_t (tree type, const gimple *stmt, const region2 *id_reg)
-    : m_type (type), m_stmt (stmt), m_id_reg (id_reg)
-    {}
-
-    hashval_t hash () const
-    {
-      inchash::hash hstate;
-      hstate.add_ptr (m_type);
-      hstate.add_ptr (m_stmt);
-      hstate.add_ptr (m_id_reg);
-      return hstate.end ();
-    }
-
-    bool operator== (const key_t &other) const
-    {
-      return (m_type == other.m_type
-	      && m_stmt == other.m_stmt
-	      && m_id_reg == other.m_id_reg);
-    }
-
-    /* Use m_stmt to mark empty/deleted, as m_type can be NULL for
-       legitimate instances.  */
-    void mark_deleted () { m_stmt = reinterpret_cast<const gimple *> (1); }
-    void mark_empty () { m_stmt = NULL; }
-    bool is_deleted () const
-    {
-      return m_stmt == reinterpret_cast<const gimple *> (1);
-    }
-    bool is_empty () const { return m_stmt == NULL; }
-
-    tree m_type;
-    const gimple *m_stmt;
-    const region2 *m_id_reg;
-  };
-
-  conjured_svalue2 (tree type, const gimple *stmt, const region2 *id_reg)
-  : svalue2 (complexity (id_reg), type),
-    m_stmt (stmt), m_id_reg (id_reg)
-  {
-    gcc_assert (m_stmt != NULL);
-  }
-
-  enum kind get_kind () const FINAL OVERRIDE { return SK_CONJURED; }
-  const conjured_svalue2 *dyn_cast_conjured_svalue2 () const { return this; }
-  void accept (visitor *v) const FINAL OVERRIDE;
-
-#if 0
-  void dump_dot_to_pp (const region_model2 &model,
-		       pretty_printer *pp) const
-    FINAL OVERRIDE;
-#endif
-
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-
- private:
-  const gimple *m_stmt;
-  const region2 *m_id_reg;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <conjured_svalue2 *>::test (svalue2 *sval)
-{
-  return sval->get_kind () == svalue2::SK_CONJURED;
-}
-
-#if 1
-template <> struct default_hash_traits<conjured_svalue2::key_t>
-: public member_function_hash_traits<conjured_svalue2::key_t>
-{
-  static const bool empty_zero_p = true;
-};
-#endif
-
-namespace ana {
-
-/* Region and its subclasses.
-
-   The class hierarchy looks like this (using indentation to show
-   inheritance, and with region_kinds shown for the concrete subclasses):
-
-   FIXME: this needs a rewrite; reorganizing regions by identity, rather than
-   structure.  So e.g. a global "struct coord c;" is now a decl_region, rather
-   than a struct_region.
-
-   region
-     space_region
-       frame_region (RK_FRAME)
-       globals_region (RK_GLOBALS)
-       code_region (RK_CODE)
-       stack_region (RK_STACK)
-       heap_region (RK_HEAP)
-       root_region (RK_ROOT)
-     function_region (RK_FUNCTION)
-     label_region2 (RK_LABEL)
-     symbolic_region (RK_SYMBOLIC)
-     field_region2 (RK_FIELD)
-     element_region2 (RK_ELEMENT)
-     offset_region2 (RK_OFFSET)
-     heap_allocated_region2 (RK_HEAP_ALLOCATED)
-     alloca_region2 (RK_ALLOCA)
-     string_region2 (RK_STRING)
-     unknown_region2 (RK_UNKNOWN).  */
-
-/* Abstract base class representing a chunk of memory.
-
-   Regions form a tree-like hierarchy, with a root region at the base,
-   with memory space regions within it, representing the stack and
-   globals, with frames within the stack, and regions for variables
-   within the frames and the "globals" region.  Regions for structs
-   can have subregions for fields.
-
-   FIXME:
-   A region can optionally have a value, or inherit its value from
-   the first ancestor with a value.  For example, the stack region
-   has a "uninitialized" poison value which is inherited by all
-   descendent regions that don't themselves have a value.  */
-
-class region2
-{
-public:
-  /* An enum for discriminating between the different concrete subclasses
-     of region2.  */
-  enum kind
-  {
-   RK_FRAME,
-   RK_GLOBALS,
-   RK_CODE,
-   RK_FUNCTION,
-   RK_LABEL,
-   RK_STACK,
-   RK_HEAP,
-   RK_ROOT,
-   RK_SYMBOLIC,
-   RK_DECL,
-   RK_FIELD,
-   RK_ELEMENT,
-   RK_OFFSET,
-   RK_CAST,
-   RK_HEAP_ALLOCATED,
-   RK_ALLOCA,
-   RK_STRING,
-   RK_UNKNOWN
-  };
-  static const char *kind_to_str (enum kind);
-
-  virtual ~region2 ();
-
-  bool operator== (const region2 &other) const;
-  bool operator!= (const region2 &other) const { return !(*this == other); }
-
-  unsigned get_id () const { return m_id; }
-  static int cmp_ids (const region2 *reg1, const region2 *reg2);
-
-  virtual enum kind get_kind () const = 0;
-  virtual const frame_region2 *
-  dyn_cast_frame_region2 () const { return NULL; }
-  virtual const function_region2 *
-  dyn_cast_function_region2 () const { return NULL; }
-  virtual const symbolic_region2 *
-  dyn_cast_symbolic_region2 () const { return NULL; }
-  virtual const decl_region2 *
-  dyn_cast_decl_region2 () const { return NULL; }
-  virtual const field_region2 *
-  dyn_cast_field_region2 () const { return NULL; }
-  virtual const element_region2 *
-  dyn_cast_element_region2 () const { return NULL; }
-  virtual const offset_region2 *
-  dyn_cast_offset_region2 () const { return NULL; }
-  virtual const cast_region2 *
-  dyn_cast_cast_region2 () const { return NULL; }
-  virtual const string_region2 *
-  dyn_cast_string_region2 () const { return NULL; }
-
-  virtual void accept (visitor *v) const;
-
-  const region2 *get_parent_region () const { return m_parent; }
-  const region2 *get_base_region () const;
-  bool base_region_p () const;
-  bool descendent_of_p (const region2 *elder) const;
-  const frame_region2 *maybe_get_frame_region () const;
-
-  tree maybe_get_decl () const;
-
-#if 0
-  void set_value (region_model2 &model, region_id this_rid, svalue2_id rhs_sid,
-		  region_model2_context *ctxt);
-  svalue2_id get_value (region_model2 &model, bool non_null,
-		       region_model2_context *ctxt);
-  svalue2_id get_value_direct () const { return m_sval_id; }
-
-  svalue2_id get_inherited_child_sid (region *child,
-				     region_model2 &model,
-				     region_model2_context *ctxt);
-#endif
-
-  tree get_type () const { return m_type; }
-
-  hashval_t hash () const;
-
-  void print (const region_model2 &model,
-	      pretty_printer *pp) const;
-  label_text get_desc (bool simple=true) const;
-
-#if 0
-  virtual void dump_dot_to_pp (const region_model2 &model,
-			       pretty_printer *pp) const;
-#endif
-  void dump_to_pp (const region_model2 &model,
-		   pretty_printer *pp,
-		   const char *prefix,
-		   bool is_last_child) const;
-
-  virtual void dump_to_pp (pretty_printer *pp, bool simple) const = 0;
-  void dump (bool simple) const;
-
-#if 0
-  void add_view (region_id view_rid, region_model2 *model);
-  region_id get_view (tree type, region_model2 *model) const;
-  region_id get_active_view () const { return m_active_view_rid; }
-  bool is_view_p () const { return m_is_view; }
-#endif
-#if 0
-  virtual void validate (const region_model2 &model) const;
-#endif
-
-  bool non_null_p (const region_model2 &model) const;
-
-  static int cmp_ptrs (const void *, const void *);
-
-  region_offset get_offset () const;
-  bool get_byte_size (byte_size_t *out) const;
-  bool get_bit_size (bit_size_t *out) const;
-
-  void
-  get_subregions_for_binding (region_model2_manager *mgr,
-			      bit_offset_t start_bit_offset,
-			      bit_size_t size_in_bits,
-			      tree type,
-			      auto_vec <const region2 *> *out) const;
-
-  bool symbolic_for_unknown_ptr_p () const;
-
-  const complexity &get_complexity () const { return m_complexity; }
-
- protected:
-  region2 (complexity c, unsigned id, const region2 *parent, tree type);
-
-  virtual void add_to_hash (inchash::hash &hstate) const;
-
- private:
-#if 0
-  void become_active_view (region_model2 &model, region_id this_rid);
-  void deactivate_any_active_view (region_model2 &model);
-  void deactivate_view (region_model2 &model, region_id this_view_rid);
-#endif
-  region_offset calc_offset () const;
-
-  complexity m_complexity;
-  unsigned m_id; // purely for deterministic sorting at this stage, for dumps
-  const region2 *m_parent;
-  tree m_type;
-
-  mutable region_offset *m_cached_offset;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const region2 *>::test (const region2 *)
-{
-  return true;
-}
-
-namespace ana {
-
-/* Abstract subclass of region2, for regions that represent an untyped
-   space within memory, such as the stack or the heap.  */
-
-class space_region2 : public region2
-{
-protected:
-  space_region2 (unsigned id, const region2 *parent)
-  : region2 (complexity (parent), id, parent, NULL_TREE)
-  {}
-};
-
-/* Concrete space_region2 subclass, representing a function frame on the stack,
-   to contain the locals.
-   The parent is the stack region; there's also a hierarchy of call-stack
-   prefixes expressed via m_calling_frame.
-   For example, given "oldest" calling "middle" called "newest" we would have
-   - a stack depth of 3
-   - frame (A) for "oldest" with index 0 for depth 1, calling_frame == NULL
-   - frame (B) for "middle" with index 1 for depth 2, calling_frame == (A)
-   - frame (C) for "newest" with index 2 for depth 3, calling_frame == (B)
-   where the parent region for each of the frames is the "stack" region.
-   The index is the count of frames earlier than this in the stack.  */
-
-class frame_region2 : public space_region2
-{
-public:
-  frame_region2 (unsigned id, const region2 *parent,
-		 const frame_region2 *calling_frame,
-		 function *fun, int index)
-  : space_region2 (id, parent), m_calling_frame (calling_frame),
-    m_fun (fun), m_index (index)
-  {}
-  ~frame_region2 ();
-
-  /* region2 vfuncs.  */
-  enum kind get_kind () const FINAL OVERRIDE { return RK_FRAME; }
-  const frame_region2 * dyn_cast_frame_region2 () const FINAL OVERRIDE
-  {
-    return this;
-  }
-  void accept (visitor *v) const FINAL OVERRIDE;
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-#if 0
-  void add_to_hash (inchash::hash &hstate) const FINAL OVERRIDE;
-#endif
-
-  /* Accessors.  */
-  const frame_region2 *get_calling_frame () const { return m_calling_frame; }
-  function *get_function () const { return m_fun; }
-  int get_index () const { return m_index; }
-  int get_stack_depth () const { return m_index + 1; }
-
-  bool compare_fields (const frame_region2 &other) const;
-
-  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;
-  int m_index;
-  typedef hash_map<tree, region2 *> map_t;
-  map_t m_locals;
-  /* TODO: do we want a distinction between params (assumed initialized)
-     vs temporaries?  */
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const frame_region2 *>::test (const region2 *reg)
-{
-  return reg->get_kind () == region2::RK_FRAME;
-}
-
-namespace ana {
-
-/* Concrete space_region2 subclass, to hold global variables (data and bss).  */
-
-class globals_region2 : public space_region2
-{
- public:
-  globals_region2 (unsigned id, const region2 *parent)
-  : space_region2 (id, parent)
-  {}
-
-  /* region2 vfuncs.  */
-  enum kind get_kind () const FINAL OVERRIDE { return RK_GLOBALS; }
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-
-  bool compare_fields (const globals_region2 &other) const;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const globals_region2 *>::test (const region2 *reg)
-{
-  return reg->get_kind () == region2::RK_GLOBALS;
-}
-
-namespace ana {
-
-/* Concrete region2 subclass.  A map_region2 representing the code, using
-   FUNCTION_DECLs for its keys.  */
-
-class code_region2 : public space_region2
-{
-public:
-  code_region2 (unsigned id, const region2 *parent)
-  : space_region2 (id, parent)
-  {}
-
-  /* region2 vfuncs.  */
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-  enum kind get_kind () const FINAL OVERRIDE { return RK_CODE; }
-
-  const region2 *get_element (region_model2 *model,
-			const svalue2 *index,
-			region_model2_context *ctxt);
-
-  bool compare_fields (const code_region2 &other) const;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const code_region2 *>::test (const region2 *reg)
-{
-  return reg->get_kind () == region2::RK_CODE;
-}
-
-namespace ana {
-
-// FIXME:
-/* Concrete region2 subclass.  A region2 representing the code for
-   a particular function, using LABEL_DECLs for its keys.  */
-
-class function_region2 : public region2
-{
-public:
-  function_region2 (unsigned id, const code_region2 *parent, tree fndecl)
-  : region2 (complexity (parent), id, parent, TREE_TYPE (fndecl)),
-    m_fndecl (fndecl)
-  {
-    gcc_assert (FUNC_OR_METHOD_TYPE_P (TREE_TYPE (fndecl)));
-  }
-
-  /* region2 vfuncs.  */
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-  enum kind get_kind () const FINAL OVERRIDE { return RK_FUNCTION; }
-  const function_region2 *
-  dyn_cast_function_region2 () const FINAL OVERRIDE{ return this; }
-
-  tree get_fndecl () const { return m_fndecl; }
-
-  region2 *get_element (region_model2 *model,
-			const svalue2 *index_sid,
-			region_model2_context *ctxt);
-
-  bool compare_fields (const function_region2 &other) const;
-
-private:
-  tree m_fndecl;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const function_region2 *>::test (const region2 *reg)
-{
-  return reg->get_kind () == region2::RK_FUNCTION;
-}
-
-namespace ana {
-
-/* Concrete region2 subclass.  A region2 representing a particular label
-   within a function.  */
-
-class label_region2 : public region2
-{
-public:
-  label_region2 (unsigned id, const function_region2 *parent, tree label)
-  : region2 (complexity (parent), id, parent, NULL_TREE), m_label (label)
-  {
-    gcc_assert (TREE_CODE (label) == LABEL_DECL);
-  }
-
-  /* region2 vfuncs.  */
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-  enum kind get_kind () const FINAL OVERRIDE { return RK_LABEL; }
-
-  //bool compare_fields (const function_region2 &other) const;
-
-private:
-  tree m_label;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const label_region2 *>::test (const region2 *reg)
-{
-  return reg->get_kind () == region2::RK_LABEL;
-}
-
-namespace ana {
-
-/* Concrete region2 subclass representing a stack, containing all stack
-   frames, and implicitly providing a POISON_KIND_UNINIT value to all
-   child region2s by default.  */
-
-class stack_region2 : public region2
-{
-public:
-  stack_region2 (unsigned id, region2 *parent)
-  : region2 (complexity (parent), id, parent, NULL_TREE)
-  {}
-
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-
-  bool compare_fields (const stack_region2 &other) const;
-
-  enum kind get_kind () const FINAL OVERRIDE { return RK_STACK; }
-
-#if 0
-  static bool can_merge_p (const stack_region2 *stack_region2_a,
-			   const stack_region2 *stack_region2_b,
-			   model_merger *merger);
-  const svalue2 *get_value_by_name (tree identifier,
-			      const region_model2 &model) const;
-#endif
-
-#if 0
-  void validate (const region_model2 &model) const FINAL OVERRIDE;
-#endif
- private:
-#if 0
-  void add_to_hash (inchash::hash &hstate) const FINAL OVERRIDE;
-#endif
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const stack_region2 *>::test (const region2 *reg)
-{
-  return reg->get_kind () == region2::RK_STACK;
-}
-
-namespace ana {
-
-/* Concrete region2 subclass: a region2 within which region2s can be
-   dynamically allocated.  */
-
-class heap_region2 : public region2
-{
-public:
-  heap_region2 (unsigned id, region2 *parent)
-  : region2 (complexity (parent), id, parent, NULL_TREE)
-  {}
-
-#if 0
-  bool compare_fields (const heap_region2 &other) const;
-#endif
-
-  enum kind get_kind () const FINAL OVERRIDE { return RK_HEAP; }
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-#if 0
-  static bool can_merge_p (const heap_region2 *heap_a, region2_id heap_a_rid,
-			   const heap_region2 *heap_b, region2_id heap_b_rid,
-			   heap_region2 *merged_heap, region2_id merged_heap_rid,
-			   model_merger *merger);
-#endif
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const heap_region2 *>::test (const region2 *reg)
-{
-  return reg->get_kind () == region2::RK_HEAP;
-}
-
-namespace ana {
-
-/* Concrete region2 subclass.  The root region2, containing all region2s
-   (either directly, or as descendents).
-   Unique within a region_model2.  */
-
-class root_region2 : public region2
-{
-public:
-  root_region2 (unsigned id);
-
-  bool compare_fields (const root_region2 &other) const;
-
-  enum kind get_kind () const FINAL OVERRIDE { return RK_ROOT; }
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-
-#if 0
-  static bool can_merge_p (const root_region2 *root_region2_a,
-			   const root_region2 *root_region2_b,
-			   root_region2 *merged_root_region2,
-			   model_merger *merger);
-#endif
-
-  const svalue2 *get_value_by_name (tree identifier,
-			      const region_model2 &model) const;
-
-#if 0
-  void validate (const region_model2 &model) const FINAL OVERRIDE;
-#endif
-
-private:
-#if 0
-  void add_to_hash (inchash::hash &hstate) const FINAL OVERRIDE;
-#endif
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const root_region2 *>::test (const region2 *reg)
-{
-  return reg->get_kind () == region2::RK_ROOT;
-}
-
-namespace ana {
-
-/* Concrete region2 subclass: a region2 to use when dereferencing an unknown
-   pointer.  */
-
-class symbolic_region2 : public region2
-{
-public:
-  symbolic_region2 (unsigned id, region2 *parent, const svalue2 *sval_ptr)
-  : region2 (complexity::from_pair (parent, sval_ptr), id, parent,
-	     TREE_TYPE (sval_ptr->get_type ())),
-    m_sval_ptr (sval_ptr)
-  {}
-
-  const symbolic_region2 *
-  dyn_cast_symbolic_region2 () const FINAL OVERRIDE { return this; }
-
-  bool compare_fields (const symbolic_region2 &other) const;
-
-  enum kind get_kind () const FINAL OVERRIDE { return region2::RK_SYMBOLIC; }
-  void accept (visitor *v) const FINAL OVERRIDE;
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-
-  const svalue2 *get_pointer () const { return m_sval_ptr; }
-
-private:
-  const svalue2 *m_sval_ptr;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const symbolic_region2 *>::test (const region2 *reg)
-{
-  return reg->get_kind () == region2::RK_SYMBOLIC;
-}
-
-namespace ana {
-
-/* Concrete region2 subclass representing the memory occupied by a
-   variable (whether for a global or a local).  */
-
-class decl_region2 : public region2
-{
-public:
-  decl_region2 (unsigned id, const region2 *parent, tree decl)
-  : region2 (complexity (parent), id, parent, TREE_TYPE (decl)), m_decl (decl)
-  {}
-
-  bool compare_fields (const decl_region2 &other) const;
-
-  enum region2::kind get_kind () const FINAL OVERRIDE { return RK_DECL; }
-  const decl_region2 *
-  dyn_cast_decl_region2 () const FINAL OVERRIDE { return this; }
-
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-
-  tree get_decl () const { return m_decl; }
-  int get_stack_depth () const;
-
-protected:
-  void add_to_hash (inchash::hash &hstate) const OVERRIDE;
-
-private:
-  tree m_decl;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const decl_region2 *>::test (const region2 *reg)
-{
-  return reg->get_kind () == region2::RK_DECL;
-}
-
-namespace ana {
-
-/* Concrete region2 subclass representing the memory occupied by a
-   field within a struct or union.  */
-
-class field_region2 : public region2
-{
-public:
-  field_region2 (unsigned id, const region2 *parent, tree field)
-  : region2 (complexity (parent), id, parent, TREE_TYPE (field)),
-    m_field (field)
-  {}
-
-  bool compare_fields (const field_region2 &other) const;
-
-  enum region2::kind get_kind () const FINAL OVERRIDE { return RK_FIELD; }
-
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-  const field_region2 *
-  dyn_cast_field_region2 () const FINAL OVERRIDE { return this; }
-
-  tree get_field () const { return m_field; }
-
-protected:
-  void add_to_hash (inchash::hash &hstate) const OVERRIDE;
-
-private:
-  tree m_field;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const field_region2 *>::test (const region2 *reg)
-{
-  return reg->get_kind () == region2::RK_FIELD;
-}
-
-namespace ana {
-
-/* An element within an array.  */
-
-class element_region2 : public region2
-{
-public:
-  element_region2 (unsigned id, const region2 *parent, tree element_type,
-		   const svalue2 *index)
-  : region2 (complexity::from_pair (parent, index), id, parent, element_type),
-    m_index (index)
-  {}
-
-  bool compare_fields (const element_region2 &other) const;
-
-  enum region2::kind get_kind () const FINAL OVERRIDE { return RK_ELEMENT; }
-  const element_region2 *
-  dyn_cast_element_region2 () const FINAL OVERRIDE { return this; }
-
-  void accept (visitor *v) const FINAL OVERRIDE;
-
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-
-  const svalue2 *get_index () const { return m_index; }
-
-protected:
-  void add_to_hash (inchash::hash &hstate) const OVERRIDE;
-
-private:
-  const svalue2 *m_index;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const element_region2 *>::test (const region2 *reg)
-{
-  return reg->get_kind () == region2::RK_ELEMENT;
-}
-
-namespace ana {
-
-/* A byte-offset within another region, for handling pointer arithmetic
-   as a region.  */
-
-class offset_region2 : public region2
-{
-public:
-  offset_region2 (unsigned id, const region2 *parent, tree type,
-		  const svalue2 *byte_offset)
-  : region2 (complexity::from_pair (parent, byte_offset), id, parent, type),
-    m_byte_offset (byte_offset)
-  {}
-
-  bool compare_fields (const offset_region2 &other) const;
-
-  enum region2::kind get_kind () const FINAL OVERRIDE { return RK_OFFSET; }
-  const offset_region2 *
-  dyn_cast_offset_region2 () const FINAL OVERRIDE { return this; }
-
-  void accept (visitor *v) const FINAL OVERRIDE;
-
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-
-  const svalue2 *get_byte_offset () const { return m_byte_offset; }
-
-protected:
-  void add_to_hash (inchash::hash &hstate) const OVERRIDE;
-
-private:
-  const svalue2 *m_byte_offset;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const offset_region2 *>::test (const region2 *reg)
-{
-  return reg->get_kind () == region2::RK_OFFSET;
-}
-
-namespace ana {
-
-/* A region that views another region using a different type.  */
-
-class cast_region2 : public region2
-{
-public:
-  cast_region2 (unsigned id, const region2 *original_region, tree type)
-  : region2 (complexity (original_region), id,
-	     original_region->get_parent_region (), type),
-    m_original_region (original_region)
-  {}
-
-  bool compare_fields (const cast_region2 &other) const;
-
-  enum region2::kind get_kind () const FINAL OVERRIDE { return RK_CAST; }
-  const cast_region2 *
-  dyn_cast_cast_region2 () const FINAL OVERRIDE { return this; }
-  void accept (visitor *v) const FINAL OVERRIDE;
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-
-  /* FIXME: should this just be the parent region? */
-  const region2 *get_original_region () const { return m_original_region; }
-
-protected:
-  void add_to_hash (inchash::hash &hstate) const OVERRIDE;
-
-private:
-  const region2 *m_original_region;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const cast_region2 *>::test (const region2 *reg)
-{
-  return reg->get_kind () == region2::RK_CAST;
-}
-
-namespace ana {
-
-/* An untyped region dynamically allocated on the heap via "malloc"
-   or similar.  */
-// FIXME: ...but could be NULL
-
-class heap_allocated_region2 : public region2
-{
-public:
-  heap_allocated_region2 (unsigned id, const region2 *parent)
-  : region2 (complexity (parent), id, parent, NULL_TREE)
-  {}
-
-#if 0
-  bool compare_fields (const element_region2 &other) const;
-#endif
-  enum region2::kind get_kind () const FINAL OVERRIDE { return RK_HEAP_ALLOCATED; }
-
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-
-protected:
-#if 0
-  void add_to_hash (inchash::hash &hstate) const OVERRIDE;
-#endif
-};
-
-/* An untyped region dynamically allocated on the stack via "alloca".  */
-
-class alloca_region2 : public region2
-{
-public:
-  alloca_region2 (unsigned id, const frame_region2 *parent)
-  : region2 (complexity (parent), id, parent, NULL_TREE)
-  {}
-
-#if 0
-  bool compare_fields (const element_region2 &other) const;
-#endif
-  enum region2::kind get_kind () const FINAL OVERRIDE { return RK_ALLOCA; }
-
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-
-protected:
-#if 0
-  void add_to_hash (inchash::hash &hstate) const OVERRIDE;
-#endif
-};
-
-/* A region for a STRING_CST.  */
-
-class string_region2 : public region2
-{
-public:
-  string_region2 (unsigned id, const region2 *parent, tree string_cst)
-  : region2 (complexity (parent), id, parent, TREE_TYPE (string_cst)),
-    m_string_cst (string_cst)
-  {}
-
-  const string_region2 *
-  dyn_cast_string_region2 () const FINAL OVERRIDE { return this; }
-
-#if 0
-  bool compare_fields (const element_region2 &other) const;
-#endif
-  enum region2::kind get_kind () const FINAL OVERRIDE { return RK_STRING; }
-
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-
-  tree get_string_cst () const { return m_string_cst; }
-
-protected:
-#if 0
-  void add_to_hash (inchash::hash &hstate) const OVERRIDE;
-#endif
-private:
-  tree m_string_cst;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const string_region2 *>::test (const region2 *reg)
-{
-  return reg->get_kind () == region2::RK_STRING;
-}
-
-namespace ana {
-
-/* An unknown region, for handling unimplemented tree codes.  */
-
-class unknown_region2 : public region2
-{
-public:
-  unknown_region2 (unsigned id, const region2 *parent, tree type)
-  : region2 (complexity (parent), id, parent, type)
-  {}
-
-#if 0
-  bool compare_fields (const element_region2 &other) const;
-#endif
-  enum region2::kind get_kind () const FINAL OVERRIDE { return RK_UNKNOWN; }
-
-  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-
-protected:
-#if 0
-  void add_to_hash (inchash::hash &hstate) const OVERRIDE;
-#endif
-};
-
-/* A class responsible for owning and consolidating region2 and svalue2
-   instances.
-   region2 and svalue2 instances are immutable as far as clients are
-   concerned, so they are provided as "const" ptrs.  */
-
-class region_model2_manager
-{
-public:
-  region_model2_manager ();
-  ~region_model2_manager ();
-
-  /* svalue2 consolidation.  */
-  const svalue2 *get_or_create_constant_svalue2 (tree cst_expr);
-  const svalue2 *get_or_create_unknown_svalue2 (tree type);
-  const svalue2 *get_or_create_setjmp_svalue2 (const setjmp_record &r,
-					       tree type);
-  const svalue2 *get_or_create_poisoned_svalue2 (enum poison_kind kind,
-						 tree type);
-  const svalue2 *get_or_create_initial_value (const region2 *reg);
-  const svalue2 *get_ptr_svalue2 (tree ptr_type, const region2 *pointee);
-  const svalue2 *get_or_create_unaryop (tree type, enum tree_code op,
-					const svalue2 *arg);
-  const svalue2 *get_or_create_cast (tree type, const svalue2 *arg);
-  const svalue2 *get_or_create_binop (tree type,
-				      enum tree_code op,
-				      const svalue2 *arg0, const svalue2 *arg1);
-  const svalue2 *get_or_create_sub_svalue2 (tree type,
-					    const svalue2 *parent_svalue,
-					    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);
-  const svalue2 *get_or_create_compound_svalue2 (tree type,
-						 const binding_map &map);
-  const svalue2 *get_or_create_conjured_svalue2 (tree type, const gimple *stmt,
-						 const region2 *id_reg);
-
-  /* region2 consolidation.  */
-  const stack_region2 * get_stack_region () const { return &m_stack_region; }
-  const heap_region2 *get_heap_region () const { return &m_heap_region; }
-  const code_region2 *get_code_region () const { return &m_code_region; }
-  const globals_region2 *get_globals_region () const
-  {
-    return &m_globals_region;
-  }
-  const function_region2 *get_region_for_fndecl (tree fndecl);
-  const label_region2 *get_region_for_label (tree label);
-  const decl_region2 *get_region_for_global (tree expr);
-  const region2 *get_field_region (const region2 *parent, tree field);
-  const region2 *get_element_region (const region2 *parent,
-				     tree element_type,
-				     const svalue2 *index);
-  const region2 *get_offset_region (const region2 *parent,
-				    tree type,
-				    const svalue2 *byte_offset);
-  const region2 *get_cast_region (const region2 *original_region,
-				  tree type);
-  const frame_region2 *get_frame_region (const frame_region2 *calling_frame,
-					 function *fun);
-  const region2 *get_symbolic_region (const svalue2 *sval);
-  const string_region2 *get_region_for_string (tree string_cst);
-
-  const region2 *
-  get_region2_for_unexpected_tree_code (region_model2_context *ctxt,
-					tree t,
-					const dump_location_t &loc);
-
-  unsigned alloc_region_id () { return m_next_region_id++; }
-
-  store2_manager *get_store2_manager () { return &m_store2_mgr; }
-
-  /* Dynamically-allocated region2 instances.
-     FIXME: for now, the number of these within the analysis can grow
-     arbitrarily.  They are still owned by the manager.  */
-  const region2 *create_region_for_heap_alloc ();
-  const region2 *create_region_for_alloca (const frame_region2 *frame);
-
-  void log_stats (logger *logger, bool show_objs) const;
-
-private:
-  bool too_complex_p (const complexity &c) const;
-  bool reject_if_too_complex (svalue2 *sval);
-
-  const svalue2 *maybe_fold_unaryop (tree type, enum tree_code op,
-				     const svalue2 *arg);
-  const svalue2 *maybe_fold_binop (tree type, enum tree_code op,
-				   const svalue2 *arg0, const svalue2 *arg1);
-  const svalue2 *maybe_fold_sub_svalue2 (tree type,
-					 const svalue2 *parent_svalue,
-					 const region2 *subregion);
-
-  unsigned m_next_region_id;
-  root_region2 m_root_region;
-  stack_region2 m_stack_region;
-  heap_region2 m_heap_region;
-
-  /* svalue2 consolidation.  */
-  typedef hash_map<tree, constant_svalue2 *> constants_map_t;
-  constants_map_t m_constants_map;
-
-  typedef hash_map<tree, unknown_svalue2 *> unknowns_map_t;
-  unknowns_map_t m_unknowns_map;
-  const unknown_svalue2 *m_unknown_NULL;
-
-  typedef hash_map<poisoned_svalue2::key_t,
-		   poisoned_svalue2 *> poisoned_values_map_t;
-  poisoned_values_map_t m_poisoned_values_map;
-
-  typedef hash_map<setjmp_svalue2::key_t,
-		   setjmp_svalue2 *> setjmp_values_map_t;
-  setjmp_values_map_t m_setjmp_values_map;
-
-  typedef hash_map<const region2 *, initial_svalue2 *> initial_values_map_t;
-  initial_values_map_t m_initial_values_map;
-
-  typedef hash_map<const region2 *, region_svalue2 *> pointer_values_map_t;
-  pointer_values_map_t m_pointer_values_map;
-
-  typedef hash_map<unaryop_svalue2::key_t,
-		   unaryop_svalue2 *> unaryop_values_map_t;
-  unaryop_values_map_t m_unaryop_values_map;
-
-  typedef hash_map<binop_svalue2::key_t, binop_svalue2 *> binop_values_map_t;
-  binop_values_map_t m_binop_values_map;
-
-  typedef hash_map<sub_svalue2::key_t, sub_svalue2 *> sub_values_map_t;
-  sub_values_map_t m_sub_values_map;
-
-  typedef hash_map<const svalue2 *,
-		   unmergeable_svalue2 *> unmergeable_values_map_t;
-  unmergeable_values_map_t m_unmergeable_values_map;
-
-  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;
-
-  typedef hash_map<compound_svalue2::key_t,
-		   compound_svalue2 *> compound_values_map_t;
-  compound_values_map_t m_compound_values_map;
-
-  typedef hash_map<conjured_svalue2::key_t,
-		   conjured_svalue2 *> conjured_values_map_t;
-  conjured_values_map_t m_conjured_values_map;
-
-  /* Maximum complexity of svalues that weren't rejected.  */
-  complexity m_max_complexity;
-
-  /* region2 consolidation.  */
-
-  code_region2 m_code_region;
-  typedef hash_map<tree, function_region2 *> fndecls_map_t;
-  typedef fndecls_map_t::iterator fndecls_iterator_t;
-  fndecls_map_t m_fndecls_map;
-
-  typedef hash_map<tree, label_region2 *> labels_map_t;
-  typedef labels_map_t::iterator labels_iterator_t;
-  labels_map_t m_labels_map;
-
-  globals_region2 m_globals_region;
-  typedef hash_map<tree, decl_region2 *> globals_map_t;
-  typedef globals_map_t::iterator globals_iterator_t;
-  globals_map_t m_globals_map;
-
-  uniq_manager<region2> m_fields;
-#if 0
-  struct field_key_t { region2 *m_parent; tree m_field; };
-  typedef hash_map<field_key_t, region2 *> fields_map_t;
-  fields_map_t m_fields_map;
-#endif
-  uniq_manager<region2> m_elements;
-  uniq_manager<region2> m_offset_regions;
-  uniq_manager<region2> m_cast_regions;
-  uniq_manager<frame_region2> m_frames;
-  uniq_manager<region2> m_symbolic_regions;
-
-  typedef hash_map<tree, string_region2 *> string_map_t;
-  string_map_t m_string_map;
-
-  store2_manager m_store2_mgr;
-
-  /* "Dynamically-allocated" region2 instances.
-     FIXME: for now, the number of these within the analysis can grow
-     arbitrarily.  They are still owned by the manager.  */
-  auto_delete_vec<region2> m_managed_dynamic_regions;
-};
-
-struct append_ssa_names_cb_data;
-
-/* Helper class for handling calls to functions with known behavior.
-   Implemented in region-model2-impl-calls.c.  */
-
-class call_details
-{
-public:
-  call_details (const gcall *call, region_model2 *model,
-		region_model2_context *ctxt);
-
-  region_model2_context *get_ctxt () const { return m_ctxt; }
-  tree get_lhs_type () const { return m_lhs_type; }
-  const region2 *get_lhs_region () const { return m_lhs_region; }
-
-  bool maybe_set_lhs (const svalue2 *result) const;
-
-  tree get_arg_tree (unsigned idx) const;
-  const svalue2 *get_arg_svalue2 (unsigned idx) const;
-
-  void dump_to_pp (pretty_printer *pp, bool simple) const;
-  void dump (bool simple) const;
-
-private:
-  const gcall *m_call;
-  region_model2 *m_model;
-  region_model2_context *m_ctxt;
-  tree m_lhs_type;
-  const region2 *m_lhs_region;
-};
-
-/* A region_model2 encapsulates a representation of the state of memory, with
-   a tree of region2s, along with their associated values.
-   The representation is graph-like because values can be pointers to
-   region2s.
-   It also stores a constraint_manager, capturing relationships between
-   the values.  */
-
-class region_model2
-{
- public:
-  region_model2 (region_model2_manager *mgr);
-  region_model2 (const region_model2 &other);
-  ~region_model2 ();
-
-#if 0//__cplusplus >= 201103
-  region_model2 (region_model2 &&other);
-#endif
-
-  region_model2 &operator= (const region_model2 &other);
-
-  bool operator== (const region_model2 &other) const;
-  bool operator!= (const region_model2 &other) const
-  {
-    return !(*this == other);
-  }
-
-  hashval_t hash () const;
-
-  void print (pretty_printer *pp) const;
-
-  void dump_dot_to_pp (pretty_printer *pp) const;
-  void dump_dot_to_file (FILE *fp) const;
-  void dump_dot (const char *path) const;
-
-  void dump_to_pp (pretty_printer *pp, bool simple, bool multiline) const;
-  void dump (FILE *fp, bool simple, bool multiline) const;
-  void dump (bool simple) const;
-  void summarize_to_pp (pretty_printer *pp, bool simple) const;
-  void summarize (bool simple) const;
-
-  void debug () const;
-
-  void validate () const;
-
-  bool on_stmt (const gimple *stmt,
-		region_model2_context *ctxt);
-  bool on_gassign (const gassign *stmt,
-		   region_model2_context *ctxt);
-  const svalue2 * get_gassign_result (const gassign *assign,
-				      region_model2_context *ctxt);
-#if 0
-  bool on_gcall (const supernode *snode,
-		 const gcall *stmt);
-#else
-  bool on_call_pre (const gcall *stmt, region_model2_context *ctxt);
-  void on_call_post (const gcall *stmt,
-		     bool unknown_side_effects,
-		     region_model2_context *ctxt);
-
-  /* Specific handling for on_call_pre.  */
-  bool impl_call_alloca (const call_details &cd);
-  void impl_call_analyzer_describe (const gcall *call,
-				    region_model2_context *ctxt);
-  void impl_call_analyzer_eval (const gcall *call,
-				region_model2_context *ctxt);
-  bool impl_call_builtin_expect (const call_details &cd);
-  bool impl_call_calloc (const call_details &cd);
-  void impl_call_free (const call_details &cd);
-  bool impl_call_malloc (const call_details &cd);
-  bool impl_call_memset (const call_details &cd);
-  bool impl_call_strlen (const call_details &cd);
-#endif
-  void handle_unrecognized_call (const gcall *call,
-				 region_model2_context *ctxt);
-  void get_reachable_svalues (svalue2_set *out,
-			      const svalue2 *extra_sval);
-  bool on_greturn (const greturn *stmt,
-		   region_model2_context *ctxt);
-  void on_setjmp (const gcall *stmt, const exploded_node *enode,
-		  region_model2_context *ctxt);
-  void on_longjmp (const gcall *longjmp_call, const gcall *setjmp_call,
-		   int setjmp_stack_depth, region_model2_context *ctxt);
-
-  void update_for_phis (const supernode *snode,
-			const cfg_superedge *last_cfg_superedge,
-			region_model2_context *ctxt);
-
-  void handle_phi (const gphi *phi,
-		   tree lhs, tree rhs, bool is_back_edge,
-		   region_model2_context *ctxt);
-
-  bool maybe_update_for_edge (const superedge &edge,
-			      const gimple *last_stmt,
-			      region_model2_context *ctxt);
-
-  const region2 *push_frame (function *fun, vec<const svalue2 *> *arg_sids,
-			     region_model2_context *ctxt);
-  const frame_region2 *get_current_frame () const { return m_current_frame; }
-  function * get_current_function () const;
-  void pop_frame (const region2 *result_dst,
-		  bool purge, purge_stats2 *stats,
-		  const svalue2 **out_result,
-		  region_model2_context *ctxt);
-  int get_stack_depth () const;
-  const frame_region2 *get_frame_at_index (int index) const;
-
-#if 0
-  void replace_svalue2 (svalue2_id sid, const svalue2 *new_sval);
-#endif
-
-  const region2 *get_lvalue (path_var pv, region_model2_context *ctxt);
-  const region2 *get_lvalue (tree expr, region_model2_context *ctxt);
-  const svalue2 *get_rvalue (path_var pv, region_model2_context *ctxt);
-  const svalue2 *get_rvalue (tree expr, region_model2_context *ctxt);
-
-#if 0
-  svalue2_id maybe_cast (tree type, svalue2_id sid, region_model2_context *ctxt);
-  svalue2_id maybe_cast_1 (tree type, svalue2_id sid);
-
-  region2_id get_field_region2 (region2_id rid, tree field,
-			      region_model2_context *ctxt);
-#endif
-
-  const region2 *deref_rvalue (const svalue2 *ptr_sval, tree ptr_tree,
-			       region_model2_context *ctxt);
-#if 0
-  const region2 *deref_rvalue (tree ptr, region_model2_context *ctxt);
-#endif
-
-  void set_value (const region2 *lhs_reg, const svalue2 *rhs_sval,
-		  region_model2_context *ctxt);
-  void set_value (tree lhs, tree rhs, region_model2_context *ctxt);
-  void clobber_region (const region2 *reg);
-  void purge_region (const region2 *reg);
-  void zero_fill_region (const region2 *reg);
-  void mark_region_as_unknown (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,
-			   enum tree_code op,
-			   const svalue2 *rhs) const;
-  tristate eval_condition_without_cm (const svalue2 *lhs,
-				      enum tree_code op,
-				      const svalue2 *rhs) const;
-  tristate compare_initial_and_pointer (const initial_svalue2 *init,
-					const region_svalue2 *ptr) const;
-  tristate eval_condition (tree lhs,
-			   enum tree_code op,
-			   tree rhs,
-			   region_model2_context *ctxt);
-  bool add_constraint (tree lhs, enum tree_code op, tree rhs,
-		       region_model2_context *ctxt);
-#if 0
-  region2_id add_new_malloc_region2 ();
-#endif
-  const region2 *create_region_for_heap_alloc (const svalue2 *size_in_bytes);
-  const region2 *create_region_for_alloca (const svalue2 *size_in_bytes);
-
-  tree get_representative_tree (const svalue2 *sval) const;
-#if 0
-  void get_path_vars_for_svalue2 (const svalue2 *sval,
-				  auto_vec<path_var> *out) const;
-#endif
-  path_var
-  get_representative_path_var (const svalue2 *sval,
-			       hash_set <const svalue2 *> *visited) const;
-  path_var
-  get_representative_path_var (const region2 *reg,
-			       hash_set <const svalue2 *> *visited) const;
-
-#if 0
-  void purge_unused_svalue2s (purge_stats *out,
-			     region_model2_context *ctxt,
-			     svalue2_id_set *known_used_sids = NULL);
-  void purge_region2s (const region2_id_set &set,
-		      purge_stats *stats,
-		      logger *logger);
-#endif
-  /* For selftests.  */
-  constraint_manager2 *get_constraints2 ()
-  {
-    return m_constraints2;
-  }
-
-  store2 *get_store () { return &m_store; }
-  const store2 *get_store () const { return &m_store; }
-
-  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;
-#endif
-
-  void unbind_region2_and_descendents (const region2 *reg,
-				       enum poison_kind pkind,
-				       purge_stats2 *stats,
-				       logger *logger);
-
-  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;
-
-  svalue2_id convert_byte_offset_to_array_index (tree ptr_type,
-						svalue2_id offset_sid);
-
-  region2_id get_or_create_mem_ref (tree type,
-				   svalue2_id ptr_sid,
-				   svalue2_id offset_sid,
-				   region_model2_context *ctxt);
-  region2_id get_or_create_pointer_plus_expr (tree type,
-					     svalue2_id ptr_sid,
-					     svalue2_id offset_sid,
-					     region_model2_context *ctxt);
-  region2_id get_or_create_view (region2_id raw_rid, tree type,
-				region_model2_context *ctxt);
-#endif
-  tree get_fndecl_for_call (const gcall *call,
-			    region_model2_context *ctxt);
-
-  void get_ssa_name_regions_for_current_frame
-    (auto_vec<const decl_region2 *> *out) const;
-  static void append_ssa_names_cb (const region2 *base_reg,
-				   struct append_ssa_names_cb_data *data);
-
-  const svalue2 *get_store_value (const region2 *reg) const;
-
-  bool region_exists_p (const region2 *reg) const;
-
-  void canonicalize ();
-  bool canonicalized_p () const;
-
-  void loop_replay_hack (const region_model2 *dst_state);
-
-private:
-  const region2 *get_lvalue_1 (path_var pv,
-			       region_model2_context *ctxt);
-  const svalue2 *get_rvalue_1 (path_var pv,
-			       region_model2_context *ctxt);
-
-#if 0
-  void copy_struct_region2 (region2_id dst_rid, struct_region2 *dst_reg,
-			   struct_region2 *src_reg, region_model2_context *ctxt);
-  void copy_union_region2 (region2_id dst_rid, union_region2 *src_reg,
-			  region_model2_context *ctxt);
-  void copy_array_region2 (region2_id dst_rid, array_region2 *dst_reg,
-			  array_region2 *src_reg, region_model2_context *ctxt);
-#endif
-  void add_any_constraints_from_ssa_def_stmt (tree lhs,
-					      enum tree_code op,
-					      tree rhs,
-					      region_model2_context *ctxt);
-  void add_any_constraints_from_gassign (enum tree_code op,
-					 tree rhs,
-					 const gassign *assign,
-					 region_model2_context *ctxt);
-  void add_any_constraints_from_gcall (enum tree_code op,
-				       tree rhs,
-				       const gcall *call,
-				       region_model2_context *ctxt);
-
-  void update_for_call_superedge (const call_superedge &call_edge,
-				  region_model2_context *ctxt);
-  void update_for_return_superedge (const return_superedge &return_edge,
-				    region_model2_context *ctxt);
-  void update_for_call_summary (const callgraph_superedge &cg_sedge,
-				region_model2_context *ctxt);
-  bool apply_constraints_for_gcond (const cfg_superedge &edge,
-				    const gcond *cond_stmt,
-				    region_model2_context *ctxt);
-  bool apply_constraints_for_gswitch (const switch_cfg_superedge &edge,
-				      const gswitch *switch_stmt,
-				      region_model2_context *ctxt);
-
-  int poison_any_pointers_to_descendents (const region2 *reg,
-					  enum poison_kind pkind);
-
-#if 0
-  void dump_summary_of_rep_path_vars (pretty_printer *pp,
-				      auto_vec<path_var> *rep_path_vars,
-				      bool *is_first);
-#endif
-  void on_top_level_param (tree param, region_model2_context *ctxt);
-
-  void record_dynamic_extents (const region2 *reg,
-			       const svalue2 *size_in_bytes);
-
-  /* Storing this here to avoid passing it around everywhere.  */
-  region_model2_manager *const m_mgr;
-
-  store2 m_store;
-
-  constraint_manager2 *m_constraints2; // TODO: embed, rather than dynalloc?
-
-  const frame_region2 *m_current_frame;
-};
-
-/* Some region_model2 activity could lead to warnings (e.g. attempts to use an
-   uninitialized value).  This abstract base class encapsulates an interface
-   for the region2 model to use when emitting such warnings.
-
-   It also provides an interface for being notified about svalue2_ids being
-   remapped, and being deleted.
-
-   Having this as an abstract base class allows us to support the various
-   operations needed by program_state in the analyzer within region_model2,
-   whilst keeping them somewhat modularized.  */
-
-class region_model2_context
-{
- public:
-  virtual void warn (pending_diagnostic *d) = 0;
-
-#if 0
-  /* Return true if if's OK to purge SID when simplifying state.
-     Subclasses can return false for values that have sm state,
-     to avoid generating "leak" false positives.  */
-  virtual bool can_purge_p (svalue2_id sid) = 0;
-#endif
-
-  // FIXME:
-  /* Hook for clients to be notified when a range of SIDs have
-     been purged, so that they can purge state relating to those
-     values (and potentially emit warnings about leaks).
-     All SIDs from FIRST_PURGED_SID numerically upwards are being
-     purged.
-     The return values is a count of how many items of data the client
-     has purged (potentially for use in selftests).
-     MAP has already been applied to the IDs, but is provided in case
-     the client needs to figure out the old IDs.  */
-  virtual void on_svalue2_leak (const svalue2 *sval) = 0;
-
-  /* Hook for clients to be notified when the set of explicitly live
-     svalue2s changes, so that they can purge state relating to dead
-     svalues.  */
-  virtual void
-  on_liveness_change (const svalue2_set &live_svalues,
-		      const region_model2 *model) = 0;
-
-  virtual logger *get_logger () = 0;
-
-#if 0
-  /* Hook for clients to be notified when CHILD_SID is created
-     from PARENT_SID, when "inheriting" a value for a region2 from a
-     parent region2.
-     This exists so that state machines that inherit state can
-     propagate the state from parent to child.  */
-  virtual void on_inherited_svalue2 (svalue2_id parent_sid,
-				    svalue2_id child_sid) = 0;
-
-  /* Hook for clients to be notified when DST_SID is created
-     (or reused) as a cast from SRC_SID.
-     This exists so that state machines can propagate the state
-     from SRC_SID to DST_SID.  */
-  virtual void on_cast (svalue2_id src_sid,
-			svalue2_id dst_sid) = 0;
-#endif
-  /* Hook for clients to be notified when the condition
-     "LHS OP RHS" is added to the region2 model.
-     This exists so that state machines can detect tests on edges,
-     and use them to trigger sm-state transitions (e.g. transitions due
-     to ptrs becoming known to be NULL or non-NULL, rather than just
-     "unchecked") */
-  virtual void on_condition (tree lhs, enum tree_code op, tree rhs) = 0;
-
-  // FIXME:
-  /* Hooks for clients to be notified when an unknown change happens
-     to BASE_REG (in response to a call to an unknown function).  */
-  virtual void on_unknown_change (const svalue2 *sval, bool is_mutable) = 0;
-
-  /* Hooks for clients to be notified when a phi node is handled,
-     where RHS is the pertinent argument.  */
-  virtual void on_phi (const gphi *phi, tree rhs) = 0;
-
-  /* Hooks for clients to be notified when the region2 model doesn't
-     know how to handle the tree code of T at LOC.  */
-  virtual void on_unexpected_tree_code (tree t,
-					const dump_location_t &loc) = 0;
-};
-
-/* A "do nothing" subclass of region_model2_context.  */
-
-class noop_region_model2_context : public region_model2_context
-{
-public:
-  void warn (pending_diagnostic *) OVERRIDE {}
-  void on_svalue2_leak (const svalue2 *) OVERRIDE {}
-  void on_liveness_change (const svalue2_set &,
-			   const region_model2 *) OVERRIDE {}
-  logger *get_logger () OVERRIDE { return NULL; }
-#if 0
-  void on_inherited_svalue2 (svalue2_id parent_sid ATTRIBUTE_UNUSED,
-			    svalue2_id child_sid  ATTRIBUTE_UNUSED)
-    OVERRIDE
-  {
-  }
-  void on_cast (svalue2_id src_sid ATTRIBUTE_UNUSED,
-		svalue2_id dst_sid ATTRIBUTE_UNUSED) OVERRIDE
-  {
-  }
-#endif
-  void on_condition (tree lhs ATTRIBUTE_UNUSED,
-		     enum tree_code op ATTRIBUTE_UNUSED,
-		     tree rhs ATTRIBUTE_UNUSED) OVERRIDE
-  {
-  }
-  void on_unknown_change (const svalue2 *sval ATTRIBUTE_UNUSED,
-			  bool is_mutable ATTRIBUTE_UNUSED) OVERRIDE
-  {
-  }
-  void on_phi (const gphi *phi ATTRIBUTE_UNUSED,
-	       tree rhs ATTRIBUTE_UNUSED) OVERRIDE
-  {
-  }
-  void on_unexpected_tree_code (tree, const dump_location_t &) OVERRIDE {}
-};
-
-/* A subclass of region_model2_context for determining if operations fail
-   e.g. "can we generate a region2 for the lvalue of EXPR?".  */
-
-class tentative_region_model2_context : public noop_region_model2_context
-{
-public:
-  tentative_region_model2_context () : m_num_unexpected_codes (0) {}
-
-  void on_unexpected_tree_code (tree, const dump_location_t &)
-    FINAL OVERRIDE
-  {
-    m_num_unexpected_codes++;
-  }
-
-  bool had_errors_p () const { return m_num_unexpected_codes > 0; }
-
-private:
-  int m_num_unexpected_codes;
-};
-
-/* A bundle of data for use when attempting to merge two region_model2
-   instances to make a third.  */
-
-struct model2_merger
-{
-  model2_merger (const region_model2 *model_a,
-		 const region_model2 *model_b,
-		 const program_point &point,
-		 region_model2 *merged_model)
-  : m_model_a (model_a), m_model_b (model_b),
-    m_point (point),
-    m_merged_model (merged_model)
-  {
-  }
-
-  void dump_to_pp (pretty_printer *pp, bool simple) const;
-  void dump (FILE *fp, bool simple) const;
-  void dump (bool simple) const;
-
-#if ITERATED_BINOPS
-  void add_iterating_widening_binop_thing (const binop_svalue2 *binop_sval);
-#endif
-  region_model2_manager *get_manager () const
-  {
-    return m_model_a->get_manager ();
-  }
-
-#if 0
-  template <typename Subclass>
-  Subclass *get_region2_a (region2_id rid_a) const
-  {
-    return m_model_a->get_region2 <Subclass> (rid_a);
-  }
-
-  template <typename Subclass>
-  Subclass *get_region2_b (region2_id rid_b) const
-  {
-    return m_model_b->get_region2 <Subclass> (rid_b);
-  }
-
-  bool can_merge_values_p (svalue2_id sid_a,
-			   svalue2_id sid_b,
-			   svalue2_id *merged_sid);
-
-  void record_region2s (region2_id a_rid,
-		       region2_id b_rid,
-		       region2_id merged_rid);
-
-  void record_svalue2s (svalue2_id a_sid,
-		       svalue2_id b_sid,
-		       svalue2_id merged_sid);
-#endif
-
-  const region_model2 *m_model_a;
-  const region_model2 *m_model_b;
-  const program_point &m_point;
-  region_model2 *m_merged_model;
-#if ITERATED_BINOPS
-  /* FIXME: comment for this.  */
-  auto_vec<const binop_svalue2 *> m_iterated_binops;
-#endif
-};
-
-/* FIXME.  */
-
-class engine2
-{
-public:
-  region_model2_manager *get_model_manager () { return &m_mgr; }
-
-  void log_stats (logger *logger) const;
-
-private:
-  region_model2_manager m_mgr;
-
-};
-
-} // namespace ana
-
-extern void debug (const region_model2 &rmodel);
-
-namespace ana {
-
-#if CHECKING_P
-
-namespace selftest {
-
-using namespace ::selftest;
-
-/* An implementation of region_model2_context for use in selftests, which
-   stores any pending_diagnostic instances passed to it.  */
-
-class test_region_model2_context : public noop_region_model2_context
-{
-public:
-  void warn (pending_diagnostic *d) FINAL OVERRIDE
-  {
-    m_diagnostics.safe_push (d);
-  }
-
-  unsigned get_num_diagnostics () const { return m_diagnostics.length (); }
-
-  void on_unexpected_tree_code (tree t, const dump_location_t &)
-    FINAL OVERRIDE
-  {
-    internal_error ("unhandled tree code: %qs",
-		    get_tree_code_name (TREE_CODE (t)));
-  }
-
-private:
-  /* Implicitly delete any diagnostics in the dtor.  */
-  auto_delete_vec<pending_diagnostic> m_diagnostics;
-};
-
-/* Attempt to add the constraint (LHS OP RHS) to MODEL.
-   Verify that MODEL remains satisfiable.  */
-
-#define ADD_SAT_CONSTRAINT(MODEL, LHS, OP, RHS)	\
-  SELFTEST_BEGIN_STMT					\
-    bool sat = (MODEL).add_constraint (LHS, OP, RHS, NULL);	\
-    ASSERT_TRUE (sat);					\
-  SELFTEST_END_STMT
-
-/* Attempt to add the constraint (LHS OP RHS) to MODEL.
-   Verify that the result is not satisfiable.  */
-
-#define ADD_UNSAT_CONSTRAINT(MODEL, LHS, OP, RHS)	\
-  SELFTEST_BEGIN_STMT					\
-    bool sat = (MODEL).add_constraint (LHS, OP, RHS, NULL);	\
-    ASSERT_FALSE (sat);				\
-  SELFTEST_END_STMT
-
-/* Implementation detail of the ASSERT_CONDITION_*2 macros.  */
-
-void assert_condition2 (const location &loc,
-			region_model2 &model,
-			const svalue2 *lhs, tree_code op, const svalue2 *rhs,
-			tristate expected);
-
-void assert_condition2 (const location &loc,
-			region_model2 &model,
-			tree lhs, tree_code op, tree rhs,
-			tristate expected);
-
-/* Assert that REGION_MODEL2 evaluates the condition "LHS OP RHS"
-   as "true".  */
-
-#define ASSERT_CONDITION_TRUE2(REGION_MODEL2, LHS, OP, RHS)		\
-  SELFTEST_BEGIN_STMT							\
-  assert_condition2 (SELFTEST_LOCATION, REGION_MODEL2, LHS, OP, RHS,	\
-		     tristate (tristate::TS_TRUE));			\
-  SELFTEST_END_STMT
-
-/* Assert that REGION_MODEL2 evaluates the condition "LHS OP RHS"
-   as "false".  */
-
-#define ASSERT_CONDITION_FALSE2(REGION_MODEL2, LHS, OP, RHS) \
-  SELFTEST_BEGIN_STMT							\
-  assert_condition2 (SELFTEST_LOCATION, REGION_MODEL2, LHS, OP, RHS,	\
-		     tristate (tristate::TS_FALSE));			\
-  SELFTEST_END_STMT
-
-/* Assert that REGION_MODEL2 evaluates the condition "LHS OP RHS"
-   as "unknown".  */
-
-#define ASSERT_CONDITION_UNKNOWN2(REGION_MODEL2, LHS, OP, RHS) \
-  SELFTEST_BEGIN_STMT							\
-  assert_condition2 (SELFTEST_LOCATION, REGION_MODEL2, LHS, OP, RHS,	\
-		     tristate (tristate::TS_UNKNOWN));			\
-  SELFTEST_END_STMT
-
-} /* end of namespace selftest.  */
-
-#endif /* #if CHECKING_P */
-
-} // namespace ana
-
-#endif /* GCC_ANALYZER_REGION_MODEL_2_H */
diff --git a/gcc/analyzer/sm-file.cc b/gcc/analyzer/sm-file.cc
index a41cd1b5b94..85fbe84999e 100644
--- a/gcc/analyzer/sm-file.cc
+++ b/gcc/analyzer/sm-file.cc
@@ -38,7 +38,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "analyzer/analyzer-selftests.h"
 #include "tristate.h"
 #include "selftest.h"
-#include "analyzer/region-model2.h"
+#include "analyzer/region-model.h"
 
 #if ENABLE_ANALYZER
 
diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc
index 56fb107c39d..ca51da7f2ab 100644
--- a/gcc/analyzer/sm-malloc.cc
+++ b/gcc/analyzer/sm-malloc.cc
@@ -37,7 +37,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "analyzer/pending-diagnostic.h"
 #include "tristate.h"
 #include "selftest.h"
-#include "analyzer/region-model2.h"
+#include "analyzer/region-model.h"
 
 #if ENABLE_ANALYZER
 
diff --git a/gcc/analyzer/store2.cc b/gcc/analyzer/store2.cc
index c9368f4774d..490bef4d774 100644
--- a/gcc/analyzer/store2.cc
+++ b/gcc/analyzer/store2.cc
@@ -51,8 +51,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "digraph.h"
 #include "analyzer/supergraph.h"
 #include "sbitmap.h"
-#include "analyzer/region-model2.h"
-//#include "analyzer/constraint-manager.h"
+#include "analyzer/region-model.h"
 #include "diagnostic-event-id.h"
 #include "analyzer/sm.h"
 #include "diagnostic-event-id.h"
-- 
2.26.2

