From 86646decd680fc2bf68def701d0f9f4364612cc0 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 4 Jun 2019 12:06:45 -0400
Subject: [PATCH 26/28] FIXME: 2019-06-04: paths for region_model warnings

---
 gcc/analyzer/constraint-manager.cc                |  10 +-
 gcc/analyzer/impl-4.cc                            | 234 +++++++++++++----
 gcc/analyzer/region-model.cc                      | 291 ++++++++++++++--------
 gcc/analyzer/region-model.h                       |  81 ++++--
 gcc/analyzer/supergraph.cc                        |   3 +
 gcc/common.opt                                    |  12 +
 gcc/testsuite/gcc.dg/analyzer/data-model-1.c      |  48 ++--
 gcc/testsuite/gcc.dg/analyzer/data-model-5.c      |   9 +-
 gcc/testsuite/gcc.dg/analyzer/data-model-path-1.c |  17 ++
 9 files changed, 505 insertions(+), 200 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-path-1.c

diff --git a/gcc/analyzer/constraint-manager.cc b/gcc/analyzer/constraint-manager.cc
index 6d245ac..18786b5 100644
--- a/gcc/analyzer/constraint-manager.cc
+++ b/gcc/analyzer/constraint-manager.cc
@@ -1255,7 +1255,7 @@ namespace selftest {
 
 #define ADD_SAT_CONSTRAINT(MODEL, LHS, OP, RHS)	\
   SELFTEST_BEGIN_STMT					\
-    bool sat = (MODEL).add_constraint (LHS, OP, RHS);	\
+    bool sat = (MODEL).add_constraint (LHS, OP, RHS, NULL);	\
     ASSERT_TRUE (sat);					\
   SELFTEST_END_STMT
 
@@ -1264,7 +1264,7 @@ namespace selftest {
 
 #define ADD_UNSAT_CONSTRAINT(MODEL, LHS, OP, RHS)	\
   SELFTEST_BEGIN_STMT					\
-    bool sat = (MODEL).add_constraint (LHS, OP, RHS);	\
+    bool sat = (MODEL).add_constraint (LHS, OP, RHS, NULL);	\
     ASSERT_FALSE (sat);				\
   SELFTEST_END_STMT
 
@@ -1274,7 +1274,7 @@ assert_condition (const location &loc,
 		  tree lhs, tree_code op, tree rhs,
 		  tristate expected)
 {
-  tristate actual = model.eval_condition (lhs, op, rhs);
+  tristate actual = model.eval_condition (lhs, op, rhs, NULL);
   ASSERT_EQ_AT (loc, actual, expected);
 }
 
@@ -1912,9 +1912,9 @@ test_constraint_impl ()
     ASSERT_EQ (cm->m_constraints.length (), 1);
     ASSERT_EQ (cm->m_equiv_classes.length (), 2);
     ASSERT_EQ (cm->m_constraints[0].m_lhs,
-	       cm->get_or_add_equiv_class (model.get_rvalue (int_0)));
+	       cm->get_or_add_equiv_class (model.get_rvalue (int_0, NULL)));
     ASSERT_EQ (cm->m_constraints[0].m_rhs,
-	       cm->get_or_add_equiv_class (model.get_rvalue (int_42)));
+	       cm->get_or_add_equiv_class (model.get_rvalue (int_42, NULL)));
     ASSERT_EQ (cm->m_constraints[0].m_op, CONSTRAINT_LT);
   }
 
diff --git a/gcc/analyzer/impl-4.cc b/gcc/analyzer/impl-4.cc
index fd76d06..624c6af 100644
--- a/gcc/analyzer/impl-4.cc
+++ b/gcc/analyzer/impl-4.cc
@@ -770,15 +770,15 @@ 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,
+  void on_assignment_for_region_model (exploded_graph &eg,
 				       const gassign *stmt,
 				       const program_state &old_state,
 				       program_state *new_state) const;
-  void on_call_for_region_model (const exploded_graph &eg,
+  void on_call_for_region_model (exploded_graph &eg,
 				 const gcall *stmt,
 				 const program_state &old_state,
 				 program_state *new_state) const;
-  void on_return_for_region_model (const exploded_graph &eg,
+  void on_return_for_region_model (exploded_graph &eg,
 				   const greturn *return_,
 				   const program_state &old_state,
 				   program_state *new_state) const;
@@ -804,6 +804,7 @@ class exploded_node : public dnode
   {
     return get_point ().get_stack_depth ();
   }
+  const gimple *get_stmt () const { return get_point ().get_stmt (); }
 
   const program_state &get_state () const { return m_ps.m_state; }
 
@@ -904,9 +905,9 @@ struct eg_hash_map_traits
 class saved_diagnostic
 {
 public:
-  saved_diagnostic (const state_machine &sm,
+  saved_diagnostic (const state_machine *sm,
 		    const exploded_node *enode,
-		    const supernode *node, const gimple *stmt,
+		    const supernode *snode, const gimple *stmt,
 		    stmt_finder *stmt_finder,
 		    tree var, state_machine::state_t state,
 		    pending_diagnostic *d);
@@ -914,9 +915,9 @@ public:
 
   bool operator== (const saved_diagnostic &other) const
   {
-    return (&m_sm == &other.m_sm
+    return (m_sm == other.m_sm
 	    /* We don't compare m_enode.  */
-	    && m_node == other.m_node
+	    && m_snode == other.m_snode
 	    && m_stmt == other.m_stmt
 	    /* We don't compare m_stmt_finder.  */
 	    && m_var == other.m_var
@@ -925,9 +926,9 @@ public:
   }
 
   //private:
-  const state_machine &m_sm;
+  const state_machine *m_sm;
   const exploded_node *m_enode;
-  const supernode *m_node;
+  const supernode *m_snode;
   const gimple *m_stmt;
   stmt_finder *m_stmt_finder;
   tree m_var;
@@ -949,13 +950,18 @@ class diagnostic_manager : public log_user
 public:
   diagnostic_manager (logger *logger, int verbosity);
 
-  void add_diagnostic (const state_machine &sm,
+  void add_diagnostic (const state_machine *sm,
 		       const exploded_node *enode,
-		       const supernode *node, const gimple *stmt,
+		       const supernode *snode, const gimple *stmt,
 		       stmt_finder *finder,
 		       tree var, state_machine::state_t state,
 		       pending_diagnostic *d);
 
+  void add_diagnostic (const exploded_node *enode,
+		       const supernode *snode, const gimple *stmt,
+		       stmt_finder *finder,
+		       pending_diagnostic *d);
+
   void emit_saved_diagnostics (const exploded_graph &eg);
   void emit_saved_diagnostic (const exploded_graph &eg,
 			      const saved_diagnostic &sd,
@@ -964,7 +970,7 @@ public:
 			      int num_dupes);
 
   void prune_path (checker4_path *path,
-		   const state_machine &sm,
+		   const state_machine *sm,
 		   tree var, state_machine::state_t state) const;
 
 private:
@@ -1061,6 +1067,12 @@ public:
   bool find_stmt_backwards (const gimple *search_stmt,
 			    int *out_idx) const;
 
+  exploded_node *get_final_enode () const;
+
+  void dump_to_pp (pretty_printer *pp) const;
+  void dump (FILE *fp) const;
+  void dump () const;
+
   auto_vec<const exploded_edge *> m_edges;
 };
 
@@ -1584,7 +1596,7 @@ public:
   int get_verbosity () const;
 #endif
 
-  void add_final_event (const state_machine &sm,
+  void add_final_event (const state_machine *sm,
 			const exploded_node *enode, const gimple *stmt,
 			tree var, state_machine::state_t state,
 			pending_diagnostic *pd)
@@ -1593,10 +1605,10 @@ public:
     label_text ev_desc = pd->describe_final_event (var, state);
     if (ev_desc.m_buffer)
       {
-	if (flag_analyzer_verbose_state_changes)
+	if (sm && flag_analyzer_verbose_state_changes)
 	  {
 	    msg = print ("%s (%qE is in state %qs)", ev_desc.m_buffer,
-			 var, sm.get_state_name (state));
+			 var, sm->get_state_name (state));
 	    ev_desc.maybe_free ();
 	  }
 	else
@@ -1604,8 +1616,11 @@ public:
       }
     else
       {
-	msg = print ("here (%qE is in state %qs)", var,
-		     sm.get_state_name (state));
+	if (sm)
+	  msg = print ("here (%qE is in state %qs)", var,
+		       sm->get_state_name (state));
+	else
+	  msg = print ("here");
 	// FIXME: ideally would show the full message here, too?
       }
     checker4_event *end_of_path
@@ -2211,7 +2226,7 @@ public:
     if (state == current)
       {
 	m_eg.get_diagnostic_manager ().add_diagnostic
-	  (m_sm, m_node, node, stmt, m_stmt_finder,
+	  (&m_sm, m_node, node, stmt, m_stmt_finder,
 	   var, state, d);
       }
     else
@@ -2758,6 +2773,39 @@ fndecl_has_gimple_body_p (tree fndecl)
 
 /* FIXME.  */
 
+class impl_region_model_context : public region_model_context, public log_user
+{
+ public:
+  impl_region_model_context (exploded_graph &eg,
+			     const exploded_node *enode,
+			     /* FIXME: should we be getting the ECs from the
+				old state, rather than the new?  */
+			     program_state *new_state,
+
+			     stmt_finder *stmt_finder = NULL)
+  : log_user (eg.get_logger ()), m_eg (eg), m_enode (enode),
+    m_new_state (new_state),
+    m_stmt_finder (stmt_finder)
+  {
+  }
+
+  void warn (pending_diagnostic *d) FINAL OVERRIDE
+  {
+    JIT_LOG_FUNC (get_logger ());
+    m_eg.get_diagnostic_manager ().add_diagnostic
+      (m_enode, m_enode->get_supernode (),
+       m_enode->get_stmt (),
+       m_stmt_finder, d);
+  }
+
+  exploded_graph &m_eg;
+  const exploded_node *m_enode;
+  program_state *m_new_state;
+  impl_4::stmt_finder *m_stmt_finder;
+};
+
+/* FIXME.  */
+
 program_state
 exploded_node::on_stmt (exploded_graph &eg) const
 {
@@ -2776,6 +2824,8 @@ exploded_node::on_stmt (exploded_graph &eg) const
   auto_cfun sentinel (point.get_function ());
   input_location = stmt->location;
 
+  impl_region_model_context ctxt (eg, this, &new_state, NULL);
+
   int sm_idx;
   sm_state_map<path_var> *smap;
   FOR_EACH_VEC_ELT (old_state.m_checker_states, sm_idx, smap)
@@ -2813,6 +2863,7 @@ exploded_node::on_stmt (exploded_graph &eg) const
 	default:
 	  break;
 
+	case FLOAT_EXPR:
 	case NOP_EXPR:
 	case SSA_NAME:
 	case INTEGER_CST:
@@ -2899,15 +2950,16 @@ exploded_node::on_stmt (exploded_graph &eg) const
       else if (is_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.  */
+	     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
 	    = (new_state.m_region_model
 	       ? new_state.m_region_model->eval_condition (t_arg,
 							   NE_EXPR,
-							   integer_zero_node)
+							   integer_zero_node,
+							   &ctxt)
 	       : new_ps->dump_condition (t_arg, NE_EXPR, integer_zero_node));
 	  warning_at (call->location, 0, "%s", t.as_string ());
 	}
@@ -2951,37 +3003,46 @@ exploded_node::on_assignment (const exploded_graph &eg,
 /* FIXME.  */
 
 void
-exploded_node::on_assignment_for_region_model (const exploded_graph &eg,
+exploded_node::on_assignment_for_region_model (exploded_graph &eg,
 					       const gassign *stmt,
-					       const program_state &old_state,
+					       const program_state &/*old_state*/,
 					       program_state *new_state) const
 {
   if (new_state->m_region_model)
-    new_state->m_region_model->on_assignment (stmt);
+    {
+      impl_region_model_context ctxt (eg, this, new_state, NULL);
+      new_state->m_region_model->on_assignment (stmt, &ctxt);
+    }
 }
 
 /* FIXME.  */
 
 void
-exploded_node::on_call_for_region_model (const exploded_graph &eg,
+exploded_node::on_call_for_region_model (exploded_graph &eg,
 					 const gcall *stmt,
-					 const program_state &old_state,
+					 const program_state &/*old_state*/,
 					 program_state *new_state) const
 {
   if (new_state->m_region_model)
-    new_state->m_region_model->on_call (stmt);
+    {
+      impl_region_model_context ctxt (eg, this, new_state, NULL);
+      new_state->m_region_model->on_call (stmt, &ctxt);
+    }
 }
 
 /* FIXME.  */
 
 void
-exploded_node::on_return_for_region_model (const exploded_graph &eg,
+exploded_node::on_return_for_region_model (exploded_graph &eg,
 					   const greturn *stmt,
-					   const program_state &old_state,
+					   const program_state &/*old_state*/,
 					   program_state *new_state) const
 {
   if (new_state->m_region_model)
-    new_state->m_region_model->on_return (stmt);
+    {
+      impl_region_model_context ctxt (eg, this, new_state, NULL);
+      new_state->m_region_model->on_return (stmt, &ctxt);
+    }
 }
 
 /* Copy SM state (if any) from RHS to LHS.  */
@@ -3371,9 +3432,12 @@ program_state::on_edge (exploded_graph &eg,
       return false;
     }
 
+  impl_region_model_context ctxt (eg, &enode, this, NULL);
+
   if (m_region_model)
     if (!m_region_model->apply_constraints_for_edge (*succ,
-						     last_stmt))
+						     last_stmt,
+						     &ctxt))
       {
 	eg.log ("edge to SN: %i is impossible due to region_model constraints",
 		succ->m_dest->m_index);
@@ -3601,13 +3665,13 @@ stats::dump (FILE *out) const
 
 /* FIXME.  */
 
-saved_diagnostic::saved_diagnostic (const state_machine &sm,
+saved_diagnostic::saved_diagnostic (const state_machine *sm,
 				    const exploded_node *enode,
-				    const supernode *node, const gimple *stmt,
+				    const supernode *snode, const gimple *stmt,
 				    stmt_finder *stmt_finder,
 				    tree var, state_machine::state_t state,
 				    pending_diagnostic *d)
-: m_sm (sm), m_enode (enode), m_node (node), m_stmt (stmt),
+: m_sm (sm), m_enode (enode), m_snode (snode), m_stmt (stmt),
  /* stmt_finder could be on-stack; we want our own copy that can
     outlive that.  */
   m_stmt_finder (stmt_finder ? stmt_finder->clone () : NULL),
@@ -3638,21 +3702,33 @@ diagnostic_manager::diagnostic_manager (logger *logger, int verbosity)
 /* FIXME.  */
 
 void
-diagnostic_manager::add_diagnostic (const state_machine &sm,
+diagnostic_manager::add_diagnostic (const state_machine *sm,
 				    const exploded_node *enode,
-				    const supernode *node, const gimple *stmt,
+				    const supernode *snode, const gimple *stmt,
 				    stmt_finder *finder,
 				    tree var, state_machine::state_t state,
 				    pending_diagnostic *d)
 {
+  JIT_LOG_FUNC (get_logger ());
   // TODO: potentially do them directly, vs saved
   saved_diagnostic *sd
-    = new saved_diagnostic (sm, enode, node, stmt, finder, var, state, d);
+    = new saved_diagnostic (sm, enode, snode, stmt, finder, var, state, d);
   m_saved_diagnostics.safe_push (sd);
   if (get_logger ())
     log ("adding saved diagnostic %i at SN %i: %qs",
 	 m_saved_diagnostics.length () - 1,
-	 node->m_index, d->get_kind ());
+	 snode->m_index, d->get_kind ());
+}
+
+/* FIXME.  */
+
+void
+diagnostic_manager::add_diagnostic (const exploded_node *enode,
+				    const supernode *snode, const gimple *stmt,
+				    stmt_finder *finder,
+				    pending_diagnostic *d)
+{
+  add_diagnostic (NULL, enode, snode, stmt, finder, NULL_TREE, 0, d);
 }
 
 /* FIXME.  */
@@ -3921,6 +3997,7 @@ diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg,
 					   int num_dupes)
 {
   JIT_LOG_SCOPE (get_logger ());
+  log ("sd: %qs at SN: %i", sd.m_d->get_kind (), sd.m_snode->m_index);
   log ("num dupes: %i", num_dupes);
 
   pretty_printer *pp = global_dc->printer->clone ();
@@ -4062,7 +4139,10 @@ diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg,
     }
 
   prune_path (&emission_path, sd.m_sm, sd.m_var, sd.m_state);
-  emission_path.add_final_event (sd.m_sm, sd.m_enode, stmt,
+  /* We use the final enode from the epath, which might be different from
+     the sd.m_enode, as the dedupe code doesn't care about enodes, just
+     snodes.  */
+  emission_path.add_final_event (sd.m_sm, epath.get_final_enode (), stmt,
 				 sd.m_var, sd.m_state, sd.m_d);
   emission_path.prepare_for_emission (sd.m_d);
 
@@ -4070,7 +4150,7 @@ diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg,
   rich_loc.set_path (&emission_path);
 
   auto_diagnostic_group d;
-  auto_cfun sentinel (sd.m_node->m_fun);
+  auto_cfun sentinel (sd.m_snode->m_fun);
   sd.m_d->emit (&rich_loc);
   if (num_dupes > 0)
     inform_n (stmt->location, num_dupes,
@@ -4083,7 +4163,7 @@ diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg,
 
 void
 diagnostic_manager::prune_path (checker4_path *path,
-				const state_machine &sm,
+				const state_machine *sm,
 				tree var,
 				state_machine::state_t state
 				  ATTRIBUTE_UNUSED) const
@@ -4099,8 +4179,13 @@ diagnostic_manager::prune_path (checker4_path *path,
     {
       checker4_event *base_event = path->m_events[idx];
       if (get_logger ())
-	log  ("considering event %i, with var: %qE, state: %qs",
-	      idx, var, sm.get_state_name (state));
+	{
+	  if (sm)
+	    log ("considering event %i, with var: %qE, state: %qs",
+		 idx, var, sm->get_state_name (state));
+	  else
+	    log ("considering event %i", idx);
+	}
       switch (base_event->m_kind)
 	{
 	default:
@@ -4144,8 +4229,13 @@ diagnostic_manager::prune_path (checker4_path *path,
 	      }
 	    else if (m_verbosity < 2)
 	      {
-		log ("filtering event %i: state change to %qE unrelated to %qE",
-		     idx, state_change->m_var, var);
+		if (var)
+		  log ("filtering event %i:"
+		       " state change to %qE unrelated to %qE",
+		       idx, state_change->m_var, var);
+		else
+		  log ("filtering event %i: state change to %qE",
+		       idx, state_change->m_var);
 		path->delete_event (idx);
 	      }
 	  }
@@ -4167,7 +4257,7 @@ diagnostic_manager::prune_path (checker4_path *path,
 		  const supernode *dest = event->m_edge->m_dest;
 		  /* Do we have an SSA_NAME defined via a phi node in
 		     the dest CFG node?  */
-		  if (TREE_CODE (var) == SSA_NAME)
+		  if (var && TREE_CODE (var) == SSA_NAME)
 		    if (SSA_NAME_DEF_STMT (var)->bb == dest->m_bb)
 		      {
 			if (const gphi *phi
@@ -4530,7 +4620,9 @@ exploded_graph::process_node (exploded_node *node)
 	    if (next_state.m_region_model)
 	      {
 		bool is_back_edge = last_cfg_superedge->back_edge_p ();
-		next_state.m_region_model->handle_phi (lhs, src, is_back_edge);
+		impl_region_model_context ctxt (*this, node, &next_state, NULL);
+		next_state.m_region_model->handle_phi (lhs, src, is_back_edge,
+						       &ctxt);
 	      }
 	  }
 
@@ -4736,6 +4828,52 @@ exploded_path::find_stmt_backwards (const gimple *search_stmt,
   return false;
 }
 
+/* FIXME.  */
+
+exploded_node *
+exploded_path::get_final_enode () const
+{
+  gcc_assert (m_edges.length () > 0);
+  return m_edges[m_edges.length () - 1]->m_dest;
+}
+
+/* FIXME.  */
+
+void
+exploded_path::dump_to_pp (pretty_printer *pp) const
+{
+  for (unsigned i = 0; i < m_edges.length (); i++)
+    {
+      const exploded_edge *eedge = m_edges[i];
+      pp_printf (pp, "m_edges[%i]: EN %i -> EN %i",
+		 i,
+		 eedge->m_src->m_index,
+		 eedge->m_dest->m_index);
+      pp_newline (pp);
+    }
+}
+
+/* FIXME.  */
+
+void
+exploded_path::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
+exploded_path::dump () const
+{
+  dump (stderr);
+}
+
 ////////////////////////////////////////////////////////////////////////////
 
 /* shortest_paths's constructor.
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 770f145..dfd8377 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -44,6 +44,7 @@
 #include "tree-diagnostic.h"
 #include "diagnostic-color.h"
 #include "constraint-manager.h"
+#include "hardcoded-state-machine.h" // for pending_diagnostic
 
 ////////////////////////////////////////////////////////////////////////////
 
@@ -81,7 +82,7 @@ class impl_constraint_manager : public constraint_manager
   svalue_id get_sid_for_constant (tree cst) const FINAL OVERRIDE
   {
     gcc_assert (CONSTANT_CLASS_P (cst));
-    return m_model->get_rvalue (cst);
+    return m_model->get_rvalue (cst, NULL);
   }
 
   int get_num_svalues () const FINAL OVERRIDE
@@ -1242,7 +1243,8 @@ stack_region::get_current_frame_id () const
    Accumulate stats on purged entities into STATS.  */
 
 svalue_id
-stack_region::pop_frame (region_model *model, purge_stats *stats)
+stack_region::pop_frame (region_model *model, purge_stats *stats,
+			 region_model_context *ctxt)
 {
   gcc_assert (m_frame_rids.length () > 0);
 
@@ -1254,7 +1256,7 @@ stack_region::pop_frame (region_model *model, purge_stats *stats)
   tree fndecl = frame->get_function ()->decl;
   tree result = DECL_RESULT (fndecl);
   if (result)
-    result_sid = model->get_rvalue (result);
+    result_sid = model->get_rvalue (result, ctxt);
 
   /* Pop the frame.  */
   m_frame_rids.pop ();
@@ -1502,10 +1504,11 @@ root_region::get_current_frame_id (const region_model &model) const
 /* FIXME.  */
 
 svalue_id
-root_region::pop_frame (region_model *model, purge_stats *out)
+root_region::pop_frame (region_model *model, purge_stats *out,
+			region_model_context *ctxt)
 {
   stack_region *stack = model->get_region <stack_region> (m_stack_rid);
-  return stack->pop_frame (model, out);
+  return stack->pop_frame (model, out, ctxt);
 }
 
 /* FIXME.  */
@@ -1881,12 +1884,12 @@ region_model::validate () const
 /* FIXME.  */
 
 void
-region_model::on_assignment (const gassign *assign)
+region_model::on_assignment (const gassign *assign, region_model_context *ctxt)
 {
   tree lhs = gimple_assign_lhs (assign);
   tree rhs1 = gimple_assign_rhs1 (assign);
 
-  region_id lhs_rid = get_lvalue (lhs);
+  region_id lhs_rid = get_lvalue (lhs, ctxt);
 
   if (lhs_rid.null_p ())
     return;
@@ -1912,8 +1915,8 @@ region_model::on_assignment (const gassign *assign)
 	tree ptr = rhs1;
 	tree offset = gimple_assign_rhs2 (assign);
 
-	region_id buffer_rid = deref_rvalue (ptr);
-	svalue_id offset_sid = get_rvalue (offset);
+	region_id buffer_rid = deref_rvalue (ptr, ctxt);
+	svalue_id offset_sid = get_rvalue (offset, ctxt);
 	/* FIXME: offset is in bytes; we presumably want to
 	   map back to an index.  */
 
@@ -1934,14 +1937,14 @@ region_model::on_assignment (const gassign *assign)
     case ADDR_EXPR:
       {
 	/* LHS = &RHS;  */
-	svalue_id ptr_sid = get_rvalue (rhs1);
+	svalue_id ptr_sid = get_rvalue (rhs1, ctxt);
 	set_value (lhs_rid, ptr_sid);
       }
       break;
 
     case MEM_REF:
       {
-	region_id rhs_rid = get_lvalue (rhs1);
+	region_id rhs_rid = get_lvalue (rhs1, ctxt);
 	svalue_id rhs_sid
 	  = get_region (rhs_rid)->get_value (*this, true);
 	set_value (lhs_rid, rhs_sid);
@@ -1953,11 +1956,13 @@ region_model::on_assignment (const gassign *assign)
     case ARRAY_REF:
       {
 	/* LHS = RHS;  */
-	svalue_id cst_sid = get_rvalue (rhs1);
+	svalue_id cst_sid = get_rvalue (rhs1, ctxt);
 	set_value (lhs_rid, cst_sid);
       }
       break;
 
+    case FIX_TRUNC_EXPR:
+    case FLOAT_EXPR:
     case NOP_EXPR:
       // cast: TODO
       // fall though for now
@@ -1966,7 +1971,7 @@ region_model::on_assignment (const gassign *assign)
     case PARM_DECL:
       {
 	/* LHS = VAR;  */
-	region_id rhs_rid = get_lvalue (rhs1);
+	region_id rhs_rid = get_lvalue (rhs1, ctxt);
 	svalue_id var_sid = get_region (rhs_rid)->get_value (*this, true);
 	set_value (lhs_rid, var_sid);
       }
@@ -1982,15 +1987,16 @@ region_model::on_assignment (const gassign *assign)
 	tree rhs2 = gimple_assign_rhs2 (assign);
 
 	// TODO: constraints between svalues
-	svalue_id rhs1_sid = get_rvalue (rhs1);
-	svalue_id rhs2_sid = get_rvalue (rhs2);
+	svalue_id rhs1_sid = get_rvalue (rhs1, ctxt);
+	svalue_id rhs2_sid = get_rvalue (rhs2, ctxt);
 
 	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));
+				 : boolean_false_node,
+				 ctxt));
       }
       break;
 
@@ -2021,8 +2027,8 @@ region_model::on_assignment (const gassign *assign)
 	/* Binary ops.  */
 	tree rhs2 = gimple_assign_rhs2 (assign);
 
-	svalue_id rhs1_sid = get_rvalue (rhs1);
-	svalue_id rhs2_sid = get_rvalue (rhs2);
+	svalue_id rhs1_sid = get_rvalue (rhs1, ctxt);
+	svalue_id rhs2_sid = get_rvalue (rhs2, ctxt);
 
 	if (tree rhs1_cst = maybe_get_constant (rhs1_sid))
 	  if (tree rhs2_cst = maybe_get_constant (rhs2_sid))
@@ -2044,7 +2050,7 @@ region_model::on_assignment (const gassign *assign)
     case COMPONENT_REF:
       {
 	/* LHS = op0.op1;  */
-	region_id child_rid = get_lvalue (rhs1);
+	region_id child_rid = get_lvalue (rhs1, ctxt);
 	svalue_id child_sid = get_region (child_rid)->get_value (*this, true);
 	set_value (lhs_rid, child_sid);
       }
@@ -2055,13 +2061,13 @@ region_model::on_assignment (const gassign *assign)
 /* FIXME.  */
 
 void
-region_model::on_call (const gcall *call)
+region_model::on_call (const gcall *call, region_model_context *ctxt)
 {
   region_id lhs_rid;
   tree lhs_type = NULL_TREE;
   if (tree lhs = gimple_call_lhs (call))
     {
-      lhs_rid = get_lvalue (lhs);
+      lhs_rid = get_lvalue (lhs, ctxt);
       lhs_type = TREE_TYPE (lhs);
     }
 
@@ -2098,7 +2104,7 @@ region_model::on_call (const gcall *call)
     }
   else if (is_named_call_p (call, "strlen", 1))
     {
-      region_id buf_rid = deref_rvalue (gimple_call_arg (call, 0));
+      region_id buf_rid = deref_rvalue (gimple_call_arg (call, 0), ctxt);
       svalue_id buf_sid = get_region (buf_rid)->get_value (*this, true);
       if (tree cst_expr = maybe_get_constant (buf_sid))
 	{
@@ -2136,20 +2142,21 @@ region_model::on_call (const gcall *call)
 /* FIXME.  */
 
 void
-region_model::on_return (const greturn *return_stmt)
+region_model::on_return (const greturn *return_stmt, region_model_context *ctxt)
 {
   tree callee = get_current_function ()->decl;
   tree lhs = DECL_RESULT (callee);
   tree rhs = gimple_return_retval (return_stmt);
 
   if (lhs && rhs)
-    set_value (get_lvalue (lhs), get_rvalue (rhs));
+    set_value (get_lvalue (lhs, ctxt), get_rvalue (rhs, ctxt));
 }
 
 /* FIXME.  */
 
 void
-region_model::handle_phi (tree lhs, tree rhs, bool is_back_edge)
+region_model::handle_phi (tree lhs, tree rhs, bool is_back_edge,
+			  region_model_context *ctxt)
 {
   /* For now, don't bother tracking the .MEM SSA names.  */
   if (tree var = SSA_NAME_VAR (lhs))
@@ -2157,7 +2164,7 @@ region_model::handle_phi (tree lhs, tree rhs, bool is_back_edge)
       if (VAR_DECL_IS_VIRTUAL_OPERAND (var))
 	return;
 
-  svalue_id rhs_sid = get_rvalue (rhs);
+  svalue_id rhs_sid = get_rvalue (rhs, ctxt);
 
   if (is_back_edge && get_svalue (rhs_sid)->get_kind () != SK_UNKNOWN)
     {
@@ -2169,16 +2176,16 @@ region_model::handle_phi (tree lhs, tree rhs, bool is_back_edge)
 	 redundant unknown values; hence we need to purge svalues
 	 before inserting the state into the exploded graph, to
 	 collect unused svalues.  */
-      set_to_new_unknown_value (get_lvalue (lhs), TREE_TYPE (lhs));
+      set_to_new_unknown_value (get_lvalue (lhs, ctxt), TREE_TYPE (lhs));
     }
   else
-    set_value (get_lvalue (lhs), rhs_sid);
+    set_value (get_lvalue (lhs, ctxt), rhs_sid);
 }
 
 /* FIXME.  */
 
 region_id
-region_model::get_lvalue (tree expr)
+region_model::get_lvalue (tree expr, region_model_context *ctxt)
 {
   switch (TREE_CODE (expr))
     {
@@ -2192,8 +2199,8 @@ region_model::get_lvalue (tree expr)
 	// TODO: operands 2 and 3, if present:
 	gcc_assert (TREE_OPERAND (expr, 2) == NULL_TREE);
 	gcc_assert (TREE_OPERAND (expr, 3) == NULL_TREE);
-	region_id array_rid = get_lvalue (array);
-	svalue_id index_sid = get_rvalue (index);
+	region_id array_rid = get_lvalue (array, ctxt);
+	svalue_id index_sid = get_rvalue (index, ctxt);
 	map_region *array_region = get_region<map_region> (array_rid);
 	tree element_type = TREE_TYPE (TREE_TYPE (array));
 	region_id element_rid
@@ -2210,7 +2217,7 @@ region_model::get_lvalue (tree expr)
 	tree offset = TREE_OPERAND (expr, 1);
 	// FIXME: what about nonzero offset?
 
-	return deref_rvalue (ptr);
+	return deref_rvalue (ptr, ctxt);
       }
       break; // FIXME
 
@@ -2251,7 +2258,7 @@ region_model::get_lvalue (tree expr)
 	/* obj.field  */
 	tree obj = TREE_OPERAND (expr, 0);
 	tree field = TREE_OPERAND (expr, 1);
-	region_id obj_rid = get_lvalue (obj);
+	region_id obj_rid = get_lvalue (obj, ctxt);
 	map_region *obj_region = get_region<map_region> (obj_rid);
 	region_id child_rid = obj_region->get_or_create (this, obj_rid, field,
 							 TREE_TYPE (field));
@@ -2276,7 +2283,7 @@ region_model::get_lvalue (tree expr)
 /* FIXME.  */
 
 svalue_id
-region_model::get_rvalue (tree expr)
+region_model::get_rvalue (tree expr, region_model_context *ctxt)
 {
   switch (TREE_CODE (expr))
     {
@@ -2288,14 +2295,14 @@ region_model::get_rvalue (tree expr)
 	/* "&EXPR".  */
 	if (TREE_CODE (TREE_OPERAND (expr, 0)) == FUNCTION_DECL)
 	  return get_svalue_for_fndecl (TREE_OPERAND (expr, 0));
-	region_id expr_rid = get_lvalue (TREE_OPERAND (expr, 0));
+	region_id expr_rid = get_lvalue (TREE_OPERAND (expr, 0), ctxt);
 	return get_svalue_for_region (expr_rid);
       }
       break;
 
     case ARRAY_REF:
       {
-	region_id element_rid = get_lvalue (expr);
+	region_id element_rid = get_lvalue (expr, ctxt);
 	return get_region (element_rid)->get_value (*this, true);
       }
 
@@ -2309,7 +2316,7 @@ region_model::get_rvalue (tree expr)
     case PARM_DECL:
     case RESULT_DECL:
       {
-	region_id var_rid = get_lvalue (expr);
+	region_id var_rid = get_lvalue (expr, ctxt);
 	return get_region (var_rid)->get_value (*this, true);
       }
     }
@@ -2352,12 +2359,72 @@ region_model::get_svalue_for_fndecl (tree fndecl)
   return get_svalue_for_constant (fndecl);
 }
 
+class deref_diagnostic : public pending_diagnostic_subclass<deref_diagnostic>
+{
+public:
+  deref_diagnostic (tree ptr)
+  : m_ptr (ptr)
+  {}
+
+  const char *get_kind () const FINAL OVERRIDE { return "deref_diagnostic"; }
+
+  bool operator== (const deref_diagnostic &other) const
+  {
+    return m_ptr == other.m_ptr;
+    // FIXME: and m_sval ?
+  }
+
+protected:
+  tree m_ptr;
+};
+
+class deref_of_poisoned_pointer : public deref_diagnostic
+{
+public:
+  deref_of_poisoned_pointer (tree ptr, enum poison_kind pkind)
+  : deref_diagnostic (ptr),
+    m_pkind (pkind)
+  {}
+
+  void emit (rich_location *rich_loc) FINAL OVERRIDE
+  {
+    metadata m;
+    // FIXME: CWEs?
+    switch (m_pkind)
+      {
+      default:
+	gcc_unreachable ();
+      case POISON_KIND_UNINIT:
+	warning_at (rich_loc, m,
+		    OPT_Wanalyzer_dereference_of_uninitialized_pointer,
+		    "dereference of uninitialized pointer %qE",
+		    m_ptr);
+	break;
+      case POISON_KIND_FREED:
+	warning_at (rich_loc, m,
+		    OPT_Wanalyzer_dereference_of_freed_pointer,
+		    "dereference of freed pointer %qE",
+		    m_ptr);
+	break;
+      case POISON_KIND_POPPED_STACK:
+	warning_at (rich_loc, m,
+		    OPT_Wanalyzer_dereference_of_pointer_in_stale_stack_frame,
+		    "dereference of pointer %qE within stale stack frame",
+		    m_ptr);
+	break;
+      }
+  }
+
+private:
+  enum poison_kind m_pkind;
+};
+
 /* FIXME.  */
 
 region_id
-region_model::deref_rvalue (tree ptr)
+region_model::deref_rvalue (tree ptr, region_model_context *ctxt)
 {
-  svalue_id ptr_sid = get_rvalue (ptr);
+  svalue_id ptr_sid = get_rvalue (ptr, ctxt);
   gcc_assert (!ptr_sid.null_p ());
   svalue *ptr_svalue = get_svalue (ptr_sid);
   gcc_assert (ptr_svalue);
@@ -2392,23 +2459,12 @@ region_model::deref_rvalue (tree ptr)
 
     case SK_POISONED:
       {
+	gcc_assert (ctxt);
 	poisoned_svalue *poisoned_sval
 	  = ptr_svalue->dyn_cast_poisoned_svalue ();
 	// FIXME: ^^ as_a
-	switch (poisoned_sval->get_poison_kind ())
-	  {
-	  default:
-	    gcc_unreachable ();
-	  case POISON_KIND_UNINIT:
-	    warning (0, "deref of uninitialized pointer");
-	    break;
-	  case POISON_KIND_FREED:
-	    warning (0, "use-after-free");
-	    break;
-	  case POISON_KIND_POPPED_STACK:
-	    warning (0, "use of memory within popped stack");
-	    break;
-	  }
+	enum poison_kind pkind = poisoned_sval->get_poison_kind ();
+	ctxt->warn (new deref_of_poisoned_pointer (ptr, pkind));
 	goto create_symbolic_region;
       }
 
@@ -2584,10 +2640,11 @@ region_model::eval_condition (svalue_id lhs_sid,
 /* FIXME.  */
 
 bool
-region_model::add_constraint (tree lhs, enum tree_code op, tree rhs)
+region_model::add_constraint (tree lhs, enum tree_code op, tree rhs,
+			      region_model_context *ctxt)
 {
-  svalue_id lhs_sid = get_rvalue (lhs);
-  svalue_id rhs_sid = get_rvalue (rhs);
+  svalue_id lhs_sid = get_rvalue (lhs, ctxt);
+  svalue_id rhs_sid = get_rvalue (rhs, ctxt);
 
   tristate t_cond = eval_condition (lhs_sid, op, rhs_sid);
 
@@ -2611,9 +2668,10 @@ region_model::add_constraint (tree lhs, enum tree_code op, tree rhs)
 tristate
 region_model::eval_condition (tree lhs,
 			      enum tree_code op,
-			      tree rhs)
+			      tree rhs,
+			      region_model_context *ctxt)
 {
-  return eval_condition (get_rvalue (lhs), op, get_rvalue (rhs));
+  return eval_condition (get_rvalue (lhs, ctxt), op, get_rvalue (rhs, ctxt));
 }
 
 /* If SID is a constant value, return the underlying tree constant.
@@ -2648,7 +2706,8 @@ region_model::set_to_new_unknown_value (region_id dst_rid, tree type)
 
 bool
 region_model::apply_constraints_for_edge (const superedge &edge,
-					  const gimple *last_stmt)
+					  const gimple *last_stmt,
+					  region_model_context *ctxt)
 {
   /* Handle frame updates for interprocedural edges.  */
   switch (edge.m_kind)
@@ -2661,7 +2720,7 @@ region_model::apply_constraints_for_edge (const superedge &edge,
 	const call_superedge *call_edge
 	  = edge.dyn_cast_call_superedge ();
 	// FIXME: should be an as_a
-	update_for_call_superedge (*call_edge);
+	update_for_call_superedge (*call_edge, ctxt);
       }
       break;
 
@@ -2670,7 +2729,7 @@ region_model::apply_constraints_for_edge (const superedge &edge,
 	const return_superedge *return_edge
 	  = edge.dyn_cast_return_superedge ();
 	// FIXME: should be an as_a
-	update_for_return_superedge (*return_edge);
+	update_for_return_superedge (*return_edge, ctxt);
       }
       break;
     }
@@ -2681,11 +2740,11 @@ region_model::apply_constraints_for_edge (const superedge &edge,
     return true;
 
   if (const gcond *cond_stmt = dyn_cast <const gcond *> (last_stmt))
-    return apply_constraints_for_gcond (edge, cond_stmt);
+    return apply_constraints_for_gcond (edge, cond_stmt, ctxt);
 
   // FIXME: some linker issues with is_a_helper const
   if (const gswitch *switch_stmt = dyn_cast <gswitch *> (const_cast <gimple *> (last_stmt)))
-    return apply_constraints_for_gswitch (edge, switch_stmt);
+    return apply_constraints_for_gswitch (edge, switch_stmt,ctxt);
 
   return true;
 }
@@ -2696,7 +2755,8 @@ region_model::apply_constraints_for_edge (const superedge &edge,
    caller's frame.  */
 
 void
-region_model::update_for_call_superedge (const call_superedge &call_edge)
+region_model::update_for_call_superedge (const call_superedge &call_edge,
+					 region_model_context *ctxt)
 {
   /* Build a vec of argument svalue_id, using the current top
      frame for resolving tree expressions.  */
@@ -2710,7 +2770,7 @@ region_model::update_for_call_superedge (const call_superedge &call_edge)
        parm = DECL_CHAIN (parm), ++i)
     {
       tree arg = gimple_call_arg (call_stmt, i);
-      arg_sids.safe_push (get_rvalue (arg));
+      arg_sids.safe_push (get_rvalue (arg, ctxt));
     }
 
   push_frame (call_edge.get_callee_function (), &arg_sids);
@@ -2721,10 +2781,11 @@ region_model::update_for_call_superedge (const call_superedge &call_edge)
    the call (if any).  */
 
 void
-region_model::update_for_return_superedge (const return_superedge &return_edge)
+region_model::update_for_return_superedge (const return_superedge &return_edge,
+					   region_model_context *ctxt)
 {
   purge_stats stats;
-  svalue_id result_sid = pop_frame (&stats);
+  svalue_id result_sid = pop_frame (&stats, ctxt);
   // TODO: do something with the stats?
 
   // FIXME: result_sid could change meaning within pop_frame;
@@ -2734,7 +2795,7 @@ region_model::update_for_return_superedge (const return_superedge &return_edge)
   gcall *call_stmt = return_edge.get_call_stmt ();
   tree lhs = gimple_call_lhs (call_stmt);
   if (lhs)
-    set_value (get_lvalue (lhs), result_sid);
+    set_value (get_lvalue (lhs, ctxt), result_sid);
 }
 
 /* Given a true or false edge guarded by conditional statement COND_STMT,
@@ -2744,7 +2805,8 @@ region_model::update_for_return_superedge (const return_superedge &return_edge)
 
 bool
 region_model::apply_constraints_for_gcond (const superedge &edge,
-					   const gcond *cond_stmt)
+					   const gcond *cond_stmt,
+					   region_model_context *ctxt)
 {
   ::edge cfg_edge = edge.get_any_cfg_edge ();
   gcc_assert (cfg_edge != NULL);
@@ -2755,14 +2817,15 @@ region_model::apply_constraints_for_gcond (const superedge &edge,
   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);
+  return add_constraint (lhs, op, rhs, ctxt);
 }
 
 /* FIXME.  */
 
 bool
 region_model::apply_constraints_for_gswitch (const superedge &edge,
-					     const gswitch *switch_stmt)
+					     const gswitch *switch_stmt,
+					     region_model_context *ctxt)
 {
   // FIXME: use an as_a for this cast:
   const switch_cfg_superedge &switch_edge = (const switch_cfg_superedge &)edge;
@@ -2776,13 +2839,13 @@ region_model::apply_constraints_for_gswitch (const superedge &edge,
       if (upper_bound)
 	{
 	  /* Range.  */
-	  if (!add_constraint (index, GE_EXPR, lower_bound))
+	  if (!add_constraint (index, GE_EXPR, lower_bound, ctxt))
 	    return false;
-	  return add_constraint (index, LE_EXPR, upper_bound);
+	  return add_constraint (index, LE_EXPR, upper_bound, ctxt);
 	}
       else
 	/* Single-value.  */
-	return add_constraint (index, EQ_EXPR, lower_bound);
+	return add_constraint (index, EQ_EXPR, lower_bound, ctxt);
     }
   else
     {
@@ -2802,14 +2865,14 @@ region_model::apply_constraints_for_gswitch (const superedge &edge,
 	      /* Exclude this range-valued case.
 		 For now, we just exclude the boundary values.
 		 TODO: exclude the values within the region.  */
-	      if (!add_constraint (index, NE_EXPR, other_lower_bound))
+	      if (!add_constraint (index, NE_EXPR, other_lower_bound, ctxt))
 		return false;
-	      if (!add_constraint (index, NE_EXPR, other_upper_bound))
+	      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))
+	    if (!add_constraint (index, NE_EXPR, other_lower_bound, ctxt))
 	      return false;
 	}
       return true;
@@ -2854,9 +2917,9 @@ region_model::get_current_function () const
 /* FIXME.  */
 
 svalue_id
-region_model::pop_frame (purge_stats *out)
+region_model::pop_frame (purge_stats *out, region_model_context *ctxt)
 {
-  return get_root_region ()->pop_frame (this, out);
+  return get_root_region ()->pop_frame (this, out, ctxt);
 }
 
 /* FIXME.  */
@@ -3221,10 +3284,13 @@ 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_model_context ctxt;
   region_model model;
-  ASSERT_EQ (model.get_rvalue (int_0), model.get_rvalue (int_0));
-  ASSERT_EQ (model.get_rvalue (int_42), model.get_rvalue (int_42));
-  ASSERT_NE (model.get_rvalue (int_0), model.get_rvalue (int_42));
+  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);
 }
 
 /* Check that operator== and hashing works as expected for the
@@ -3346,14 +3412,16 @@ test_purge_svalues ()
 		       get_identifier ("y"),
 		       integer_type_node);
 
+  test_region_model_context ctxt;
   region_model model;
-  model.set_to_new_unknown_value (model.get_lvalue (x), TREE_TYPE (x));
-  model.set_to_new_unknown_value (model.get_lvalue (x), TREE_TYPE (x));
-  model.set_to_new_unknown_value (model.get_lvalue (x), TREE_TYPE (x));
-  model.add_constraint (x, NE_EXPR, int_42);
+  model.set_to_new_unknown_value (model.get_lvalue (x, &ctxt), TREE_TYPE (x));
+  model.set_to_new_unknown_value (model.get_lvalue (x, &ctxt), TREE_TYPE (x));
+  model.set_to_new_unknown_value (model.get_lvalue (x, &ctxt), TREE_TYPE (x));
+  model.add_constraint (x, NE_EXPR, int_42, &ctxt);
 
-  model.set_value (model.get_lvalue (x), model.get_rvalue (int_42));
-  model.add_constraint (y, GT_EXPR, int_0);
+  model.set_value (model.get_lvalue (x, &ctxt),
+		   model.get_rvalue (int_42, &ctxt));
+  model.add_constraint (y, GT_EXPR, int_0, &ctxt);
 
   /* The redundant unknown values should have been purged.  */
   purge_stats purged;
@@ -3367,10 +3435,14 @@ test_purge_svalues ()
   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), tristate::TS_TRUE);
+  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), tristate::TS_TRUE);
+  ASSERT_EQ (model.eval_condition (y, GT_EXPR, int_0, &ctxt),
+	     tristate::TS_TRUE);
+
+  ASSERT_EQ (ctxt.get_num_diagnostics (), 0);
 }
 
 #if 0
@@ -3445,37 +3517,40 @@ test_stack_frames ()
 		       get_identifier ("q"),
 		       ptr_type_node);
 
+  test_region_model_context ctxt;
   region_model model;
 
   /* Push stack frame for "parent_fn".  */
   region_id parent_frame_rid
     = model.push_frame (DECL_STRUCT_FUNCTION (parent_fndecl), NULL);
   ASSERT_EQ (model.get_current_frame_id (), parent_frame_rid);
-  region_id a_in_parent_rid = model.get_lvalue (a);
-  model.set_value (a_in_parent_rid, model.get_rvalue (int_42));
-  model.set_to_new_unknown_value (model.get_lvalue (b), integer_type_node);
-  model.add_constraint (b, LT_EXPR, int_10);
-  ASSERT_EQ (model.eval_condition (b, LT_EXPR, int_10),
+  region_id a_in_parent_rid = model.get_lvalue (a, &ctxt);
+  model.set_value (a_in_parent_rid, model.get_rvalue (int_42, &ctxt));
+  model.set_to_new_unknown_value (model.get_lvalue (b, &ctxt),
+				  integer_type_node);
+  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".  */
   region_id child_frame_rid
     = model.push_frame (DECL_STRUCT_FUNCTION (child_fndecl), NULL);
   ASSERT_EQ (model.get_current_frame_id (), child_frame_rid);
-  region_id x_in_child_rid = model.get_lvalue (x);
-  model.set_value (x_in_child_rid, model.get_rvalue (int_0));
-  model.set_to_new_unknown_value (model.get_lvalue (y), integer_type_node);
-  model.add_constraint (y, NE_EXPR, int_5);
-  ASSERT_EQ (model.eval_condition (y, NE_EXPR, int_5),
+  region_id x_in_child_rid = model.get_lvalue (x, &ctxt);
+  model.set_value (x_in_child_rid, model.get_rvalue (int_0, &ctxt));
+  model.set_to_new_unknown_value (model.get_lvalue (y, &ctxt),
+				  integer_type_node);
+  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.  */
-  region_id p_in_globals_rid = model.get_lvalue (p);
+  region_id p_in_globals_rid = model.get_lvalue (p, &ctxt);
   model.set_value (p_in_globals_rid,
 		   model.get_svalue_for_region (x_in_child_rid));
 
   /* Point another global pointer at p: q = &p.  */
-  region_id q_in_globals_rid = model.get_lvalue (q);
+  region_id q_in_globals_rid = model.get_lvalue (q, &ctxt);
   model.set_value (q_in_globals_rid,
 		   model.get_svalue_for_region (p_in_globals_rid));
 
@@ -3489,7 +3564,7 @@ test_stack_frames ()
 
   /* Pop the "child_fn" frame from the stack.  */
   purge_stats purged;
-  model.pop_frame (&purged);
+  model.pop_frame (&purged, &ctxt);
 
   /* We should have purged the unknown values for x and y. */
   ASSERT_EQ (purged.m_num_svalues, 2);
@@ -3502,29 +3577,29 @@ test_stack_frames ()
   ASSERT_EQ (purged.m_num_constraints, 1);
 
   /* Verify that p has been poisoned.  */
-  svalue *new_p_sval = model.get_svalue (model.get_rvalue (p));
+  svalue *new_p_sval = model.get_svalue (model.get_rvalue (p, &ctxt));
   ASSERT_EQ (new_p_sval->get_kind (), SK_POISONED);
   ASSERT_EQ (new_p_sval->dyn_cast_poisoned_svalue ()->get_poison_kind (),
 	     POISON_KIND_POPPED_STACK);
 
   /* Verify that q still points to p, in spite of the region
      renumbering.  */
-  svalue *new_q_sval = model.get_svalue (model.get_rvalue (q));
+  svalue *new_q_sval = model.get_svalue (model.get_rvalue (q, &ctxt));
   ASSERT_EQ (new_q_sval->get_kind (), SK_REGION);
   ASSERT_EQ (new_q_sval->dyn_cast_region_svalue ()->get_pointee (),
-	     model.get_lvalue (p));
+	     model.get_lvalue (p, &ctxt));
 
   /* Verify that top of stack has been updated.  */
   ASSERT_EQ (model.get_current_frame_id (), parent_frame_rid);
 
   /* Verify locals in parent frame.  */
   /* Verify "a" still has its value.  */
-  svalue *new_a_sval = model.get_svalue (model.get_rvalue (a));
+  svalue *new_a_sval = model.get_svalue (model.get_rvalue (a, &ctxt));
   ASSERT_EQ (new_a_sval->get_kind (), SK_CONSTANT);
   ASSERT_EQ (new_a_sval->dyn_cast_constant_svalue ()->get_constant (),
 	     int_42);
   /* Verify "b" still has its constraint.  */
-  ASSERT_EQ (model.eval_condition (b, LT_EXPR, int_10),
+  ASSERT_EQ (model.eval_condition (b, LT_EXPR, int_10, &ctxt),
 	     tristate (tristate::TS_TRUE));
 }
 
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index 457cd12..7be2792 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -35,6 +35,8 @@ class svalue;
   class unknown_svalue;
 class region;
 class region_model;
+class region_model_context;
+class pending_diagnostic;
 
 class constraint_manager;
 
@@ -596,7 +598,8 @@ public:
 
   void push_frame (region_id frame_rid);
   region_id get_current_frame_id () const;
-  svalue_id pop_frame (region_model *model, purge_stats *stats);
+  svalue_id pop_frame (region_model *model, purge_stats *stats,
+		       region_model_context *ctxt);
 
   void remap_region_ids (const region_id_map &map) FINAL OVERRIDE;
 
@@ -652,7 +655,8 @@ public:
   region_id push_frame (region_model *model, function *fun,
 			vec<svalue_id> *arg_sids);
   region_id get_current_frame_id (const region_model &model) const;
-  svalue_id pop_frame (region_model *model, purge_stats *stats);
+  svalue_id pop_frame (region_model *model, purge_stats *stats,
+		       region_model_context *ctxt);
 
   region_id ensure_stack_region (region_model *model);
   stack_region *get_stack_region (const region_model *model) const;
@@ -727,20 +731,22 @@ class region_model
 
   void validate () const;
 
-  void on_assignment (const gassign *stmt);
-  void on_call (const gcall *stmt);
-  void on_return (const greturn *stmt);
-  void handle_phi (tree lhs, tree rhs, bool is_back_edge);
+  void on_assignment (const gassign *stmt, region_model_context *ctxt);
+  void on_call (const gcall *stmt, region_model_context *ctxt);
+  void on_return (const greturn *stmt, region_model_context *ctxt);
+  void handle_phi (tree lhs, tree rhs, bool is_back_edge,
+		   region_model_context *ctxt);
 
   bool apply_constraints_for_edge (const superedge &edge,
-				   const gimple *last_stmt);
+				   const gimple *last_stmt,
+				   region_model_context *ctxt);
 
   root_region *get_root_region () const;
 
   region_id push_frame (function *fun, vec<svalue_id> *arg_sids);
   region_id get_current_frame_id () const;
   function * get_current_function () const;
-  svalue_id pop_frame (purge_stats *stats);
+  svalue_id pop_frame (purge_stats *stats, region_model_context *ctxt);
 
   svalue_id add_svalue (svalue *sval);
   void replace_svalue (svalue_id sid, svalue *new_sval);
@@ -760,14 +766,14 @@ class region_model
   void assign_to_lvalue (lvalue *lval,
 			 rvalue *rval);
 #endif
-  region_id get_lvalue (tree expr);
-  svalue_id get_rvalue (tree expr);
+  region_id get_lvalue (tree expr, region_model_context *ctxt);
+  svalue_id get_rvalue (tree expr, region_model_context *ctxt);
 
   svalue_id get_svalue_for_region (region_id id);
   svalue_id get_svalue_for_constant (tree cst_expr);
   svalue_id get_svalue_for_fndecl (tree fndecl);
 
-  region_id deref_rvalue (tree ptr);
+  region_id deref_rvalue (tree ptr, region_model_context *ctxt);
 
   void set_value (region_id lhs_rid, svalue_id rhs_sid);
   svalue_id set_to_new_unknown_value (region_id dst_rid, tree type);
@@ -777,8 +783,10 @@ class region_model
 			   svalue_id rhs) const;
   tristate eval_condition (tree lhs,
 			   enum tree_code op,
-			   tree rhs);
-  bool add_constraint (tree lhs, enum tree_code op, tree rhs);
+			   tree rhs,
+			   region_model_context *ctxt);
+  bool add_constraint (tree lhs, enum tree_code op, tree rhs,
+		       region_model_context *ctxt);
 
   tree maybe_get_constant (svalue_id sid);
 
@@ -800,12 +808,16 @@ class region_model
   void get_descendents (region_id rid, region_id_set *out) const;
 
  private:
-  void update_for_call_superedge (const call_superedge &call_edge);
-  void update_for_return_superedge (const return_superedge &return_edge);
+  void update_for_call_superedge (const call_superedge &call_edge,
+				  region_model_context *ctxt);
+  void update_for_return_superedge (const return_superedge &return_edge,
+				    region_model_context *ctxt);
   bool apply_constraints_for_gcond (const superedge &edge,
-				    const gcond *cond_stmt);
+				    const gcond *cond_stmt,
+				    region_model_context *ctxt);
   bool apply_constraints_for_gswitch (const superedge &edge,
-				      const gswitch *switch_stmt);
+				      const gswitch *switch_stmt,
+				      region_model_context *ctxt);
 
  public: // FIXME:
   auto_delete_vec<svalue> m_svalues;
@@ -815,6 +827,16 @@ class region_model
   constraint_manager *m_constraints; // FIXME: embed, rather than dynalloc?
 };
 
+/* Some region_model activity could lead to warnings (e.g. attempts to use an
+   uninitialized value).  This abstract base class encapsulates an interface
+   for the region model to use when emitting such warnings.  */
+
+class region_model_context
+{
+ public:
+  virtual void warn (pending_diagnostic *d) = 0;
+};
+
 namespace inchash
 {
   extern void add (svalue_id sid, hash &hstate);
@@ -823,4 +845,29 @@ namespace inchash
 
 extern void debug (const region_model &rmodel);
 
+#if CHECKING_P
+
+namespace selftest {
+
+/* An implementation of region_model_context for use in selftests, which
+   stores the pending_diagnostic instances passed to it.  */
+
+class test_region_model_context : public region_model_context
+{
+public:
+  void warn (pending_diagnostic *d) FINAL OVERRIDE
+  {
+    m_diagnostics.safe_push (d);
+  }
+
+  unsigned get_num_diagnostics () const { return m_diagnostics.length (); }
+
+private:
+  auto_delete_vec<pending_diagnostic> m_diagnostics;
+};
+
+} /* end of namespace selftest.  */
+
+#endif /* #if CHECKING_P */
+
 #endif /* GCC_ANALYZER_REGION_MODEL_H */
diff --git a/gcc/analyzer/supergraph.cc b/gcc/analyzer/supergraph.cc
index bf20f74..c8ae9c9 100644
--- a/gcc/analyzer/supergraph.cc
+++ b/gcc/analyzer/supergraph.cc
@@ -944,6 +944,9 @@ callgraph_superedge::map_expr_from_caller_to_callee (tree caller_expr) const
 tree
 callgraph_superedge::map_expr_from_callee_to_caller (tree callee_expr) const
 {
+  if (callee_expr == NULL_TREE)
+    return NULL_TREE;
+
   /* If it's a parameter (formal param), get the argument (actual param).  */
   if (TREE_CODE (callee_expr) == PARM_DECL)
     return get_arg_for_parm (callee_expr);
diff --git a/gcc/common.opt b/gcc/common.opt
index 19f54d7..68a0285 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -549,6 +549,18 @@ Wanalyzer-double-free
 Common Init(1) Warning
 FIXME
 
+Wanalyzer-dereference-of-uninitialized-pointer
+Common Init(1) Warning
+FIXME
+
+Wanalyzer-dereference-of-freed-pointer
+Common Init(1) Warning
+FIXME
+
+Wanalyzer-dereference-of-pointer-in-stale-stack-frame
+Common Init(1) Warning
+FIXME
+
 Wanalyzer-exposure-through-output-file
 Common Init(1) Warning
 FIXME
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-1.c b/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
index 54ddcbc..e68d660 100644
--- a/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
@@ -689,25 +689,39 @@ void test_36 (int i)
   __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
 }
 
-// TODO: this currently crashes the analyzer
-#if 0
+/* Read through uninitialized pointer.  */
+
 int test_37 (void)
 {
-  /* Read through uninitialized pointer.  */
   int *ptr;
-  return *ptr;
+  return *ptr; /* { dg-warning "dereference of uninitialized pointer 'ptr'" } */
 }
-#endif
 
-// TODO: this currently crashes the analyzer
-#if 0
+/* Write through uninitialized pointer.  */
+
+void test_37a (int i)
+{
+  int *ptr;
+  *ptr = i; /* { dg-warning "dereference of uninitialized pointer 'ptr'" } */
+}
+
+// TODO: the various other ptr deref poisonings
+
+/* Read through NULL pointer.  */
+
 int test_38 (void)
 {
-  /* Read through NULL pointer.  */
   int *ptr = NULL;
-  return *ptr;
+  return *ptr; /* { dg-warning "use of NULL 'ptr'" } */
+}
+
+/* Write through NULL pointer.  */
+
+int test_38a (int i)
+{
+  int *ptr = NULL;
+  *ptr = i; /* { dg-warning "use of NULL 'ptr'" } */
 }
-#endif
 
 // TODO: this currently crashes the analyzer
 #if 0
@@ -755,29 +769,27 @@ void test_41 (void)
   __analyzer_eval (u.i == 42); /* { dg-warning "TRUE" } */
 }
 
-// FIXME: this currently crashes the analyzer
-#if 0
 void test_42 (void)
 {
   int i;
   float f;
   i = 42;
   f = i;
-  __analyzer_eval (f == 42.0); /* { d-todo-g-warning "TRUE" } */
+  __analyzer_eval (f == 42.0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail)
 }
-#endif
 
-// FIXME: this currently crashes the analyzer
-#if 0
 void test_43 (void)
 {
   int i;
   float f;
   f = 42.0f;
   i = f;
-  __analyzer_eval (i == 42); /* { d-todo-g-warning "TRUE" } */
+  __analyzer_eval (i == 42);  /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail)
 }
-#endif
 
 struct sbits
 {
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-5.c b/gcc/testsuite/gcc.dg/analyzer/data-model-5.c
index e20a9f8..ed02407 100644
--- a/gcc/testsuite/gcc.dg/analyzer/data-model-5.c
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-5.c
@@ -57,7 +57,6 @@ type_obj str_type = {
   { &str_type, 1},
   str_del
 };
-#if 1
 
 base_obj *alloc_obj (type_obj *ob_type, size_t sz)
 {
@@ -91,7 +90,8 @@ base_obj *new_string_obj (const char *str)
 
 void unref (base_obj *obj)
 {
-  if (--obj->ob_refcnt == 0)
+  if (--obj->ob_refcnt == 0) /* { dg-bogus "dereference of uninitialized pointer 'obj'" "" { xfail *-*-* } } */
+    // TODO (xfail): I think it's purging a return value somewhere
     {
       //__analyzer_dump();
       obj->ob_type->tp_dealloc (obj);
@@ -101,9 +101,10 @@ void unref (base_obj *obj)
 void test_1 (const char *str)
 {
   base_obj *obj = new_string_obj (str);
+  //__analyzer_dump();
   unref (obj);
-}
-#endif
+} /* { dg-bogus "leak of 'obj'" "" { xfail *-*-* } } */
+// TODO (xfail): not sure why this is treated as leaking
 
 /* FIXME:
    - ought to complain about NULL deref for when the malloc fails
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-path-1.c b/gcc/testsuite/gcc.dg/analyzer/data-model-path-1.c
new file mode 100644
index 0000000..ec7ef87
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-path-1.c
@@ -0,0 +1,17 @@
+/* { dg-additional-options "-fanalyzer-region-model" } */
+
+#include <stddef.h>
+
+static int *__attribute__((noinline))
+callee (void)
+{
+  return NULL;
+}
+
+void test_1 (void)
+{
+  int *p = callee ();
+  *p = 42; /* { dg-warning "use of NULL 'p'" } */
+}
+
+/* FIXME: maybe better path events describing what happened?  */
-- 
1.8.5.3

