From 36763f8de01fa36162a5b452b5c791ba64d92de6 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Mon, 4 Nov 2019 03:54:34 -0500
Subject: [PATCH 102/119] FIXME: propagate sm-state on casts; split
 region_model::on_call into pre/post

---
 gcc/analyzer/engine.cc        |  29 ++++-
 gcc/analyzer/exploded-graph.h |   3 +
 gcc/analyzer/program-state.cc |  36 +++++-
 gcc/analyzer/program-state.h  |   3 +
 gcc/analyzer/region-model.cc  | 288 ++++++++++++++++++++++++++----------------
 gcc/analyzer/region-model.h   |  33 ++++-
 6 files changed, 273 insertions(+), 119 deletions(-)

diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index c043001..35a382a 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -606,6 +606,25 @@ impl_region_model_context::on_inherited_svalue (svalue_id parent_sid,
     }
 }
 
+/* Implementation of region_model_context::on_cast vfunc
+   for impl_region_model_context.
+   Notify all checkers that DST_SID is a cast of SRC_SID, so that sm-state
+   can be propagated from src to dst.  */
+
+void
+impl_region_model_context::on_cast (svalue_id src_sid,
+				    svalue_id dst_sid)
+{
+  /* FIXME: */
+  if (!m_new_state)
+    return;
+
+  int sm_idx;
+  sm_state_map *smap;
+  FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
+    smap->on_cast (src_sid, dst_sid);
+}
+
 /* Implementation of region_model_context::on_condition vfunc.
    Notify all state machines about the condition, which could lead to
    state transitions.  */
@@ -950,7 +969,7 @@ exploded_node::on_stmt (exploded_graph &eg,
 	  return true;
 	}
       else
-	state->m_region_model->on_call (call, &ctxt);
+	state->m_region_model->on_call_pre (call, &ctxt);
     }
 
   bool any_sm_changes = false;
@@ -981,6 +1000,10 @@ exploded_node::on_stmt (exploded_graph &eg,
       if (*old_smap != *new_smap)
 	any_sm_changes = true;
     }
+
+  if (const gcall *call = dyn_cast <const gcall *> (stmt))
+    state->m_region_model->on_call_post (call, &ctxt);
+
   return any_sm_changes;
 }
 
@@ -1636,7 +1659,7 @@ exploded_graph::add_function_entry (function *fun)
 {
   program_point point = program_point::from_function_entry (m_sg, fun);
   program_state state (m_ext_state);
-  state.m_region_model->push_frame (fun, NULL);
+  state.m_region_model->push_frame (fun, NULL, NULL);
 
   exploded_node *enode = get_or_create_node (point, state);
   /* We should never fail to add such a node.  */
@@ -2599,7 +2622,7 @@ exploded_path::feasible_p (logger *logger) const
 			  == PK_BEFORE_SUPERNODE);
 	      function *fun = eedge->m_dest->get_function ();
 	      gcc_assert (fun);
-	      model.push_frame (fun, NULL);
+	      model.push_frame (fun, NULL, NULL);
 	      if (logger)
 		logger->log ("  pushing frame for %qD", fun->decl);
 	    }
diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h
index a7e058a..8eb22c1 100644
--- a/gcc/analyzer/exploded-graph.h
+++ b/gcc/analyzer/exploded-graph.h
@@ -91,6 +91,9 @@ class impl_region_model_context : public region_model_context, public log_user
   void on_inherited_svalue (svalue_id parent_sid,
 			    svalue_id child_sid) FINAL OVERRIDE;
 
+  void on_cast (svalue_id src_sid,
+		svalue_id dst_sid) FINAL OVERRIDE;
+
   void on_condition (tree lhs, enum tree_code op, tree rhs) FINAL OVERRIDE;
 
   exploded_graph *m_eg;
diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc
index 3e2ed3d..b18f912 100644
--- a/gcc/analyzer/program-state.cc
+++ b/gcc/analyzer/program-state.cc
@@ -196,8 +196,10 @@ sm_state_map::get_origin (svalue_id sid) const
 
   entry_t *slot
     = const_cast <map_t &> (m_map).get (sid);
-  gcc_assert (slot);
-  return slot->m_origin;
+  if (slot)
+    return slot->m_origin;
+  else
+    return svalue_id::null ();
 }
 
 /* Set the state of SID within MODEL to STATE, recording that
@@ -213,6 +215,19 @@ sm_state_map::set_state (region_model *model,
     return;
   equiv_class &ec = model->get_constraints ()->get_equiv_class (sid);
   set_state (ec, state, origin);
+
+  /* Also do it for all svalues that are equal via non-cm, so that
+     e.g. (void *)&r and (foo *)&r transition together.  */
+  for (unsigned i = 0; i < model->get_num_svalues (); i++)
+    {
+      svalue_id other_sid = svalue_id::from_int (i);
+      if (other_sid == sid)
+	continue;
+
+      tristate eq = model->eval_condition_without_cm (sid, EQ_EXPR, other_sid);
+      if (eq.is_true ())
+	impl_set_state (other_sid, state, origin);
+    }
 }
 
 /* Set the state of EC to STATE, recording that the state came from
@@ -403,6 +418,21 @@ sm_state_map::on_inherited_svalue (svalue_id parent_sid,
   impl_set_state (child_sid, state, parent_sid);
 }
 
+/* Set the state of DST_SID to that of SRC_SID.  */
+
+void
+sm_state_map::on_cast (svalue_id src_sid,
+		       svalue_id dst_sid)
+{
+  state_machine::state_t state = get_state (src_sid);
+  // FIXME: should we use src_sid's state's origin?
+#if 1
+  impl_set_state (dst_sid, state, get_origin (src_sid));
+#else
+  impl_set_state (dst_sid, state, src_sid);
+#endif
+}
+
 /* Assert that this object is sane.  */
 
 void
@@ -1043,7 +1073,7 @@ test_program_state_merging ()
   svalue_id ptr_sid
       = model0->get_or_create_ptr_svalue (ptr_type_node, new_rid);
   model0->set_value (model0->get_lvalue (p, &ctxt),
-		     ptr_sid);
+		     ptr_sid, &ctxt);
   sm_state_map *smap = s0.m_checker_states[0];
   const state_machine::state_t TEST_STATE = 3;
   smap->impl_set_state (ptr_sid, TEST_STATE, svalue_id::null ());
diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h
index 3a0dff6..14cfe56 100644
--- a/gcc/analyzer/program-state.h
+++ b/gcc/analyzer/program-state.h
@@ -184,6 +184,9 @@ public:
   void on_inherited_svalue (svalue_id parent_sid,
 			    svalue_id child_sid);
 
+  void on_cast (svalue_id src_sid,
+		svalue_id dst_sid);
+
   void validate (const state_machine &sm, int num_svalues) const;
 
   iterator_t begin () const { return m_map.begin (); }
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index affc482..716700e 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -1081,14 +1081,15 @@ region::get_parent_region (const region_model &model) const
    for some kinds of casts).  */
 
 void
-region::set_value (region_model &model, region_id this_rid, svalue_id rhs_sid)
+region::set_value (region_model &model, region_id this_rid, svalue_id rhs_sid,
+		   region_model_context *ctxt)
 {
   /* Handle some kinds of casting.  */
   if (m_type)
     {
       svalue *sval = model.get_svalue (rhs_sid);
       if (sval->get_type ())
-	rhs_sid = model.maybe_cast (m_type, rhs_sid);
+	rhs_sid = model.maybe_cast (m_type, rhs_sid, ctxt);
 
       sval = model.get_svalue (rhs_sid);
       if (sval->get_type ())
@@ -2081,7 +2082,8 @@ map_region::can_merge_p (const map_region *map_region_a,
 	  if (!child_merged_sid.null_p ())
 	    child_merged_region->set_value (*merger->m_merged_model,
 					    child_merged_rid,
-					    child_merged_sid);
+					    child_merged_sid,
+					    NULL);
 
 #if 0
 	  /* Recurse.  */
@@ -3075,7 +3077,8 @@ root_region::dump_child_label (const region_model &model ATTRIBUTE_UNUSED,
 
 region_id
 root_region::push_frame (region_model *model, function *fun,
-			 vec<svalue_id> *arg_sids)
+			 vec<svalue_id> *arg_sids,
+			 region_model_context *ctxt)
 {
   gcc_assert (fun);
   /* arg_sids can be NULL.  */
@@ -3107,7 +3110,7 @@ root_region::push_frame (region_model *model, function *fun,
 	  region_id parm_rid
 	    = region->get_or_create (model, frame_rid, iter_parm,
 				     TREE_TYPE (iter_parm));
-	  model->set_value (parm_rid, arg_sid);
+	  model->set_value (parm_rid, arg_sid, ctxt);
 
 	  /* Also do it for default SSA name (sharing the same unknown
 	     value).  */
@@ -3117,7 +3120,7 @@ root_region::push_frame (region_model *model, function *fun,
 	      region_id defssa_rid
 		= region->get_or_create (model, frame_rid, parm_default_ssa,
 					 TREE_TYPE (iter_parm));
-	      model->set_value (defssa_rid, arg_sid);
+	      model->set_value (defssa_rid, arg_sid, ctxt);
 	    }
 	}
     }
@@ -3135,7 +3138,8 @@ root_region::push_frame (region_model *model, function *fun,
 	    = region->get_or_create (model, frame_rid, iter_parm,
 				     TREE_TYPE (iter_parm));
 	  svalue_id parm_sid
-	    = model->set_to_new_unknown_value (parm_rid, TREE_TYPE (iter_parm));
+	    = model->set_to_new_unknown_value (parm_rid, TREE_TYPE (iter_parm),
+					       ctxt);
 
 	  /* Also do it for default SSA name (sharing the same unknown
 	     value).  */
@@ -3146,7 +3150,7 @@ root_region::push_frame (region_model *model, function *fun,
 		= region->get_or_create (model, frame_rid, parm_default_ssa,
 					 TREE_TYPE (iter_parm));
 	      model->get_region (defssa_rid)->set_value (*model, defssa_rid,
-							 parm_sid);
+							 parm_sid, ctxt);
 	    }
 	}
     }
@@ -4223,7 +4227,7 @@ region_model::on_assignment (const gassign *assign, region_model_context *ctxt)
 	if (0)
 	  sorry_at (assign->location, "unhandled assignment op: %qs",
 		    get_tree_code_name (op));
-	set_to_new_unknown_value (lhs_rid, TREE_TYPE (lhs));
+	set_to_new_unknown_value (lhs_rid, TREE_TYPE (lhs), ctxt);
       }
       break;
 
@@ -4257,7 +4261,7 @@ region_model::on_assignment (const gassign *assign, region_model_context *ctxt)
 					     ctxt);
 	svalue_id element_ptr_sid
 	  = get_or_create_ptr_svalue (TREE_TYPE (ptr), element_rid);
-	set_value (lhs_rid, element_ptr_sid);
+	set_value (lhs_rid, element_ptr_sid, ctxt);
       }
       break;
 
@@ -4267,7 +4271,7 @@ region_model::on_assignment (const gassign *assign, region_model_context *ctxt)
 
 	/* TODO.  */
 
-	set_to_new_unknown_value (lhs_rid, TREE_TYPE (lhs));
+	set_to_new_unknown_value (lhs_rid, TREE_TYPE (lhs), ctxt);
       }
       break;
 
@@ -4275,7 +4279,7 @@ region_model::on_assignment (const gassign *assign, region_model_context *ctxt)
       {
 	/* LHS = &RHS;  */
 	svalue_id ptr_sid = get_rvalue (rhs1, ctxt);
-	set_value (lhs_rid, ptr_sid);
+	set_value (lhs_rid, ptr_sid, ctxt);
       }
       break;
 
@@ -4284,7 +4288,7 @@ region_model::on_assignment (const gassign *assign, region_model_context *ctxt)
 	region_id rhs_rid = get_lvalue (rhs1, ctxt);
 	svalue_id rhs_sid
 	  = get_region (rhs_rid)->get_value (*this, true, ctxt);
-	set_value (lhs_rid, rhs_sid);
+	set_value (lhs_rid, rhs_sid, ctxt);
       }
       break;
 
@@ -4294,7 +4298,7 @@ region_model::on_assignment (const gassign *assign, region_model_context *ctxt)
       {
 	/* LHS = RHS;  */
 	svalue_id cst_sid = get_rvalue (rhs1, ctxt);
-	set_value (lhs_rid, cst_sid);
+	set_value (lhs_rid, cst_sid, ctxt);
       }
       break;
 
@@ -4309,7 +4313,7 @@ region_model::on_assignment (const gassign *assign, region_model_context *ctxt)
       {
 	/* LHS = VAR;  */
 	svalue_id var_sid = get_rvalue (rhs1, ctxt);
-	set_value (lhs_rid, var_sid);
+	set_value (lhs_rid, var_sid, ctxt);
       }
       break;
 
@@ -4332,9 +4336,10 @@ region_model::on_assignment (const gassign *assign, region_model_context *ctxt)
 		     get_rvalue (t.is_true ()
 				 ? boolean_true_node
 				 : boolean_false_node,
-				 ctxt));
+				 ctxt),
+		     ctxt);
 	else
-	  set_to_new_unknown_value (lhs_rid, TREE_TYPE (lhs));
+	  set_to_new_unknown_value (lhs_rid, TREE_TYPE (lhs), ctxt);
       }
       break;
 
@@ -4345,7 +4350,7 @@ region_model::on_assignment (const gassign *assign, region_model_context *ctxt)
 
 	// TODO: constant?
 
-	set_to_new_unknown_value (lhs_rid, TREE_TYPE (lhs));
+	set_to_new_unknown_value (lhs_rid, TREE_TYPE (lhs), ctxt);
       }
       break;
 
@@ -4377,11 +4382,11 @@ region_model::on_assignment (const gassign *assign, region_model_context *ctxt)
 		{
 		  svalue_id result_sid
 		    = get_or_create_constant_svalue (result);
-		  set_value (lhs_rid, result_sid);
+		  set_value (lhs_rid, result_sid, ctxt);
 		  return;
 		}
 	    }
-	set_to_new_unknown_value (lhs_rid, TREE_TYPE (lhs));
+	set_to_new_unknown_value (lhs_rid, TREE_TYPE (lhs), ctxt);
       }
       break;
 
@@ -4391,17 +4396,21 @@ region_model::on_assignment (const gassign *assign, region_model_context *ctxt)
 	region_id child_rid = get_lvalue (rhs1, ctxt);
 	svalue_id child_sid
 	  = get_region (child_rid)->get_value (*this, true, ctxt);
-	set_value (lhs_rid, child_sid);
+	set_value (lhs_rid, child_sid, ctxt);
       }
       break;
     }
 }
 
 /* Update this model for the CALL stmt, using CTXT to report any
-   diagnostics.  */
+   diagnostics - the first half.
+
+   Updates to the region_model that should be made *before* sm-states
+   are updated are done here; other updates to the region_model are done
+   in region_model::on_call_post.  */
 
 void
-region_model::on_call (const gcall *call, region_model_context *ctxt)
+region_model::on_call_pre (const gcall *call, region_model_context *ctxt)
 {
   region_id lhs_rid;
   tree lhs_type = NULL_TREE;
@@ -4430,33 +4439,7 @@ region_model::on_call (const gcall *call, region_model_context *ctxt)
 	{
 	  svalue_id ptr_sid
 	    = get_or_create_ptr_svalue (lhs_type, new_rid);
-	  set_value (lhs_rid, ptr_sid);
-	}
-      return;
-    }
-  else if (is_named_call_p (call, "free", 1))
-    {
-      tree ptr = gimple_call_arg (call, 0);
-      svalue_id ptr_sid = get_rvalue (ptr, ctxt);
-      svalue *ptr_sval = get_svalue (ptr_sid);
-      if (region_svalue *ptr_to_region_sval
-	  = ptr_sval->dyn_cast_region_svalue ())
-	{
-	  /* If the ptr points to an underlying heap region, delete it,
-	     poisoning pointers.  */
-	  region_id pointee_rid = ptr_to_region_sval->get_pointee ();
-	  region_id heap_rid = get_root_region ()->ensure_heap_region (this);
-	  if (!pointee_rid.null_p ()
-	      && get_region (pointee_rid)->get_parent () == heap_rid)
-	    {
-	      purge_stats stats;
-	      delete_region_and_descendents (pointee_rid,
-					     POISON_KIND_FREED,
-					     &stats, ctxt->get_logger ());
-	      purge_unused_svalues (&stats, ctxt);
-	      validate ();
-	      // TODO: do anything with stats?
-	    }
+	  set_value (lhs_rid, ptr_sid, ctxt);
 	}
       return;
     }
@@ -4468,7 +4451,7 @@ region_model::on_call (const gcall *call, region_model_context *ctxt)
 	{
 	  svalue_id ptr_sid
 	    = get_or_create_ptr_svalue (lhs_type, new_rid);
-	  set_value (lhs_rid, ptr_sid);
+	  set_value (lhs_rid, ptr_sid, ctxt);
 	}
       return;
     }
@@ -4487,7 +4470,7 @@ region_model::on_call (const gcall *call, region_model_context *ctxt)
 	      tree t_cst = build_int_cst (lhs_type, strlen_cst);
 	      svalue_id result_sid
 		= get_or_create_constant_svalue (t_cst);
-	      set_value (lhs_rid, result_sid);
+	      set_value (lhs_rid, result_sid, ctxt);
 	      return;
 	    }
 	}
@@ -4517,13 +4500,65 @@ region_model::on_call (const gcall *call, region_model_context *ctxt)
 
   /* Unknown return value.  */
   if (!lhs_rid.null_p ())
-    set_to_new_unknown_value (lhs_rid, lhs_type);
+    set_to_new_unknown_value (lhs_rid, lhs_type, ctxt);
 
   /* TODO: also, any pointer arguments might have been written through,
      or the things they point to (implying a graph traversal, which
      presumably we need to do before overwriting the old value).  */
 }
 
+/* Update this model for the CALL stmt, using CTXT to report any
+   diagnostics - the second half.
+
+   Updates to the region_model that should be made *after* sm-states
+   are updated are done here; other updates to the region_model are done
+   in region_model::on_call_pre.  */
+
+void
+region_model::on_call_post (const gcall *call, region_model_context *ctxt)
+{
+  /* Update for "free" here, after sm-handling.
+
+     If the ptr points to an underlying heap region, delete the region,
+     poisoning pointers to it and regions within it.
+
+     We delay this until after sm-state has been updated so that the
+     sm-handling can transition all of the various casts of the pointer
+     to a "freed" state *before* we delete the related region here.
+
+     This has to be done here so that the sm-handling can use the fact
+     that they point to the same region to establish that they are equal
+     (in region_model::eval_condition_without_cm), and thus transition
+     all pointers to the region to the "freed" state together, regardless
+     of casts.  */
+  if (is_named_call_p (call, "free", 1))
+    {
+      tree ptr = gimple_call_arg (call, 0);
+      svalue_id ptr_sid = get_rvalue (ptr, ctxt);
+      svalue *ptr_sval = get_svalue (ptr_sid);
+      if (region_svalue *ptr_to_region_sval
+	  = ptr_sval->dyn_cast_region_svalue ())
+	{
+	  /* If the ptr points to an underlying heap region, delete it,
+	     poisoning pointers.  */
+	  region_id pointee_rid = ptr_to_region_sval->get_pointee ();
+	  region_id heap_rid = get_root_region ()->ensure_heap_region (this);
+	  if (!pointee_rid.null_p ()
+	      && get_region (pointee_rid)->get_parent () == heap_rid)
+	    {
+	      purge_stats stats;
+	      delete_region_and_descendents (pointee_rid,
+					     POISON_KIND_FREED,
+					     &stats, ctxt->get_logger ());
+	      purge_unused_svalues (&stats, ctxt);
+	      validate ();
+	      // TODO: do anything with stats?
+	    }
+	}
+      return;
+    }
+}
+
 /* Update this model for the RETURN_STMT, using CTXT to report any
    diagnostics.  */
 
@@ -4535,7 +4570,7 @@ region_model::on_return (const greturn *return_stmt, region_model_context *ctxt)
   tree rhs = gimple_return_retval (return_stmt);
 
   if (lhs && rhs)
-    set_value (get_lvalue (lhs, ctxt), get_rvalue (rhs, ctxt));
+    set_value (get_lvalue (lhs, ctxt), get_rvalue (rhs, ctxt), ctxt);
 }
 
 /* Update this model for a call and return of "setjmp" at CALL within ENODE,
@@ -4556,7 +4591,7 @@ region_model::on_setjmp (const gcall *call, const exploded_node *enode,
     {
       svalue *sval = new setjmp_svalue (enode, buf->get_type ());
       svalue_id new_sid = add_svalue (sval);
-      set_value (buf_rid, new_sid);
+      set_value (buf_rid, new_sid, ctxt);
     }
 
   /* Direct calls to setjmp return 0.  */
@@ -4565,7 +4600,7 @@ region_model::on_setjmp (const gcall *call, const exploded_node *enode,
       tree zero = build_int_cst (TREE_TYPE (lhs), 0);
       svalue_id new_sid = get_or_create_constant_svalue (zero);
       region_id lhs_rid = get_lvalue (lhs, ctxt);
-      set_value (lhs_rid, new_sid);
+      set_value (lhs_rid, new_sid, ctxt);
     }
 }
 
@@ -4615,7 +4650,7 @@ region_model::on_longjmp (const gcall *longjmp_call, const gcall *setjmp_call,
 	}
 
       region_id lhs_rid = get_lvalue (lhs, ctxt);
-      set_value (lhs_rid, fake_retval_sid);
+      set_value (lhs_rid, fake_retval_sid, ctxt);
     }
 
   /* Now that we've assigned the fake_retval, we can purge the unused
@@ -4650,10 +4685,10 @@ 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, ctxt), TREE_TYPE (lhs));
+      set_to_new_unknown_value (get_lvalue (lhs, ctxt), TREE_TYPE (lhs), ctxt);
     }
   else
-    set_value (get_lvalue (lhs, ctxt), rhs_sid);
+    set_value (get_lvalue (lhs, ctxt), rhs_sid, ctxt);
 }
 
 /* Get the id of the region for PV within this region_model.  */
@@ -4752,7 +4787,7 @@ region_model::get_lvalue_1 (path_var pv, region_model_context *ctxt)
 	array_region *cst_region = new array_region (m_root_rid, cst_type);
 	region_id cst_rid = add_region (cst_region);
 	svalue_id cst_sid = get_or_create_constant_svalue (expr);
-	cst_region->set_value (*this, cst_rid, cst_sid);
+	cst_region->set_value (*this, cst_rid, cst_sid, ctxt);
 	return cst_rid;
       }
       break;
@@ -4953,7 +4988,7 @@ region_model::get_region_for_label (tree label)
 }
 
 /* If the type of SID's underlying value is DST_TYPE, return SID.
-   Otherwise, attempt to create an svalue representing an access
+   Otherwise, attempt to create (or reuse) an svalue representing an access
    of SID as a DST_TYPE and return that value's svalue_id.
 
    FIXME:
@@ -4961,7 +4996,7 @@ region_model::get_region_for_label (tree label)
    to base *.  */
 
 svalue_id
-region_model::maybe_cast (tree dst_type, svalue_id sid)
+region_model::maybe_cast_1 (tree dst_type, svalue_id sid)
 {
   svalue *sval = get_svalue (sid);
   tree src_type = sval->get_type ();
@@ -5049,6 +5084,31 @@ region_model::maybe_cast (tree dst_type, svalue_id sid)
   return add_svalue (unknown_sval);
 }
 
+/* If the type of SID's underlying value is DST_TYPE, return SID.
+   Otherwise, attempt to create (or reuse) an svalue representing an access
+   of SID as a DST_TYPE and return that value's svalue_id.
+
+   If the result != SID, then call CTXT's on_cast vfunc (if CTXT is non-NULL),
+   so that sm-state can be propagated from SID to the result.
+
+   FIXME:
+   This is a crude way of handling some casts e.g. from subclass *
+   to base *.  */
+
+svalue_id
+region_model::maybe_cast (tree dst_type, svalue_id sid,
+			  region_model_context *ctxt)
+{
+  svalue_id result = maybe_cast_1 (dst_type, sid);
+  if (result != sid)
+    if (ctxt)
+      {
+	/* Notify ctxt about a cast, so any sm-state can be copied.  */
+	ctxt->on_cast (sid, result);
+      }
+  return result;
+}
+
 /* Ensure that the region for OBJ_RID has a child region for FIELD;
    return the child region's region_id.  */
 
@@ -5181,11 +5241,12 @@ region_model::deref_rvalue (tree ptr, region_model_context *ctxt)
    by RHS_SID.  */
 
 void
-region_model::set_value (region_id lhs_rid, svalue_id rhs_sid)
+region_model::set_value (region_id lhs_rid, svalue_id rhs_sid,
+			 region_model_context *ctxt)
 {
   gcc_assert (!lhs_rid.null_p ());
   gcc_assert (!rhs_sid.null_p ());
-  get_region (lhs_rid)->set_value (*this, lhs_rid, rhs_sid);
+  get_region (lhs_rid)->set_value (*this, lhs_rid, rhs_sid, ctxt);
 }
 
 /* Determine what is known about the condition "LHS_SID OP RHS_SID" within
@@ -5557,11 +5618,12 @@ region_model::get_path_vars_for_svalue (svalue_id sid, vec<path_var> *out) const
 /* Set DST_RID value to be a new unknown value of type TYPE.  */
 
 svalue_id
-region_model::set_to_new_unknown_value (region_id dst_rid, tree type)
+region_model::set_to_new_unknown_value (region_id dst_rid, tree type,
+					region_model_context *ctxt)
 {
   gcc_assert (!dst_rid.null_p ());
   svalue_id new_sid = add_svalue (new unknown_svalue (type));
-  set_value (dst_rid, new_sid);
+  set_value (dst_rid, new_sid, ctxt);
 
   // TODO: presumably purge all child regions too (but do this in set_value?)
 
@@ -5687,7 +5749,7 @@ region_model::update_for_call_superedge (const call_superedge &call_edge,
       arg_sids.quick_push (get_rvalue (arg, ctxt));
     }
 
-  push_frame (call_edge.get_callee_function (), &arg_sids);
+  push_frame (call_edge.get_callee_function (), &arg_sids, ctxt);
 }
 
 /* Pop the top-most frame_region from the stack, and store the svalue
@@ -5709,7 +5771,7 @@ region_model::update_for_return_superedge (const return_superedge &return_edge,
   const gcall *call_stmt = return_edge.get_call_stmt ();
   tree lhs = gimple_call_lhs (call_stmt);
   if (lhs)
-    set_value (get_lvalue (lhs, ctxt), result_sid);
+    set_value (get_lvalue (lhs, ctxt), result_sid, ctxt);
   else if (!result_sid.null_p ())
     {
       /* This could be a leak; try purging again, but this time,
@@ -5734,7 +5796,7 @@ region_model::update_for_call_summary (const callgraph_superedge &cg_sedge,
   const gcall *call_stmt = cg_sedge.get_call_stmt ();
   tree lhs = gimple_call_lhs (call_stmt);
   if (lhs)
-    set_to_new_unknown_value (get_lvalue (lhs, ctxt), TREE_TYPE (lhs));
+    set_to_new_unknown_value (get_lvalue (lhs, ctxt), TREE_TYPE (lhs), ctxt);
 
   // TODO: actually implement some kind of summary here
 }
@@ -5853,9 +5915,10 @@ region_model::get_stack_region_id () const
    Return the region_id of the new frame_region.  */
 
 region_id
-region_model::push_frame (function *fun, vec<svalue_id> *arg_sids)
+region_model::push_frame (function *fun, vec<svalue_id> *arg_sids,
+			  region_model_context *ctxt)
 {
-  return get_root_region ()->push_frame (this, fun, arg_sids);
+  return get_root_region ()->push_frame (this, fun, arg_sids, ctxt);
 }
 
 /* Get the region_id of the top-most frame in this region_model's stack,
@@ -7322,13 +7385,17 @@ test_purge_unused_svalues ()
 
   test_region_model_context ctxt;
   region_model model;
-  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.set_to_new_unknown_value (model.get_lvalue (x, &ctxt), TREE_TYPE (x),
+				  &ctxt);
+  model.set_to_new_unknown_value (model.get_lvalue (x, &ctxt), TREE_TYPE (x),
+				  &ctxt);
+  model.set_to_new_unknown_value (model.get_lvalue (x, &ctxt), TREE_TYPE (x),
+				  &ctxt);
   model.add_constraint (x, NE_EXPR, int_42, &ctxt);
 
   model.set_value (model.get_lvalue (x, &ctxt),
-		   model.get_rvalue (int_42, &ctxt));
+		   model.get_rvalue (int_42, &ctxt),
+		   &ctxt);
   model.add_constraint (y, GT_EXPR, int_0, &ctxt);
 
   /* The redundant unknown values should have been purged.  */
@@ -7367,7 +7434,8 @@ test_assignment ()
   ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, int_0);
   ASSERT_CONDITION_UNKNOWN (model, y, EQ_EXPR, int_0);
   model.set_value (model.get_lvalue (y, NULL),
-		   model.get_rvalue (int_0, NULL));
+		   model.get_rvalue (int_0, NULL),
+		   NULL);
   ASSERT_CONDITION_TRUE (model, y, EQ_EXPR, int_0);
   ASSERT_CONDITION_TRUE (model, y, EQ_EXPR, x);
 
@@ -7421,24 +7489,24 @@ test_stack_frames ()
 
   /* Push stack frame for "parent_fn".  */
   region_id parent_frame_rid
-    = model.push_frame (DECL_STRUCT_FUNCTION (parent_fndecl), NULL);
+    = model.push_frame (DECL_STRUCT_FUNCTION (parent_fndecl), NULL, &ctxt);
   ASSERT_EQ (model.get_current_frame_id (), parent_frame_rid);
   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_value (a_in_parent_rid, model.get_rvalue (int_42, &ctxt), &ctxt);
   model.set_to_new_unknown_value (model.get_lvalue (b, &ctxt),
-				  integer_type_node);
+				  integer_type_node, &ctxt);
   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);
+    = model.push_frame (DECL_STRUCT_FUNCTION (child_fndecl), NULL, &ctxt);
   ASSERT_EQ (model.get_current_frame_id (), child_frame_rid);
   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_value (x_in_child_rid, model.get_rvalue (int_0, &ctxt), &ctxt);
   model.set_to_new_unknown_value (model.get_lvalue (y, &ctxt),
-				  integer_type_node);
+				  integer_type_node, &ctxt);
   model.add_constraint (y, NE_EXPR, int_5, &ctxt);
   ASSERT_EQ (model.eval_condition (y, NE_EXPR, int_5, &ctxt),
 	     tristate (tristate::TS_TRUE));
@@ -7447,13 +7515,15 @@ test_stack_frames ()
   region_id p_in_globals_rid = model.get_lvalue (p, &ctxt);
   model.set_value (p_in_globals_rid,
 		   model.get_or_create_ptr_svalue (ptr_type_node,
-						   x_in_child_rid));
+						   x_in_child_rid),
+		   &ctxt);
 
   /* Point another global pointer at p: q = &p.  */
   region_id q_in_globals_rid = model.get_lvalue (q, &ctxt);
   model.set_value (q_in_globals_rid,
 		   model.get_or_create_ptr_svalue (ptr_type_node,
-						   p_in_globals_rid));
+						   p_in_globals_rid),
+		   &ctxt);
 
   /* Test get_descendents.  */
   region_id_set descendents (&model);
@@ -7543,14 +7613,14 @@ test_get_representative_path_var ()
   for (int depth = 0; depth < 5; depth++)
     {
       region_id frame_rid
-	= model.push_frame (DECL_STRUCT_FUNCTION (fndecl), NULL);
+	= model.push_frame (DECL_STRUCT_FUNCTION (fndecl), NULL, NULL);
       region_id rid_n = model.get_lvalue (path_var (n, depth), NULL);
       parm_rids.safe_push (rid_n);
 
       ASSERT_EQ (model.get_region (rid_n)->get_parent (), frame_rid);
 
       svalue_id sid_n
-	= model.set_to_new_unknown_value (rid_n, integer_type_node);
+	= model.set_to_new_unknown_value (rid_n, integer_type_node, NULL);
       parm_sids.safe_push (sid_n);
       // FIXME: do something with these?
     }
@@ -7608,15 +7678,19 @@ test_canonicalization_2 ()
 
   region_model model0;
   model0.set_value (model0.get_lvalue (x, NULL),
-		    model0.get_rvalue (int_42, NULL));
+		    model0.get_rvalue (int_42, NULL),
+		    NULL);
   model0.set_value (model0.get_lvalue (y, NULL),
-		    model0.get_rvalue (int_113, NULL));
+		    model0.get_rvalue (int_113, NULL),
+		    NULL);
 
   region_model model1;
   model1.set_value (model1.get_lvalue (y, NULL),
-		    model1.get_rvalue (int_113, NULL));
+		    model1.get_rvalue (int_113, NULL),
+		    NULL);
   model1.set_value (model1.get_lvalue (x, NULL),
-		    model1.get_rvalue (int_42, NULL));
+		    model1.get_rvalue (int_42, NULL),
+		    NULL);
 
   model0.canonicalize (NULL);
   model1.canonicalize (NULL);
@@ -7669,10 +7743,12 @@ assert_region_models_merge (tree expr, tree val_a, tree val_b,
   region_model model1;
   if (val_a)
     model0.set_value (model0.get_lvalue (expr, &ctxt),
-		      model0.get_rvalue (val_a, &ctxt));
+		      model0.get_rvalue (val_a, &ctxt),
+		      &ctxt);
   if (val_b)
     model1.set_value (model1.get_lvalue (expr, &ctxt),
-		      model1.get_rvalue (val_b, &ctxt));
+		      model1.get_rvalue (val_b, &ctxt),
+		      &ctxt);
 
   /* They should be mergeable.  */
   ASSERT_TRUE (model0.can_merge_with_p (model1, out_merged_model));
@@ -7736,13 +7812,13 @@ test_state_merging ()
     test_region_model_context ctxt;
     region_model model0;
     region_model model1;
-    model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL);
-    model1.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL);
+    model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, &ctxt);
+    model1.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, &ctxt);
     svalue_id sid_a
       = model0.set_to_new_unknown_value (model0.get_lvalue (a, &ctxt),
-					 integer_type_node);
+					 integer_type_node, &ctxt);
     model1.set_to_new_unknown_value (model1.get_lvalue (a, &ctxt),
-				     integer_type_node);
+				     integer_type_node, &ctxt);
     ASSERT_EQ (model0, model1);
 
     /* Check that get_value_by_name works for locals.  */
@@ -7764,9 +7840,9 @@ test_state_merging ()
     region_model model1;
     svalue_id sid_x
       = model0.set_to_new_unknown_value (model0.get_lvalue (x, &ctxt),
-					 integer_type_node);
+					 integer_type_node, &ctxt);
     model1.set_to_new_unknown_value (model1.get_lvalue (x, &ctxt),
-				     integer_type_node);
+				     integer_type_node, &ctxt);
     ASSERT_EQ (model0, model1);
 
     /* Check that get_value_by_name works for globals.  */
@@ -7852,11 +7928,11 @@ test_state_merging ()
   /* Pointers: non-NULL and non-NULL: ptr to a local.  */
   {
     region_model model0;
-    model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL);
+    model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL);
     model0.set_to_new_unknown_value (model0.get_lvalue (a, NULL),
-				     integer_type_node);
+				     integer_type_node, NULL);
     model0.set_value (model0.get_lvalue (p, NULL),
-		      model0.get_rvalue (addr_of_a, NULL));
+		      model0.get_rvalue (addr_of_a, NULL), NULL);
 
     region_model model1 (model0);
     ASSERT_EQ (model0, model1);
@@ -7902,7 +7978,7 @@ test_state_merging ()
     svalue_id ptr_sid
       = model0.get_or_create_ptr_svalue (ptr_type_node, new_rid);
     model0.set_value (model0.get_lvalue (p, &ctxt),
-		      ptr_sid);
+		      ptr_sid, &ctxt);
     model0.canonicalize (&ctxt);
 
     region_model model1 (model0);
@@ -7926,8 +8002,8 @@ test_state_merging ()
     region_model model0;
     svalue_id sid
       = model0.set_to_new_unknown_value (model0.get_lvalue (x, &ctxt),
-					 integer_type_node);
-    model0.set_value (model0.get_lvalue (y, &ctxt), sid);
+					 integer_type_node, &ctxt);
+    model0.set_value (model0.get_lvalue (y, &ctxt), sid, &ctxt);
     region_model model1 (model0);
 
     /* They should be mergeable, and the result should be the same.  */
@@ -7989,7 +8065,7 @@ test_constraint_merging ()
   /* model0: 0 <= (x == y) < n.  */
   region_model model0;
   model0.set_to_new_unknown_value (model0.get_lvalue (x, &ctxt),
-				   integer_type_node);
+				   integer_type_node, &ctxt);
   model0.add_constraint (x, EQ_EXPR, y, &ctxt);
   model0.add_constraint (x, GE_EXPR, int_0, NULL);
   model0.add_constraint (x, LT_EXPR, n, NULL);
@@ -7997,7 +8073,7 @@ test_constraint_merging ()
   /* model1: z != 5 && (0 <= x < n).  */
   region_model model1;
   model1.set_to_new_unknown_value (model1.get_lvalue (x, &ctxt),
-				   integer_type_node);
+				   integer_type_node, &ctxt);
   model1.add_constraint (z, NE_EXPR, int_5, NULL);
   model1.add_constraint (x, GE_EXPR, int_0, NULL);
   model1.add_constraint (x, LT_EXPR, n, NULL);
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index 138046e..3b12391 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -871,7 +871,8 @@ public:
   region_id get_parent () const { return m_parent_rid; }
   region *get_parent_region (const region_model &model) const;
 
-  void set_value (region_model &model, region_id this_rid, svalue_id rhs_sid);
+  void set_value (region_model &model, region_id this_rid, svalue_id rhs_sid,
+		  region_model_context *ctxt);
   svalue_id get_value (region_model &model, bool non_null,
 		       region_model_context *ctxt);
   svalue_id get_value_direct () const { return m_sval_id; }
@@ -1525,7 +1526,8 @@ public:
     FINAL OVERRIDE;
 
   region_id push_frame (region_model *model, function *fun,
-			vec<svalue_id> *arg_sids);
+			vec<svalue_id> *arg_sids,
+			region_model_context *ctxt);
   region_id get_current_frame_id (const region_model &model) const;
   svalue_id pop_frame (region_model *model, bool purge, purge_stats *stats,
 		       region_model_context *ctxt);
@@ -1660,7 +1662,8 @@ class region_model
 
   void check_for_poison (tree expr, region_model_context *ctxt);
   void on_assignment (const gassign *stmt, region_model_context *ctxt);
-  void on_call (const gcall *stmt, region_model_context *ctxt);
+  void on_call_pre (const gcall *stmt, region_model_context *ctxt);
+  void on_call_post (const gcall *stmt, region_model_context *ctxt);
   void on_return (const greturn *stmt, region_model_context *ctxt);
   void on_setjmp (const gcall *stmt, const exploded_node *enode,
 		  region_model_context *ctxt);
@@ -1682,7 +1685,8 @@ class region_model
   root_region *get_root_region () const;
 
   region_id get_stack_region_id () const;
-  region_id push_frame (function *fun, vec<svalue_id> *arg_sids);
+  region_id push_frame (function *fun, vec<svalue_id> *arg_sids,
+			region_model_context *ctxt);
   region_id get_current_frame_id () const;
   function * get_current_function () const;
   svalue_id pop_frame (bool purge, purge_stats *stats,
@@ -1723,15 +1727,18 @@ class region_model
   region_id get_region_for_fndecl (tree fndecl);
   region_id get_region_for_label (tree label);
 
-  svalue_id maybe_cast (tree type, svalue_id sid);
+  svalue_id maybe_cast (tree type, svalue_id sid, region_model_context *ctxt);
+  svalue_id maybe_cast_1 (tree type, svalue_id sid);
 
   region_id get_field_region (region_id rid, tree field);
 
   region_id deref_rvalue (svalue_id ptr_sid, region_model_context *ctxt);
   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);
+  void set_value (region_id lhs_rid, svalue_id rhs_sid,
+		  region_model_context *ctxt);
+  svalue_id set_to_new_unknown_value (region_id dst_rid, tree type,
+				      region_model_context *ctxt);
 
   tristate eval_condition (svalue_id lhs,
 			   enum tree_code op,
@@ -1886,6 +1893,13 @@ class region_model_context
   virtual void on_inherited_svalue (svalue_id parent_sid,
 				    svalue_id child_sid) = 0;
 
+  /* Hook for clients to be notified when DST_SID is created
+     (or reused) as a cast from SRC_SID.
+     This exists so that state machines can propagate the state
+     from SRC_SID to DST_SID.  */
+  virtual void on_cast (svalue_id src_sid,
+			svalue_id dst_sid) = 0;
+
   /* Hook for clients to be notified when the condition
      "LHS OP RHS" is added to the region model.
      This exists so that state machines can detect tests on edges,
@@ -2046,6 +2060,11 @@ public:
   {
   }
 
+  void on_cast (svalue_id src_sid ATTRIBUTE_UNUSED,
+		svalue_id dst_sid ATTRIBUTE_UNUSED) FINAL OVERRIDE
+  {
+  }
+
   unsigned get_num_diagnostics () const { return m_diagnostics.length (); }
 
   void on_condition (tree lhs ATTRIBUTE_UNUSED,
-- 
1.8.5.3

