From 1fbd1e28663e4da16a1598fd97e37239c647320f Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 14 May 2019 11:38:18 -0400
Subject: [PATCH 51/54] FIXME: more on better memory model

---
 gcc/analyzer/digraph.cc                            |   2 +
 gcc/analyzer/impl-4.cc                             |  31 +-
 gcc/analyzer/region-model.cc                       | 878 +++++++++++++++++++--
 gcc/analyzer/region-model.h                        | 262 +++++-
 .../NOT_FOR_UPSTREAM-clang-paper-example-1.c       |   5 +-
 gcc/testsuite/gcc.dg/analyzer/data-model-1.c       |  45 +-
 6 files changed, 1123 insertions(+), 100 deletions(-)

diff --git a/gcc/analyzer/digraph.cc b/gcc/analyzer/digraph.cc
index d03fd6b..f7c1ce1 100644
--- a/gcc/analyzer/digraph.cc
+++ b/gcc/analyzer/digraph.cc
@@ -49,6 +49,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "graphviz.h"
 #include "cgraph.h"
 #include "digraph.h"
+#include "tree-diagnostic.h"
 
 void
 digraph::dump_dot_to_pp (pretty_printer *pp, cluster *root_cluster) const
@@ -106,6 +107,7 @@ void
 digraph::dump_dot_to_file (FILE *fp, cluster *root_cluster) const
 {
   pretty_printer pp;
+  pp_format_decoder (&pp) = default_tree_printer;
   pp.buffer->stream = fp;
   dump_dot_to_pp (&pp, root_cluster);
   pp_flush (&pp);
diff --git a/gcc/analyzer/impl-4.cc b/gcc/analyzer/impl-4.cc
index 0fadd06..5266f84 100644
--- a/gcc/analyzer/impl-4.cc
+++ b/gcc/analyzer/impl-4.cc
@@ -50,6 +50,7 @@
 #include "tree-phinodes.h"
 #include "ssa-iterators.h"
 #include "region-model.h"
+#include "tree-diagnostic.h"
 
 /* Overview:
 
@@ -758,6 +759,11 @@ class exploded_node : public dnode
 		      const program_state &old_state,
 		      program_state *new_state) const;
 
+  void on_assignment_for_region_model (const exploded_graph &eg,
+				       const gassign *stmt,
+				       const program_state &old_state,
+				       program_state *new_state) const;
+
   void copy_sm_state (const exploded_graph &eg,
 		      path_var lhs,
 		      path_var rhs,
@@ -2318,6 +2324,7 @@ std::string
 program_state::to_str (const extrinsic_state &ext_state) const
 {
   pretty_printer pp;
+  pp_format_decoder (&pp) = default_tree_printer;
   print (ext_state, &pp);
   return std::string (pp_formatted_text (&pp));
 }
@@ -2686,6 +2693,9 @@ exploded_node::on_stmt (exploded_graph &eg) const
 	  }
 	  break;
 	}
+
+      /* TODO: working on having region model handle all assignments.  */
+      on_assignment_for_region_model (eg, assign, old_state, &new_state);
     }
 
   if (const greturn *return_ = dyn_cast <const greturn *> (stmt))
@@ -2717,7 +2727,11 @@ exploded_node::on_stmt (exploded_graph &eg) const
 	     failures.  */
 	  tree t_arg = gimple_call_arg (call, 0);
 	  tristate t
-	    = new_ps->dump_condition (t_arg, NE_EXPR, integer_zero_node);
+	    = (new_state.m_region_model
+	       ? new_state.m_region_model->eval_condition (t_arg,
+							   NE_EXPR,
+							   integer_zero_node)
+	       : new_ps->dump_condition (t_arg, NE_EXPR, integer_zero_node));
 	  warning_at (call->location, 0, "%s", t.as_string ());
 	}
     }
@@ -2748,12 +2762,21 @@ exploded_node::on_assignment (const exploded_graph &eg,
 
   new_state->m_path_state.on_assignment (lhs, rhs);
 
-  if (new_state->m_region_model)
-    new_state->m_region_model->on_assignment (lhs.m_tree, rhs.m_tree);
-
   copy_sm_state (eg, lhs, rhs, old_state, new_state);
 }
 
+/* FIXME.  */
+
+void
+exploded_node::on_assignment_for_region_model (const exploded_graph &eg,
+					       const gassign *stmt,
+					       const program_state &old_state,
+					       program_state *new_state) const
+{
+  if (new_state->m_region_model)
+    new_state->m_region_model->on_assignment (stmt);
+}
+
 /* Copy SM state (if any) from RHS to LHS.  */
 
 void
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 8de5625..7acbc10 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -41,47 +41,263 @@
 #include "constraints.h"
 #include "supergraph.h"
 #include "region-model.h"
+#include "tree-diagnostic.h"
+
+////////////////////////////////////////////////////////////////////////////
+
+void
+svalue_id::print (pretty_printer *pp) const
+{
+  if (null_p ())
+    pp_printf (pp, "null");
+  else
+    pp_printf (pp, "sv%i", m_idx);
+}
 
 void
 svalue_id::dump_node_name_to_pp (pretty_printer *pp) const
 {
+  gcc_assert (!null_p ());
   pp_printf (pp, "svalue_%i", m_idx);
 }
 
 void
+region_id::print (pretty_printer *pp) const
+{
+  if (null_p ())
+    pp_printf (pp, "null");
+  else
+    pp_printf (pp, "r%i", m_idx);
+}
+
+void
 region_id::dump_node_name_to_pp (pretty_printer *pp) const
 {
+  gcc_assert (!null_p ());
   pp_printf (pp, "region_%i", m_idx);
 }
 
+////////////////////////////////////////////////////////////////////////////
+
+/* class svalue.  */
+
+void
+svalue::dump_dot_to_pp (const region_model &model,
+			svalue_id this_sid,
+			pretty_printer *pp) const
+{
+  this_sid.dump_node_name_to_pp (pp);
+  pp_printf (pp, " [label=\"");
+  pp_write_text_to_stream (pp);
+  this_sid.print (pp);
+  pp_string (pp, ": {");
+  print (model, this_sid, pp);
+  pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/false);
+  pp_string (pp, "}\"];");
+  pp_newline (pp);
+}
+
+#if 0
+/* FIXME.  */
+
+std::string
+svalue::to_str () const
+{
+  pretty_printer pp;
+  print (&pp);
+  return pp_formatted_text (&pp);
+}
+#endif
+
 /* class region_svalue : public svalue.  */
 
+hashval_t
+region_svalue::hash () const
+{
+  return m_rid.as_int ();
+}
+
+void
+region_svalue::print (const region_model &model,
+		      svalue_id this_sid,
+		      pretty_printer *pp) const
+{
+  if (m_rid.null_p ())
+    pp_string (pp, "NULL");
+  else
+    {
+      pp_string (pp, "&");
+      m_rid.print (pp);
+    }
+}
+
+
+/* FIXME.  */
+
+void
+region_svalue::dump_dot_to_pp (const region_model &model,
+			       svalue_id this_sid,
+			       pretty_printer *pp) const
+{
+  svalue::dump_dot_to_pp (model, this_sid, pp);
+
+  /* If non-NULL, add an edge to the pointed-to region.  */
+  if (!m_rid.null_p ())
+    {
+      this_sid.dump_node_name_to_pp (pp);
+      pp_string (pp, " -> ");
+      m_rid.dump_node_name_to_pp (pp);
+      pp_string (pp, ";");
+      pp_newline (pp);
+  }
+}
+
+/* class constant_svalue : public svalue.  */
+
+/* FIXME.  */
+
+hashval_t
+constant_svalue::hash () const
+{
+  inchash::hash hstate;
+  inchash::add_expr (m_cst_expr, hstate);
+  return hstate.end ();
+}
+
 /* FIXME.  */
 
 void
-region_svalue::dump_dot_to_pp (pretty_printer *pp) const
+constant_svalue::print (const region_model &model,
+			svalue_id this_sid,
+			pretty_printer *pp) const
 {
-// TODO
+  pp_printf (pp, "%qE", m_cst_expr);
+}
+
+/* FIXME.  */
+
+const char *
+poison_kind_to_str (enum poison_kind kind)
+{
+  switch (kind)
+    {
+    default:
+      gcc_unreachable ();
+    case POISON_KIND_UNINIT:
+      return "uninit";
+    case POISON_KIND_FREED:
+      return "freed";
+    }
 }
 
 /* class poisoned_svalue : public svalue.  */
 
 /* FIXME.  */
 
+hashval_t
+poisoned_svalue::hash () const
+{
+  return m_kind;
+}
+
+/* FIXME.  */
+
 void
-poisoned_svalue::dump_dot_to_pp (pretty_printer *pp) const
+poisoned_svalue::print (const region_model &model,
+			svalue_id this_sid,
+			pretty_printer *pp) const
+{
+  pp_printf (pp, "poisoned: %s", poison_kind_to_str (m_kind));
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+/* FIXME.  */
+
+const char *
+region_kind_to_str (enum region_kind kind)
 {
-// TODO
+  switch (kind)
+    {
+    default:
+      gcc_unreachable ();
+    case RK_MAP:
+      return "map";
+    case RK_FRAME:
+      return "frame";
+    case RK_STACK:
+      return "stack";
+    case RK_ROOT:
+      return "root";
+    case RK_SYMBOLIC:
+      return "symbolic";
+    }
 }
 
 /* class region.  */
 
+/* FIXME.  */
+
+svalue_id
+region::get_value (const region_model &model) const
+{
+  /* If this region 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. */
+  for (region *ancestor = model.get_region (m_parent_rid);
+       ancestor;
+       ancestor = model.get_region (ancestor->m_parent_rid))
+    {
+      if (!ancestor->m_sval_id.null_p ())
+	return m_sval_id;
+    }
+  return svalue_id::null ();
+}
+
+/* FIXME.  */
+
+hashval_t
+region::hash () const
+{
+  inchash::hash hstate;
+  add_to_hash (hstate);
+  return hstate.end ();
+}
+
+/* FIXME.  */
+
+void
+region::print (const region_model &model,
+		   region_id this_rid,
+		   pretty_printer *pp) const
+{
+  this_rid.print (pp);
+  pp_string (pp, ": {");
+
+  /* vfunc.  */
+  print_fields (model, this_rid, pp);
+
+  pp_string (pp, "}");
+}
+
+/* FIXME.  */
+
 void
 region::dump_dot_to_pp (const region_model &model,
 			region_id this_rid,
 			pretty_printer *pp) const
 {
-  impl_dump_dot_to_pp (model, this_rid, pp);
+  this_rid.dump_node_name_to_pp (pp);
+  pp_printf (pp, " [shape=%s,style=filled,fillcolor=%s,label=\"",
+	     "record", "lightgrey");
+  pp_write_text_to_stream (pp);
+  print (model, this_rid, pp);
+  pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true);
+  pp_string (pp, "\"];");
+  pp_newline (pp);
 
   /* Add edge to svalue.  */
   if (!m_sval_id.null_p ())
@@ -101,29 +317,186 @@ region::dump_dot_to_pp (const region_model &model,
       m_parent_rid.dump_node_name_to_pp (pp);
       pp_string (pp, ";");
       pp_newline (pp);
-  }
+    }
+}
+
+/* FIXME.  */
+
+void
+region::dump_to_pp (const region_model &model,
+		    region_id this_rid,
+		    pretty_printer *pp,
+		    const char *prefix,
+		    bool is_last_child) const
+{
+  if (!m_parent_rid.null_p ())
+    {
+      // FIXME: colorize hierachical ASCII art
+      const char *tail = is_last_child ? "`-" : "|-";
+      pp_printf (pp, "%r%s%s%R", "note", prefix, tail);
+      // FIXME: dedicated color code?
+    }
+  print (model, this_rid, pp);
+  pp_newline (pp);
+
+  const char *new_prefix;
+  if (!m_parent_rid.null_p ())
+    new_prefix = ACONCAT ((prefix, is_last_child ? "  " : "|  ", NULL));
+  else
+    new_prefix = prefix;
+
+  /* TODO: iter children:
+     TODO: we'll also want to label them
+     (potentially with tree labels, or with sval labels).  */
+
+  auto_vec<region_id> child_rids;
+  unsigned i;
+  region *child;
+  FOR_EACH_VEC_ELT (model.m_regions, i, child)
+    if (child->m_parent_rid == this_rid)
+      child_rids.safe_push (region_id::from_int (i));
+
+  region_id *child_rid;
+  FOR_EACH_VEC_ELT (child_rids, i, child_rid)
+    model.get_region (*child_rid)->dump_to_pp (model, *child_rid, pp,
+					       new_prefix,
+					       i == child_rids.length () - 1);
+}
+
+#if 0
+/* FIXME.  */
+
+std::string
+region::to_str () const
+{
+  pretty_printer pp;
+  print (&pp);
+  return pp_formatted_text (&pp);
+}
+#endif
+
+/* FIXME.  */
+
+void
+region::add_to_hash (inchash::hash &hstate) const
+{
+  inchash::add (m_parent_rid, hstate);
+  inchash::add (m_sval_id, hstate);
+}
+
+/* FIXME.  */
+
+void
+region::print_fields (const region_model &model,
+		      region_id this_rid,
+		      pretty_printer *pp) const
+{
+  pp_printf (pp, "kind: %qs", region_kind_to_str (get_kind ()));
+
+  pp_string (pp, ", parent: ");
+  m_parent_rid.print (pp);
+
+  pp_printf (pp, ", sval: ");
+  m_sval_id.print (pp);
 }
 
 /* class map_region : public region.  */
 
 /* FIXME.  */
 
+map_region::map_region (const map_region &other)
+: region (other),
+  m_map (other.m_map)
+{
+}
+
+/* FIXME.  */
+
+region *
+map_region::clone () const
+{
+  return new map_region (*this);
+}
+
+/* FIXME.  */
+
 void
-map_region::impl_dump_dot_to_pp (const region_model &model,
-				 region_id this_rid,
-				 pretty_printer *pp) const
+map_region::print_fields (const region_model &model,
+			  region_id this_rid,
+			  pretty_printer *pp) const
 {
-// TODO
-  this_rid.dump_node_name_to_pp (pp);
-  pp_printf (pp, " [shape=%s,style=filled,fillcolor=%s,label=\"",
-	     "record", "lightgrey");
-  pp_left_brace (pp);
-  pp_string (pp, "map region");
-  pp_write_text_to_stream (pp);
-  pp_right_brace (pp);
-  pp_string (pp, "\"];");
-  pp_newline (pp);
+  region::print_fields (model, this_rid, pp);
+  pp_string (pp, ", map: {");
+  for (map_t::iterator iter = m_map.begin ();
+       iter != m_map.end ();
+       ++iter)
+    {
+      if (iter != m_map.begin ())
+	pp_string (pp, ", ");
+      tree expr = (*iter).first;
+      region_id child_rid = (*iter).second;
+      pp_printf (pp, "%qE: ", expr);
+      child_rid.print (pp);
+    }
+  pp_string (pp, "}");
+}
+
+/* FIXME.  */
+
+void
+map_region::dump_dot_to_pp (const region_model &model,
+			    region_id this_rid,
+			    pretty_printer *pp) const
+{
+  region::dump_dot_to_pp (model, this_rid, pp);
+  for (map_t::iterator iter = m_map.begin ();
+       iter != m_map.end ();
+       ++iter)
+    {
+      // TODO: add nodes/edges to label things
+
+      tree expr = (*iter).first;
+      region_id child_rid = (*iter).second;
 
+      pp_printf (pp, "rid_label_%i [label=\"", child_rid.as_int ());
+      pp_write_text_to_stream (pp);
+      pp_printf (pp, "%qE", expr);
+      pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/false);
+      pp_string (pp, "\"];");
+      pp_newline (pp);
+
+      pp_printf (pp, "rid_label_%i", child_rid.as_int ());
+      pp_string (pp, " -> ");
+      child_rid.dump_node_name_to_pp (pp);
+      pp_string (pp, ";");
+      pp_newline (pp);
+    }
+}
+
+/* FIXME.  */
+
+region_id
+map_region::get_or_create (region_model *model,
+			   region_id this_rid,
+			   tree expr)
+{
+  // TODO
+  region_id *slot = m_map.get (expr);
+  if (slot)
+    return *slot;
+  // FIXME: what kind of region?
+  region_id child_rid = model->add_region (new map_region (this_rid));
+  m_map.put (expr, child_rid);
+  return child_rid;
+}
+
+/* FIXME.  */
+
+void
+map_region::add_to_hash (inchash::hash &hstate) const
+{
+  region::add_to_hash (hstate);
+  // TODO
 }
 
 /* FIXME.  */
@@ -134,6 +507,26 @@ frame_region::clone () const
   return new frame_region (*this);
 }
 
+/* FIXME.  */
+
+void
+frame_region::print_fields (const region_model &model,
+			    region_id this_rid,
+			    pretty_printer *pp) const
+{
+  map_region::print_fields (model, this_rid, pp);
+  pp_printf (pp, ", function: %qs", function_name (m_fun));
+}
+
+/* FIXME.  */
+
+void
+frame_region::add_to_hash (inchash::hash &hstate) const
+{
+  map_region::add_to_hash (hstate);
+  hstate.add_ptr (m_fun);
+}
+
 /* class stack_region : public region.  */
 
 /* FIXME.  */
@@ -159,19 +552,12 @@ stack_region::clone () const
 /* FIXME.  */
 
 void
-stack_region::impl_dump_dot_to_pp (const region_model &model,
-				   region_id this_rid,
-				   pretty_printer *pp) const
+stack_region::print_fields (const region_model &model,
+				  region_id this_rid,
+				  pretty_printer *pp) const
 {
-  this_rid.dump_node_name_to_pp (pp);
-  pp_printf (pp, " [shape=%s,style=filled,fillcolor=%s,label=\"",
-	     "record", "lightgrey");
-  pp_left_brace (pp);
-  pp_string (pp, "stack region");
-  pp_write_text_to_stream (pp);
-  pp_right_brace (pp);
-  pp_string (pp, "\"];");
-  pp_newline (pp);
+  region::print_fields (model, this_rid, pp);
+  // TODO
 }
 
 /* FIXME.  */
@@ -182,6 +568,30 @@ stack_region::push_frame (region_id frame_rid)
   m_frame_rids.safe_push (frame_rid);
 }
 
+/* FIXME.  */
+
+region_id
+stack_region::get_current_frame_id () const
+{
+  if (m_frame_rids.length () > 0)
+    return m_frame_rids[m_frame_rids.length () - 1];
+  else
+    return region_id::null ();
+}
+
+/* FIXME.  */
+
+void
+stack_region::add_to_hash (inchash::hash &hstate) const
+{
+  region::add_to_hash (hstate);
+
+  int i;
+  region_id *frame_rid;
+  FOR_EACH_VEC_ELT (m_frame_rids, i, frame_rid)
+    inchash::add (*frame_rid, hstate);
+}
+
 /* class root_region : public region.  */
 
 /* FIXME.  */
@@ -215,24 +625,12 @@ root_region::clone () const
 /* FIXME.  */
 
 void
-root_region::impl_dump_dot_to_pp (const region_model &model,
-				  region_id this_rid,
-				  pretty_printer *pp) const
+root_region::print_fields (const region_model &model,
+			   region_id this_rid,
+			   pretty_printer *pp) const
 {
-  this_rid.dump_node_name_to_pp (pp);
-  pp_printf (pp, " [shape=%s,style=filled,fillcolor=%s,label=\"",
-	     "record", "lightgrey");
-  pp_left_brace (pp);
-  pp_string (pp, "root region");
-  pp_write_text_to_stream (pp);
-  pp_right_brace (pp);
-  pp_string (pp, "\"];");
-  pp_newline (pp);
-
-#if 0
-  model.get_region (m_stack_rid).dump_dot_to_pp (pp);
-  m_globals.dump_dot_to_pp (pp);
-#endif
+  region::print_fields (model, this_rid, pp);
+  // TODO
 }
 
 void
@@ -253,6 +651,43 @@ root_region::push_frame (region_model *model, function *fun)
   stack->push_frame (frame_rid);
 }
 
+/* FIXME.  */
+
+region_id
+root_region::get_current_frame_id (const region_model &model) const
+{
+  stack_region *stack = model.get_region <stack_region> (m_stack_rid);
+  if (stack)
+    return stack->get_current_frame_id ();
+  else
+    return region_id::null ();
+}
+
+/* FIXME.  */
+
+void
+root_region::add_to_hash (inchash::hash &hstate) const
+{
+  region::add_to_hash (hstate);
+  inchash::add (m_stack_rid, hstate);
+  inchash::add (m_globals_rid, hstate);
+}
+
+/* class symbolic_region : public map_region.  */
+
+symbolic_region::symbolic_region (const symbolic_region &other)
+: map_region (other)
+{
+}
+
+region *
+symbolic_region::clone () const
+{
+  return new symbolic_region (*this);
+}
+
+////////////////////////////////////////////////////////////////////////////
+
 /* class region_model.  */
 
 /* FIXME.  */
@@ -294,8 +729,18 @@ region_model::operator== (const region_model &other) const
 hashval_t
 region_model::hash () const
 {
-  // TODO
-  return 0; // FIXME
+  hashval_t result = 0;
+  int i;
+
+  svalue *svalue;
+  FOR_EACH_VEC_ELT (m_svalues, i, svalue)
+    result ^= svalue->hash ();
+
+  region *region;
+  FOR_EACH_VEC_ELT (m_regions, i, region)
+    result ^= region->hash ();
+
+  return result;
 }
 
 /* FIXME.  */
@@ -304,6 +749,29 @@ void
 region_model::print (pretty_printer *pp) const
 {
   // TODO
+
+  int i;
+
+  pp_string (pp, "svalues: [");
+  svalue *svalue;
+  FOR_EACH_VEC_ELT (m_svalues, i, svalue)
+    {
+      if (i > 0)
+	pp_string (pp, ", ");
+      svalue->print (*this, svalue_id::from_int (i), pp);
+    }
+
+  pp_string (pp, "], regions: [");
+
+  region *region;
+  FOR_EACH_VEC_ELT (m_regions, i, region)
+    {
+      if (i > 0)
+	pp_string (pp, ", ");
+      region->print (*this, region_id::from_int (i), pp);
+    }
+
+  pp_string (pp, "]");
 }
 
 /* FIXME.  */
@@ -312,6 +780,7 @@ std::string
 region_model::to_str () const
 {
   pretty_printer pp;
+  pp_format_decoder (&pp) = default_tree_printer;
   print (&pp);
   return pp_formatted_text (&pp);
 }
@@ -334,6 +803,11 @@ region_model::dump_dot_to_pp (pretty_printer *pp) const
   pp_string (pp, "compound=true;\n");
 
   int i;
+
+  svalue *svalue;
+  FOR_EACH_VEC_ELT (m_svalues, i, svalue)
+    svalue->dump_dot_to_pp (*this, svalue_id::from_int (i), pp);
+
   region *region;
   FOR_EACH_VEC_ELT (m_regions, i, region)
     region->dump_dot_to_pp (*this, region_id::from_int (i), pp);
@@ -350,6 +824,7 @@ void
 region_model::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);
@@ -368,16 +843,283 @@ region_model::dump_dot (const char *path) const
 /* FIXME.  */
 
 void
+region_model::dump_to_pp (pretty_printer *pp) const
+{
+  get_region (m_root_rid)->dump_to_pp (*this, m_root_rid, pp, "", true);
+}
+
+/* FIXME.  */
+
+void
+region_model::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);
+}
+
+/* FIXME.  */
+
+DEBUG_FUNCTION void
+region_model::dump () const
+{
+  dump (stderr);
+}
+
+/* FIXME.  */
+
+void
 region_model::validate () const
 {
   // TODO: skip this in a release build
+  // TODO
 }
 
 /* FIXME.  */
 
 void
-region_model::on_assignment (tree lhs, tree rhs)
+region_model::on_assignment (const gassign *assign)
 {
+  tree lhs = gimple_assign_lhs (assign);
+  tree rhs1 = gimple_assign_rhs1 (assign);
+
+  region_id lhs_rid = get_lvalue (lhs);
+
+  if (lhs_rid.null_p ())
+    return;
+  // TODO: issue a warning for this case
+
+  enum tree_code op = gimple_assign_rhs_code (assign);
+  switch (op)
+    {
+    default:
+      gcc_unreachable ();
+      break;
+
+    case CONSTRUCTOR:
+      {
+	/* e.g. "x ={v} {CLOBBER};"  */
+	// TODO
+      }
+      break;
+
+    case ADDR_EXPR:
+      {
+	/* LHS = &RHS;  */
+	region_id rhs_rid = get_lvalue (TREE_OPERAND (rhs1, 0));
+	svalue_id ptr_sid = get_svalue_for_region (rhs_rid);
+	set_value (lhs_rid, ptr_sid);
+      }
+      break;
+
+    case INTEGER_CST:
+      {
+	/* LHS = RHS;  */
+	svalue_id cst_sid = get_rvalue (rhs1);
+	set_value (lhs_rid, cst_sid);
+      }
+      break;
+
+    case NOP_EXPR:
+      // cast: TODO
+      // fall though for now
+    case VAR_DECL:
+      {
+	/* LHS = VAR;  */
+	region_id rhs_rid = get_lvalue (rhs1);
+	svalue_id var_sid = get_region (rhs_rid)->get_value (*this);
+	set_value (lhs_rid, var_sid);
+      }
+      break;
+
+    case EQ_EXPR:
+      {
+	tree rhs2 = gimple_assign_rhs2 (assign);
+
+	// TODO: constraints between svalues
+	svalue_id rhs1_sid = get_rvalue (rhs1);
+	svalue_id rhs2_sid = get_rvalue (rhs2);
+
+	tristate t = eval_condition (rhs1_sid, op, rhs2_sid);
+	if (t.is_known ())
+	  set_value (lhs_rid,
+		     get_rvalue (t.is_true ()
+				 ? boolean_true_node
+				 : boolean_false_node));
+      }
+      break;
+
+    case COMPONENT_REF:
+      {
+	/* LHS = op0.op1;  */
+	region_id child_rid = get_lvalue (rhs1);
+	svalue_id child_sid = get_region (child_rid)->get_value (*this);
+	set_value (lhs_rid, child_sid);
+      }
+      break;
+    }
+}
+
+/* FIXME.  */
+
+region_id
+region_model::get_lvalue (tree expr)
+{
+  switch (TREE_CODE (expr))
+    {
+    default:
+      gcc_unreachable ();
+
+    case MEM_REF:
+      {
+	tree ptr = TREE_OPERAND (expr, 0);
+	tree offset = TREE_OPERAND (expr, 1);
+	// FIXME: what about nonzero offset?
+
+	svalue_id ptr_sid = get_rvalue (ptr);
+	svalue *ptr_svalue = get_svalue (ptr_sid);
+	if (ptr_svalue)
+	  {
+	    if (region_svalue *region_sval
+		= ptr_svalue->dyn_cast_region_svalue ())
+	      return region_sval->get_pointee ();
+	  }
+	else
+	  {
+	    /* We need a symbolic_region to represent this unknown region.
+	       We don't know if it on the heap, stack, or a global,
+	       so use the root region as parent.  */
+	    region_id new_rid = add_region (new symbolic_region (m_root_rid));
+
+	    /* We need to write the region back into the pointer,
+	       or we'll get a new, different region each time.  */
+	    region_id ptr_rid = get_lvalue (ptr);
+	    ptr_sid = add_svalue (new region_svalue (new_rid));
+	    set_value (ptr_rid, ptr_sid);
+
+	    return new_rid;
+	  }
+	// FIXME: how to hit this path?
+	return region_id::null ();
+      }
+      break; // FIXME
+
+    case SSA_NAME:
+    case PARM_DECL:
+    case VAR_DECL:
+      {
+	// FIXME:
+	gcc_assert (TREE_CODE (expr) == SSA_NAME
+		    || TREE_CODE (expr) == PARM_DECL
+		    || TREE_CODE (expr) == VAR_DECL);
+	// TODO: globals vs locals
+
+	region_id frame_rid = get_current_frame_id ();
+	frame_region *frame = get_region <frame_region> (frame_rid);
+	region_id child_rid = frame->get_or_create (this, frame_rid, expr);
+	return child_rid;
+      }
+
+    case COMPONENT_REF:
+      {
+	/* obj.field  */
+	tree obj = TREE_OPERAND (expr, 0);
+	tree field = TREE_OPERAND (expr, 1);
+	region_id obj_rid = get_lvalue (obj);
+	map_region *obj_region = get_region<map_region> (obj_rid);
+	region_id child_rid = obj_region->get_or_create (this, obj_rid, field);
+	return child_rid;
+      }
+      break;
+
+    }
+}
+
+/* FIXME.  */
+
+svalue_id
+region_model::get_rvalue (tree expr)
+{
+  switch (TREE_CODE (expr))
+    {
+    default:
+      gcc_unreachable ();
+
+    case INTEGER_CST:
+      return get_svalue_for_constant (expr);
+
+    case SSA_NAME:
+    case VAR_DECL:
+      {
+	region_id var_rid = get_lvalue (expr);
+	return get_region (var_rid)->get_value (*this);
+      }
+    }
+}
+
+/* FIXME.  */
+
+svalue_id
+region_model::get_svalue_for_region (region_id rid)
+{
+  // TODO: maybe reuse existing ones? (maybe keep a record in the region?)
+  return add_svalue (new region_svalue (rid));
+}
+
+/* FIXME.  */
+
+svalue_id
+region_model::get_svalue_for_constant (tree cst_expr)
+{
+  return add_svalue (new constant_svalue (cst_expr));
+}
+
+/* FIXME.  */
+
+void
+region_model::set_value (region_id lhs_rid, svalue_id rhs_sid)
+{
+  get_region (lhs_rid)->set_value (rhs_sid);
+}
+
+/* FIXME.  */
+
+tristate
+region_model::eval_condition (svalue_id lhs_sid,
+			      enum tree_code op,
+			      svalue_id rhs_sid) const
+{
+  svalue *lhs = get_svalue (lhs_sid);
+  svalue *rhs = get_svalue (rhs_sid);
+
+  if (lhs && rhs)
+    if (constant_svalue *cst_lhs = lhs->dyn_cast_constant_svalue ())
+      if (constant_svalue *cst_rhs = rhs->dyn_cast_constant_svalue ())
+	{
+	  tree lhs_const = cst_lhs->get_constant ();
+	  tree rhs_const = cst_rhs->get_constant ();
+	  tree comparison
+	    = fold_build2 (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;
+	}
+
+  // TODO
+  return tristate::TS_UNKNOWN;
+}
+
+tristate
+region_model::eval_condition (tree lhs,
+			      enum tree_code op,
+			      tree rhs)
+{
+  return eval_condition (get_rvalue (lhs), op, get_rvalue (rhs));
 }
 
 /* FIXME.  */
@@ -391,10 +1133,26 @@ region_model::apply_constraints_for_edge (const superedge &edge,
 
 /* FIXME.  */
 
+root_region *
+region_model::get_root_region () const
+{
+  return get_region<root_region> (m_root_rid);
+}
+
+/* FIXME.  */
+
 void
 region_model::push_frame (function *fun)
 {
-  get_region<root_region> (m_root_rid)->push_frame (this, fun);
+  get_root_region ()->push_frame (this, fun);
+}
+
+/* FIXME.  */
+
+region_id
+region_model::get_current_frame_id () const
+{
+  return get_root_region ()->get_current_frame_id (*this);
 }
 
 /* FIXME.  */
@@ -437,6 +1195,22 @@ region_model::get_region (region_id rid) const
   return m_regions[rid.as_int ()];
 }
 
+/* FIXME.  */
+
+void
+inchash::add (svalue_id sid, inchash::hash &hstate)
+{
+  hstate.add_int (sid.as_int ());
+}
+
+/* FIXME.  */
+
+void
+inchash::add (region_id rid, inchash::hash &hstate)
+{
+  hstate.add_int (rid.as_int ());
+}
+
 // FIXME:
 void
 debug (const region_model &rmodel)
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index d6707bc..1b3f46b 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -27,6 +27,10 @@ along with GCC; see the file COPYING3.  If not see
      http://lcs.ios.ac.cn/~xuzb/canalyze/memmodel.pdf  */
 
 class superedge;
+
+class svalue;
+  class region_svalue;
+  class constant_svalue;
 class region;
 class region_model;
 
@@ -46,6 +50,7 @@ public:
   static svalue_id from_int (int idx) { return svalue_id (idx); }
   int as_int () const { return m_idx; }
 
+  void print (pretty_printer *pp) const;
   void dump_node_name_to_pp (pretty_printer *pp) const;
 
 private:
@@ -61,11 +66,17 @@ public:
 
   region_id () : m_idx (-1) {}
 
+  bool operator== (const region_id &other) const
+  {
+    return m_idx == other.m_idx;
+  }
+
   bool null_p () const { return m_idx == -1; }
 
   static region_id from_int (int idx) { return region_id (idx); }
   int as_int () const { return m_idx; }
 
+  void print (pretty_printer *pp) const;
   void dump_node_name_to_pp (pretty_printer *pp) const;
 
 private:
@@ -74,6 +85,17 @@ private:
   int m_idx;
 };
 
+////////////////////////////////////////////////////////////////////////////
+
+/* FIXME.  */
+
+enum svalue_kind
+{
+  SK_REGION,
+  SK_CONSTANT,
+  SK_POISONED
+};
+
 /* FIXME.  */
 
 class svalue
@@ -83,10 +105,22 @@ public:
 
   virtual svalue *clone () const = 0;
 
-  virtual void dump_dot_to_pp (pretty_printer *pp) const = 0;
+  virtual enum svalue_kind get_kind () const = 0;
 
-private:
-  int m_index;
+  virtual hashval_t hash () const = 0;
+
+  virtual void print (const region_model &model,
+		      svalue_id this_sid,
+		      pretty_printer *pp) const = 0;
+
+  virtual void dump_dot_to_pp (const region_model &model,
+			       svalue_id this_sid,
+			       pretty_printer *pp) const;
+
+  std::string to_str () const;
+
+  virtual region_svalue *dyn_cast_region_svalue () { return NULL; }
+  virtual constant_svalue *dyn_cast_constant_svalue () { return NULL; }
 };
 
 /* FIXME.  */
@@ -99,20 +133,65 @@ public:
   svalue *clone () const FINAL OVERRIDE
   { return new region_svalue (m_rid); }
 
-  void dump_dot_to_pp (pretty_printer *pp) const
+  enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_REGION; }
+
+  hashval_t hash () const FINAL OVERRIDE;
+
+  void print (const region_model &model,
+	      svalue_id this_sid,
+	      pretty_printer *pp) const
+    FINAL OVERRIDE;
+
+  void dump_dot_to_pp (const region_model &model,
+		       svalue_id this_sid,
+		       pretty_printer *pp) const
     FINAL OVERRIDE;
 
+  region_svalue *dyn_cast_region_svalue () FINAL OVERRIDE { return this; }
+
+  region_id get_pointee () const { return m_rid; }
+
+ private:
   region_id m_rid;
 };
 
 /* FIXME.  */
 
+class constant_svalue : public svalue
+{
+public:
+  constant_svalue (tree cst_expr) : m_cst_expr (cst_expr) {}
+
+  svalue *clone () const FINAL OVERRIDE
+  { return new constant_svalue (m_cst_expr); }
+
+  enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_CONSTANT; }
+
+  hashval_t hash () const FINAL OVERRIDE;
+
+  void print (const region_model &model,
+	      svalue_id this_sid,
+	      pretty_printer *pp) const
+    FINAL OVERRIDE;
+
+  constant_svalue *dyn_cast_constant_svalue () FINAL OVERRIDE { return this; }
+
+  tree get_constant () const { return m_cst_expr; }
+
+ private:
+  tree m_cst_expr;
+};
+
+/* FIXME.  */
+
 enum poison_kind
 {
   POISON_KIND_UNINIT,
   POISON_KIND_FREED
 };
 
+extern const char *poison_kind_to_str (enum poison_kind);
+
 /* FIXME.  */
 
 class poisoned_svalue : public svalue
@@ -123,12 +202,33 @@ public:
   svalue *clone () const FINAL OVERRIDE
   { return new poisoned_svalue (m_kind); }
 
-  void dump_dot_to_pp (pretty_printer *pp) const
+  enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_POISONED; }
+
+  hashval_t hash () const FINAL OVERRIDE;
+
+  void print (const region_model &model,
+	      svalue_id this_sid,
+	      pretty_printer *pp) const
     FINAL OVERRIDE;
 
   enum poison_kind m_kind;
 };
 
+////////////////////////////////////////////////////////////////////////////
+
+/* FIXME.  */
+
+enum region_kind
+{
+  RK_MAP,
+  RK_FRAME,
+  RK_STACK,
+  RK_ROOT,
+  RK_SYMBOLIC
+};
+
+extern const char *region_kind_to_str (enum region_kind);
+
 /* FIXME.  */
 
 class region
@@ -146,14 +246,36 @@ public:
 
   virtual region *clone () const = 0;
 
-  void dump_dot_to_pp (const region_model &model,
-		       region_id this_rid,
-		       pretty_printer *pp) const;
-  virtual void impl_dump_dot_to_pp (const region_model &model,
-				    region_id this_rid,
-				    pretty_printer *pp) const = 0;
+  virtual enum region_kind get_kind () const = 0;
 
-public:
+  void set_value (svalue_id rhs_sid) { m_sval_id = rhs_sid; }
+  svalue_id get_value (const region_model &model) const;
+
+  hashval_t hash () const;
+
+  void print (const region_model &model,
+	      region_id this_rid,
+	      pretty_printer *pp) const;
+
+  virtual void dump_dot_to_pp (const region_model &model,
+			       region_id this_rid,
+			       pretty_printer *pp) const;
+
+  virtual void dump_to_pp (const region_model &model,
+			   region_id this_rid,
+			   pretty_printer *pp,
+			   const char *prefix,
+			   bool is_last_child) const;
+
+  std::string to_str () const;
+
+ protected:
+  virtual void add_to_hash (inchash::hash &hstate) const;
+  virtual void print_fields (const region_model &model,
+			     region_id this_rid,
+			     pretty_printer *pp) const;
+
+ private:
   region_id m_parent_rid;
   svalue_id m_sval_id;
 };
@@ -166,18 +288,29 @@ public:
   map_region (region_id parent_rid)
   : region (parent_rid, svalue_id::null ())
   {}
+  map_region (const map_region &other);
 
-  map_region (const map_region &other)
-  : region (other)
-  {
-    // TODO
-  }
+  region *clone () const OVERRIDE;
+
+  enum region_kind get_kind () const OVERRIDE { return RK_MAP; }
 
-  void impl_dump_dot_to_pp (const region_model &model,
-			    region_id this_rid,
-			    pretty_printer *pp) const
+  void dump_dot_to_pp (const region_model &model,
+		       region_id this_rid,
+		       pretty_printer *pp) const
     FINAL OVERRIDE;
 
+  region_id get_or_create (region_model *model,
+			   region_id this_rid,
+			   tree expr);
+
+ protected:
+  void add_to_hash (inchash::hash &hstate) const OVERRIDE;
+  void print_fields (const region_model &model,
+		     region_id this_rid,
+		     pretty_printer *pp) const
+    OVERRIDE;
+
+ private:
   // FIXME: mapping from tree to child region
   typedef hash_map<tree, region_id> map_t;
   map_t m_map;
@@ -199,6 +332,15 @@ public:
 
   region *clone () const FINAL OVERRIDE;
 
+  enum region_kind get_kind () const FINAL OVERRIDE { return RK_FRAME; }
+
+ private:
+  void add_to_hash (inchash::hash &hstate) const FINAL OVERRIDE;
+  void print_fields (const region_model &model,
+		     region_id this_rid,
+		     pretty_printer *pp) const
+    FINAL OVERRIDE;
+
   function *m_fun;
 };
 
@@ -206,19 +348,24 @@ class stack_region : public region
 {
 public:
   stack_region (region_id parent_rid, svalue_id sval_id)
-  : region (parent_rid, sval_id/*new poisoned_svalue (POISON_KIND_UNINIT)*/)
+  : region (parent_rid, sval_id)
   {}
 
   stack_region (const stack_region &other);
 
   region *clone () const FINAL OVERRIDE;
 
-  void impl_dump_dot_to_pp (const region_model &model,
-			    region_id this_rid,
-			    pretty_printer *pp) const
-    FINAL OVERRIDE;
+  enum region_kind get_kind () const FINAL OVERRIDE { return RK_STACK; }
 
   void push_frame (region_id frame_rid);
+  region_id get_current_frame_id () const;
+
+ private:
+  void add_to_hash (inchash::hash &hstate) const FINAL OVERRIDE;
+  void print_fields (const region_model &model,
+		     region_id this_rid,
+		     pretty_printer *pp) const
+    FINAL OVERRIDE;
 
   auto_vec<region_id> m_frame_rids;
 };
@@ -231,20 +378,42 @@ public:
 
   region *clone () const FINAL OVERRIDE;
 
-  void impl_dump_dot_to_pp (const region_model &model,
-			    region_id this_rid,
-			    pretty_printer *pp) const
-    FINAL OVERRIDE;
+  enum region_kind get_kind () const FINAL OVERRIDE { return RK_ROOT; }
 
   void push_frame (region_model *model, function *fun);
+  region_id get_current_frame_id (const region_model &model) const;
 
 private:
+  void add_to_hash (inchash::hash &hstate) const FINAL OVERRIDE;
+  void print_fields (const region_model &model,
+		     region_id this_rid,
+		     pretty_printer *pp) const
+    FINAL OVERRIDE;
+
   region_id m_stack_rid;
   region_id m_globals_rid;
 };
 
 /* FIXME.  */
 
+class symbolic_region : public map_region
+{
+public:
+  symbolic_region (region_id parent_rid)
+  : map_region (parent_rid)
+  {}
+  symbolic_region (const symbolic_region &other);
+
+  region *clone () const OVERRIDE;
+
+  enum region_kind get_kind () const OVERRIDE { return RK_SYMBOLIC; }
+};
+
+
+////////////////////////////////////////////////////////////////////////////
+
+/* FIXME.  */
+
 class region_model
 {
  public:
@@ -262,14 +431,21 @@ class region_model
   void dump_dot_to_file (FILE *fp) const;
   void dump_dot (const char *path) const;
 
+  void dump_to_pp (pretty_printer *pp) const;
+  void dump (FILE *fp) const;
+  void dump () const;
+
   void validate () const;
 
-  void on_assignment (tree lhs, tree rhs);
+  void on_assignment (const gassign *stmt);
 
   bool apply_constraints_for_edge (const superedge &edge,
 				   const gimple *last_stmt);
 
+  root_region *get_root_region () const;
+
   void push_frame (function *fun);
+  region_id get_current_frame_id () const;
 
   svalue_id add_svalue (svalue *sval);
   region_id add_region (region *r);
@@ -283,12 +459,38 @@ class region_model
     return (Subclass *)get_region (rid); // TODO: checking
   }
 
+#if 0
+  void assign_to_lvalue (lvalue *lval,
+			 rvalue *rval);
+#endif
+  region_id get_lvalue (tree expr);
+  svalue_id get_rvalue (tree expr);
+
+  svalue_id get_svalue_for_region (region_id id);
+  svalue_id get_svalue_for_constant (tree cst_expr);
+
+  void set_value (region_id lhs_rid, svalue_id rhs_sid);
+
+  tristate eval_condition (svalue_id lhs,
+			   enum tree_code op,
+			   svalue_id rhs) const;
+
+  tristate eval_condition (tree lhs,
+			   enum tree_code op,
+			   tree rhs);
+
   // private:
   auto_delete_vec<svalue> m_svalues;
   auto_delete_vec<region> m_regions;
   region_id m_root_rid;
 };
 
+namespace inchash
+{
+  extern void add (svalue_id sid, hash &hstate);
+  extern void add (region_id rid, hash &hstate);
+} // namespace inchash
+
 extern void debug (const region_model &rmodel);
 
 #endif /* GCC_ANALYZER_REGION_MODEL_H */
diff --git a/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-clang-paper-example-1.c b/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-clang-paper-example-1.c
index c5444bc..585a06f 100644
--- a/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-clang-paper-example-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-clang-paper-example-1.c
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-fanalyzer-region-model" } */
+
 void test_1 (void)
 {
   int x, y;
@@ -6,6 +8,5 @@ void test_1 (void)
   *p = 4;
   y = x;
 
-  __analyzer_eval (y == 4); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
-  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */  
+  __analyzer_eval (y == 4); /* { dg-warning "TRUE" } */
 }
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-1.c b/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
index f04574b..ed10f3e 100644
--- a/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
@@ -5,34 +5,55 @@ struct foo
   int i;
 };
 
+/* Fields of a local.  */
+
 void test_1 (void)
 {
   struct foo f;
-  f.i = 42;
-  __analyzer_eval (f.i == 42); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
-  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
-  /* TODO(xfail^^^): need to model field values.  */
+  f.i = 1;
+  __analyzer_eval (f.i == 1); /* { dg-warning "TRUE" } */
 }
 
-void test_2 (struct foo *f)
+/* Fields of a param.  */
+
+void test_2 (struct foo f)
 {
-  f->i = 42;
-  __analyzer_eval (f->i == 42); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
-  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
-  /* TODO(xfail^^^): need to model field values.  */
+  __analyzer_eval (f.i == 2); /* { dg-warning "UNKNOWN" } */
+  f.i = 2;
+  __analyzer_eval (f.i == 2); /* { dg-warning "TRUE" } */
 }
 
-void test_3 (void)
+/* Fields of a param ptr.  */
+
+void test_3 (struct foo *f)
+{
+  __analyzer_eval (f->i == 3); /* { dg-warning "UNKNOWN" } */
+  f->i = 3;
+  __analyzer_eval (f->i == 3); /* { dg-warning "TRUE" } */
+}
+
+/* Pointer to a local.  */
+
+void test_4 (void)
 {
   int i;
   int *p = &i;
   i = 1;
   *p = 2;
-  __analyzer_eval (i == 2); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
-  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  __analyzer_eval (i == 2); /* { dg-warning "TRUE" } */
 }
 
+// TODO:
+
 struct link
 {
   struct link *next;
 };
+
+// TODO:
+
+union u
+{
+  int i;
+  int *ptr;
+};
-- 
1.8.5.3

