From 79c9bfce7452e70eee2ef6e9e538b7e9b3d80e8b Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Wed, 29 Apr 2020 14:40:43 -0400
Subject: [PATCH 090/132] FIXME: sm-state: enough for first double-free to be
 emitted

---
 gcc/analyzer/diagnostic-manager.cc |   4 +
 gcc/analyzer/engine.cc             | 462 ++++++++++++++++++++++++++++-
 gcc/analyzer/exploded-graph.h      |  73 +++++
 gcc/analyzer/program-state.cc      |  20 +-
 gcc/analyzer/program-state.h       |   4 +-
 gcc/analyzer/region-model2.cc      | 101 ++++---
 gcc/analyzer/region-model2.h       |   6 +-
 7 files changed, 613 insertions(+), 57 deletions(-)

diff --git a/gcc/analyzer/diagnostic-manager.cc b/gcc/analyzer/diagnostic-manager.cc
index e132a5625de..7b3c435182e 100644
--- a/gcc/analyzer/diagnostic-manager.cc
+++ b/gcc/analyzer/diagnostic-manager.cc
@@ -593,6 +593,10 @@ get_any_origin (const gimple *stmt,
 
   gcc_assert (dst_rep);
 
+  // TODO:
+  if (dst_state.m_region_model == NULL)
+    return NULL;
+
   if (const gassign *assign = dyn_cast <const gassign *> (stmt))
     {
       tree lhs = gimple_assign_lhs (assign);
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index dd41d040faf..cafbc031a07 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -151,6 +151,83 @@ impl_region_model_context::on_unknown_change (svalue_id sid)
     smap->on_unknown_change (sid);
 }
 
+/* class impl_region_model2_context : public region_model_context.  */
+
+impl_region_model2_context::
+impl_region_model2_context (exploded_graph &eg,
+			   const exploded_node *enode_for_diag,
+			   const program_state *old_state,
+			   program_state *new_state,
+			   state_change *change,
+			   const gimple *stmt,
+			   stmt_finder *stmt_finder)
+: m_eg (&eg), m_logger (eg.get_logger ()),
+  m_enode_for_diag (enode_for_diag),
+  m_old_state (old_state),
+  m_new_state (new_state),
+  m_change (change),
+  m_stmt (stmt),
+  m_stmt_finder (stmt_finder),
+  m_ext_state (eg.get_ext_state ())
+{
+}
+
+impl_region_model2_context::
+impl_region_model2_context (program_state *state,
+			   state_change *change,
+			   const extrinsic_state &ext_state,
+			   logger *logger)
+: m_eg (NULL), m_logger (logger), m_enode_for_diag (NULL),
+  m_old_state (NULL),
+  m_new_state (state),
+  m_change (change),
+  m_stmt (NULL),
+  m_stmt_finder (NULL),
+  m_ext_state (ext_state)
+{
+}
+
+void
+impl_region_model2_context::warn (pending_diagnostic *d)
+{
+  LOG_FUNC (get_logger ());
+  if (m_eg)
+    m_eg->get_diagnostic_manager ().add_diagnostic
+      (m_enode_for_diag, m_enode_for_diag->get_supernode (),
+       m_stmt, m_stmt_finder, d);
+}
+
+#if 0
+int
+impl_region_model2_context::on_svalue_purge (svalue_id first_unused_sid,
+					    const svalue_id_map &map)
+{
+  int total = 0;
+  int sm_idx;
+  sm_state_map *smap;
+  FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
+    {
+      const state_machine &sm = m_ext_state.get_sm (sm_idx);
+      total += smap->on_svalue_purge (sm, sm_idx, first_unused_sid,
+				      map, this);
+    }
+  if (m_change)
+    total += m_change->on_svalue_purge (first_unused_sid);
+  return total;
+}
+#endif
+
+#if 0
+void
+impl_region_model2_context::on_unknown_change (svalue_id sid)
+{
+  int sm_idx;
+  sm_state_map *smap;
+  FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
+    smap->on_unknown_change (sid);
+}
+#endif
+
 /* class setjmp_svalue : public svalue.  */
 
 /* Compare the fields of this setjmp_svalue with OTHER, returning true
@@ -350,6 +427,175 @@ public:
   stmt_finder *m_stmt_finder;
 };
 
+// FIXME: revamped version:
+/* Concrete implementation of sm_context, wiring it up to the rest of this
+   file.  */
+
+class impl_sm_context2 : public sm_context
+{
+public:
+  impl_sm_context2 (exploded_graph &eg,
+		    int sm_idx,
+		    const state_machine &sm,
+		    const exploded_node *enode_for_diag,
+		    const program_state *old_state,
+		    program_state *new_state,
+		    state_change *change,
+		    const sm_state_map2 *old_smap,
+		    sm_state_map2 *new_smap,
+		    stmt_finder *stmt_finder = NULL)
+  : sm_context (sm_idx, sm),
+    m_logger (eg.get_logger ()),
+    m_eg (eg), m_enode_for_diag (enode_for_diag),
+    m_old_state (old_state), m_new_state (new_state),
+    m_change (change),
+    m_old_smap (old_smap), m_new_smap (new_smap),
+    m_stmt_finder (stmt_finder)
+  {
+  }
+
+  logger *get_logger () const { return m_logger.get_logger (); }
+
+  tree get_fndecl_for_call (const gcall *call) FINAL OVERRIDE
+  {
+    impl_region_model2_context old_ctxt
+      (m_eg, m_enode_for_diag, NULL, NULL/*m_enode->get_state ()*/,
+       m_change, call);
+    region_model2 *model = m_new_state->m_region_model2;
+    return model->get_fndecl_for_call (call, &old_ctxt);
+  }
+
+  void on_transition (const supernode *node  ATTRIBUTE_UNUSED,
+		      const gimple *stmt  ATTRIBUTE_UNUSED,
+		      tree var,
+		      state_machine::state_t from,
+		      state_machine::state_t to,
+		      tree origin) FINAL OVERRIDE
+  {
+    logger * const logger = get_logger ();
+    LOG_FUNC (logger);
+    impl_region_model2_context old_ctxt
+      (m_eg, m_enode_for_diag, NULL, NULL/*m_enode->get_state ()*/,
+       m_change, stmt);
+    svalue2 *var_old_sval
+      = m_old_state->m_region_model2->get_rvalue (var, &old_ctxt);
+
+    impl_region_model2_context new_ctxt (m_eg, m_enode_for_diag,
+					 m_old_state, m_new_state,
+					 m_change, NULL);
+    svalue2 *var_new_sval
+      = m_new_state->m_region_model2->get_rvalue (var, &new_ctxt);
+    svalue2 *origin_new_sval
+      = m_new_state->m_region_model2->get_rvalue (origin, &new_ctxt);
+
+    state_machine::state_t current = m_old_smap->get_state (var_old_sval);
+    if (current == from)
+      {
+	if (logger)
+	  logger->log ("%s: state transition of %qE: %s -> %s",
+		       m_sm.get_name (),
+		       var,
+		       m_sm.get_state_name (from),
+		       m_sm.get_state_name (to));
+	m_new_smap->set_state (m_new_state->m_region_model2, var_new_sval,
+			       to, origin_new_sval);
+#if 0
+	if (m_change)
+	  m_change->add_sm_change (m_sm_idx, var_new_sval, from, to);
+#endif
+      }
+  }
+
+  void warn_for_state (const supernode *snode, const gimple *stmt,
+		       tree var, state_machine::state_t state,
+		       pending_diagnostic *d) FINAL OVERRIDE
+  {
+    LOG_FUNC (get_logger ());
+    gcc_assert (d); // take ownership
+
+    impl_region_model2_context old_ctxt
+      (m_eg, m_enode_for_diag, m_old_state, m_new_state, m_change, NULL);
+    state_machine::state_t current;
+    if (var)
+      {
+	svalue2 *var_old_sval
+	  = m_old_state->m_region_model2->get_rvalue (var, &old_ctxt);
+	current = m_old_smap->get_state (var_old_sval);
+      }
+    else
+      current = m_old_smap->get_global_state ();
+
+    if (state == current)
+      {
+	m_eg.get_diagnostic_manager ().add_diagnostic
+	  (&m_sm, m_enode_for_diag, snode, stmt, m_stmt_finder,
+	   var, state, d);
+      }
+    else
+      delete d;
+  }
+
+  /* Hook for picking more readable trees for SSA names of temporaries,
+     so that rather than e.g.
+       "double-free of '<unknown>'"
+     we can print:
+       "double-free of 'inbuf.data'".  */
+
+  tree get_readable_tree (tree expr) FINAL OVERRIDE
+  {
+    /* Only for SSA_NAMEs of temporaries; otherwise, return EXPR, as it's
+       likely to be the least surprising tree to report.  */
+    if (TREE_CODE (expr) != SSA_NAME)
+      return expr;
+    if (SSA_NAME_VAR (expr) != NULL)
+      return expr;
+
+#if 1
+    // TODO:
+    return expr;
+#else
+    gcc_assert (m_new_state);
+    svalue2 *sval = m_new_state->m_region_model2->get_rvalue (expr, NULL);
+    /* Find trees for all regions storing the value.  */
+    auto_vec<path_var> pvs;
+    m_new_state->m_region_model2->get_path_vars_for_svalue (sval, &pvs);
+    if (pvs.length () < 1)
+      return expr;
+    /* Pick the "best" such tree.  */
+    // TODO: should we also consider (and consolidate) equiv classes?
+    pvs.qsort (readability_comparator);
+    return pvs[0].m_tree;
+#endif
+  }
+
+  state_machine::state_t get_global_state () const FINAL OVERRIDE
+  {
+    return m_old_state->m_checker_states[m_sm_idx]->get_global_state ();
+  }
+
+  void set_global_state (state_machine::state_t state) FINAL OVERRIDE
+  {
+    m_new_state->m_checker_states[m_sm_idx]->set_global_state (state);
+  }
+
+  void on_custom_transition (custom_transition *transition) FINAL OVERRIDE
+  {
+    transition->impl_transition (&m_eg,
+				 const_cast<exploded_node *> (m_enode_for_diag),
+				 m_sm_idx);
+  }
+
+  log_user m_logger;
+  exploded_graph &m_eg;
+  const exploded_node *m_enode_for_diag;
+  const program_state *m_old_state;
+  program_state *m_new_state;
+  state_change *m_change;
+  const sm_state_map2 *m_old_smap;
+  sm_state_map2 *m_new_smap;
+  stmt_finder *m_stmt_finder;
+};
+
 /* Subclass of stmt_finder for finding the best stmt to report the leak at,
    given the emission path.  */
 
@@ -708,6 +954,69 @@ impl_region_model_context::on_unexpected_tree_code (tree t,
     m_new_state->m_valid = false;
 }
 
+/* Implementation of region_model_context::on_condition vfunc.
+   Notify all state machines about the condition, which could lead to
+   state transitions.  */
+
+void
+impl_region_model2_context::on_condition (tree lhs, enum tree_code op, tree rhs)
+{
+  int sm_idx;
+  sm_state_map *smap;
+  FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
+    {
+      const state_machine &sm = m_ext_state.get_sm (sm_idx);
+      impl_sm_context sm_ctxt (*m_eg, sm_idx, sm, m_enode_for_diag,
+			       m_old_state, m_new_state,
+			       m_change,
+			       m_old_state->m_checker_states[sm_idx],
+			       m_new_state->m_checker_states[sm_idx]);
+      sm.on_condition (&sm_ctxt,
+		       m_enode_for_diag->get_supernode (), m_stmt,
+		       lhs, op, rhs);
+    }
+}
+
+/* Implementation of region_model_context::on_phi vfunc.
+   Notify all state machines about the phi, which could lead to
+   state transitions.  */
+
+void
+impl_region_model2_context::on_phi (const gphi *phi, tree rhs)
+{
+  int sm_idx;
+  sm_state_map *smap;
+  FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
+    {
+      const state_machine &sm = m_ext_state.get_sm (sm_idx);
+      impl_sm_context sm_ctxt (*m_eg, sm_idx, sm, m_enode_for_diag,
+			       m_old_state, m_new_state,
+			       m_change,
+			       m_old_state->m_checker_states[sm_idx],
+			       m_new_state->m_checker_states[sm_idx]);
+      sm.on_phi (&sm_ctxt, m_enode_for_diag->get_supernode (), phi, rhs);
+    }
+}
+
+/* Implementation of region_model_context::on_unexpected_tree_code vfunc.
+   Mark the new state as being invalid for further exploration.
+   TODO(stage1): introduce a warning for when this occurs.  */
+
+void
+impl_region_model2_context::on_unexpected_tree_code (tree t,
+						    const dump_location_t &loc)
+{
+  logger * const logger = get_logger ();
+  if (logger)
+    logger->log ("unhandled tree code: %qs in %qs at %s:%i",
+		 get_tree_code_name (TREE_CODE (t)),
+		 loc.get_impl_location ().m_function,
+		 loc.get_impl_location ().m_file,
+		 loc.get_impl_location ().m_line);
+  if (m_new_state)
+    m_new_state->m_valid = false;
+}
+
 /* struct point_and_state.  */
 
 /* Assert that this object is sane.  */
@@ -1030,12 +1339,7 @@ exploded_node::on_stmt (exploded_graph &eg,
 	 return on_stmt_flags::terminate_path ();
      }
   else if (state->m_region_model2)
-    {
-      if (state->m_region_model2->on_stmt (snode, stmt))
-	return on_stmt_flags (false);
-      else
-	return on_stmt_flags::terminate_path ();
-    }
+    return on_stmt2 (eg, snode, stmt, state, change);
 
   /* Preserve the old state.  It is used here for looking
      up old checker states, for determining state transitions, and
@@ -1154,6 +1458,152 @@ exploded_node::on_stmt (exploded_graph &eg,
   return on_stmt_flags (any_sm_changes);
 }
 
+/* Modify STATE in place, applying the effects of the stmt at this node's
+   point.  */
+
+exploded_node::on_stmt_flags
+exploded_node::on_stmt2 (exploded_graph &eg,
+			const supernode *snode,
+			const gimple *stmt,
+			program_state *state,
+			state_change *change) const
+{
+  // FIXME: this is for revamped states
+  gcc_assert (state->m_region_model2);
+#if 0
+    {
+      if (state->m_region_model2->on_stmt (snode, stmt))
+	return on_stmt_flags (false);
+      else
+	return on_stmt_flags::terminate_path ();
+    }
+#endif
+
+  /* Preserve the old state.  It is used here for looking
+     up old checker states, for determining state transitions, and
+     also within impl_region_model_context and impl_sm_context for
+     going from tree to svalue_id.  */
+  const program_state old_state (*state);
+
+  impl_region_model2_context ctxt (eg, this,
+				   &old_state, state, change,
+				   stmt);
+  
+  if (const gassign *assign = dyn_cast <const gassign *> (stmt))
+    state->m_region_model2->on_stmt (snode, stmt/*, &ctxt*/);
+
+  if (const greturn *return_ = dyn_cast <const greturn *> (stmt))
+    state->m_region_model2->on_stmt (snode, stmt/*, return_, &ctxt*/);
+
+  /* Track whether we have a gcall to a function that's not recognized by
+     anything, for which we don't have a function body, or for which we
+     don't know the fndecl.  */
+  bool unknown_side_effects = false;
+  if (const gcall *call = dyn_cast <const gcall *> (stmt))
+    {
+      /* Debugging/test support.  */
+      if (is_special_named_call_p (call, "__analyzer_dump", 0))
+	{
+	  /* Handle the builtin "__analyzer_dump" by dumping state
+	     to stderr.  */
+	  dump (eg.get_ext_state ());
+	}
+      else if (is_special_named_call_p (call, "__analyzer_dump_path", 0))
+	{
+	  /* Handle the builtin "__analyzer_dump_path" by queuing a
+	     diagnostic at this exploded_node.  */
+#if 0
+	  ctxt.warn (new dump_path_diagnostic ());
+#endif
+	}
+      else if (is_special_named_call_p (call, "__analyzer_dump_region_model", 0))
+	{
+	  /* Handle the builtin "__analyzer_dump_region_model" by dumping
+	     the region model's state to stderr.  */
+	  state->m_region_model->dump (false);
+	}
+      else if (is_special_named_call_p (call, "__analyzer_eval", 1))
+	{
+	  /* Handle the builtin "__analyzer_eval" by evaluating the input
+	     and dumping as a dummy warning, so that test cases can use
+	     dg-warning to validate the result (and so unexpected warnings will
+	     lead to DejaGnu failures).  */
+	  tree t_arg = gimple_call_arg (call, 0);
+	  tristate t
+	    = state->m_region_model2->eval_condition (t_arg,
+						      NE_EXPR,
+						      integer_zero_node,
+						      NULL /* &ctxt*/);
+	  warning_at (call->location, 0, "%s", t.as_string ());
+	}
+      else if (is_special_named_call_p (call, "__analyzer_break", 0))
+	{
+	  /* Handle the builtin "__analyzer_break" by triggering a
+	     breakpoint.  */
+	  /* TODO: is there a good cross-platform way to do this?  */
+	  raise (SIGINT);
+	}
+      else if (is_special_named_call_p (call, "__analyzer_dump_exploded_nodes",
+					1))
+	{
+	  /* This is handled elsewhere.  */
+	}
+      else if (is_setjmp_call_p (call))
+	gcc_unreachable ();
+#if 0
+	state->m_region_model2->on_setjmp (call, this, &ctxt);
+#endif
+      else if (is_longjmp_call_p (call))
+	{
+	  gcc_unreachable ();
+#if 0
+	  on_longjmp (eg, call, state, &ctxt);
+	  return on_stmt_flags::terminate_path ();
+#endif
+	}
+      else
+	unknown_side_effects = state->m_region_model2->on_call_pre (call, &ctxt);
+    }
+
+  bool any_sm_changes = false;
+  int sm_idx;
+  sm_state_map2 *smap;
+  FOR_EACH_VEC_ELT (old_state.m_checker_states2, sm_idx, smap)
+    {
+      const state_machine &sm = eg.get_ext_state ().get_sm (sm_idx);
+      const sm_state_map2 *old_smap
+	= old_state.m_checker_states2[sm_idx];
+      sm_state_map2 *new_smap = state->m_checker_states2[sm_idx];
+      impl_sm_context2 sm_ctxt (eg, sm_idx, sm, this, &old_state, state,
+				change,
+				old_smap, new_smap);
+      /* Allow the state_machine to handle the stmt.  */
+      if (sm.on_stmt (&sm_ctxt, snode, stmt))
+	unknown_side_effects = false;
+      else
+	{
+	  /* For those stmts that were not handled by the state machine.  */
+	  if (const gcall *call = dyn_cast <const gcall *> (stmt))
+	    {
+	      tree callee_fndecl
+		= state->m_region_model2->get_fndecl_for_call (call, &ctxt);
+
+	      if (!fndecl_has_gimple_body_p (callee_fndecl))
+		new_smap->purge_for_unknown_fncall (eg, sm, call, callee_fndecl,
+						    state->m_region_model2,
+						    &ctxt);
+	    }
+	}
+      if (*old_smap != *new_smap)
+	any_sm_changes = true;
+    }
+
+  if (const gcall *call = dyn_cast <const gcall *> (stmt))
+    state->m_region_model2->on_call_post (call, unknown_side_effects, &ctxt);
+
+  return on_stmt_flags (any_sm_changes);
+}
+
 /* Consider the effect of following superedge SUCC from this node.
 
    Return true if it's feasible to follow the edge, or false
diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h
index c252d76477f..d60e523700f 100644
--- a/gcc/analyzer/exploded-graph.h
+++ b/gcc/analyzer/exploded-graph.h
@@ -21,6 +21,8 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_ANALYZER_EXPLODED_GRAPH_H
 #define GCC_ANALYZER_EXPLODED_GRAPH_H
 
+#include "region-model2.h" // FIXME
+
 namespace ana {
 
 /* Concrete implementation of region_model_context, wiring it up to the
@@ -91,6 +93,72 @@ class impl_region_model_context : public region_model_context
   const extrinsic_state &m_ext_state;
 };
 
+/* Concrete implementation of region_model_context, wiring it up to the
+   rest of the analysis engine.  */
+
+class impl_region_model2_context : public region_model2_context
+{
+ public:
+  impl_region_model2_context (exploded_graph &eg,
+			     const exploded_node *enode_for_diag,
+
+			     /* TODO: should we be getting the ECs from the
+				old state, rather than the new?  */
+			     const program_state *old_state,
+			     program_state *new_state,
+			     state_change *change,
+
+			     const gimple *stmt,
+			     stmt_finder *stmt_finder = NULL);
+
+  impl_region_model2_context (program_state *state,
+			     state_change *change,
+			     const extrinsic_state &ext_state,
+			     logger *logger = NULL);
+
+  void warn (pending_diagnostic *d) FINAL OVERRIDE;
+
+#if 0
+  int on_svalue_purge (svalue_id first_unused_sid,
+		       const svalue_id_map &map) FINAL OVERRIDE;
+#endif
+  logger *get_logger () FINAL OVERRIDE
+  {
+    return m_logger.get_logger ();
+  }
+#if 0
+  void on_state_leak (const state_machine &sm,
+		      int sm_idx,
+		      svalue_id sid,
+		      svalue_id first_unused_sid,
+		      const svalue_id_map &map,
+		      state_machine::state_t state);
+
+  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;
+#endif
+  void on_condition (tree lhs, enum tree_code op, tree rhs) FINAL OVERRIDE;
+#if 0
+  void on_unknown_change (svalue_id sid ATTRIBUTE_UNUSED) FINAL OVERRIDE;
+#endif
+  void on_phi (const gphi *phi, tree rhs) FINAL OVERRIDE;
+  void on_unexpected_tree_code (tree t,
+				const dump_location_t &loc) FINAL OVERRIDE;
+
+  exploded_graph *m_eg;
+  log_user m_logger;
+  const exploded_node *m_enode_for_diag;
+  const program_state *m_old_state;
+  program_state *m_new_state;
+  state_change *m_change;
+  const gimple *m_stmt;
+  stmt_finder *m_stmt_finder;
+  const extrinsic_state &m_ext_state;
+};
+
 /* A <program_point, program_state> pair, used internally by
    exploded_node as its immutable data, and as a key for identifying
    exploded_nodes we've already seen in the graph.  */
@@ -228,6 +296,11 @@ class exploded_node : public dnode<eg_traits>
 			 const gimple *stmt,
 			 program_state *state,
 			 state_change *change) const;
+  on_stmt_flags on_stmt2 (exploded_graph &eg,
+			  const supernode *snode,
+			  const gimple *stmt,
+			  program_state *state,
+			  state_change *change) const;
   bool on_edge (exploded_graph &eg,
 		const superedge *succ,
 		program_point *next_point,
diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc
index 445989b298d..bcabb54ce0a 100644
--- a/gcc/analyzer/program-state.cc
+++ b/gcc/analyzer/program-state.cc
@@ -869,10 +869,10 @@ sm_state_map2::purge_for_unknown_fncall (const exploded_graph &eg,
 					const state_machine &sm,
 					const gcall *call,
 					tree fndecl,
-					region_model *new_model,
-					region_model_context *ctxt)
+					region_model2 *new_model,
+					region_model2_context *ctxt)
 {
-  gcc_unreachable ();
+  // TODO
 #if 0
   logger * const logger = eg.get_logger ();
   if (logger)
@@ -1255,10 +1255,16 @@ program_state::dump_to_pp (const extrinsic_state &ext_state,
 			   bool summarize,
 			   pretty_printer *pp) const
 {
-  if (!m_region_model)
-    return;
-  pp_printf (pp, "rmodel: ");
-  m_region_model->dump_to_pp (pp, summarize);
+  if (m_region_model)
+    {
+      pp_printf (pp, "rmodel: ");
+      m_region_model->dump_to_pp (pp, summarize);
+    }
+  if (m_region_model2)
+    {
+      pp_printf (pp, "rmodel2: ");
+      m_region_model2->dump_to_pp (pp, summarize);
+    }
 
   int i;
   sm_state_map *smap;
diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h
index b759f3bcbb1..2a1e9ee6914 100644
--- a/gcc/analyzer/program-state.h
+++ b/gcc/analyzer/program-state.h
@@ -297,8 +297,8 @@ public:
   void purge_for_unknown_fncall (const exploded_graph &eg,
 				 const state_machine &sm,
 				 const gcall *call, tree fndecl,
-				 region_model *new_model,
-				 region_model_context *ctxt);
+				 region_model2 *new_model,
+				 region_model2_context *ctxt);
 
 #if 0
   int on_svalue_purge (const state_machine &sm,
diff --git a/gcc/analyzer/region-model2.cc b/gcc/analyzer/region-model2.cc
index ffa3b634bfa..f5c9515e38b 100644
--- a/gcc/analyzer/region-model2.cc
+++ b/gcc/analyzer/region-model2.cc
@@ -3539,7 +3539,8 @@ region_model2::on_stmt (const supernode *snode,
   switch (stmt->code)
     {
     case GIMPLE_CALL:
-      return on_gcall (snode, as_a <const gcall *> (stmt));
+      gcc_unreachable ();
+      //return on_gcall (snode, as_a <const gcall *> (stmt));
 
     case GIMPLE_COND:
       /* This is a no-op; the condition is handled by the true/false
@@ -3760,7 +3761,7 @@ region_model2::on_gassign (const supernode */*snode*/,
 }
 
 /* FIXME.  */
-
+#if 0
 bool
 region_model2::on_gcall (const supernode *snode,
 			 const gcall *call)
@@ -3865,6 +3866,7 @@ region_model2::on_gcall (const supernode *snode,
 
   return true;
 }
+#endif
 
 /* Update this model for the CALL stmt, using CTXT to report any
    diagnostics - the first half.
@@ -3876,7 +3878,7 @@ region_model2::on_gcall (const supernode *snode,
    Return true if the function call has unknown side effects (it wasn't
    recognized and we don't have a body for it, or are unable to tell which
    fndecl it is).  */
-#if 0
+#if 1
 bool
 region_model2::on_call_pre (const gcall *call, region_model2_context *ctxt)
 {
@@ -3891,38 +3893,52 @@ region_model2::on_call_pre (const gcall *call, region_model2_context *ctxt)
   /* Check for uses of poisoned values.
      For now, special-case "free", to avoid warning about "use-after-free"
      when "double free" would be more precise.  */
+#if 0
   if (!is_special_named_call_p (call, "free", 1))
     for (unsigned i = 0; i < gimple_call_num_args (call); i++)
       check_for_poison (gimple_call_arg (call, i), ctxt);
+#endif
 
-  bool unknown_svale_effects = false;
+  bool unknown_side_effects = false;
 
   if (tree callee_fndecl = get_fndecl_for_call (call, ctxt))
     {
+      /* Get any left-hand side for the destination of the result.  */
+      region2 *lhs_reg = NULL;
+      tree lhs_type = NULL_TREE;
+      if (tree lhs = gimple_call_lhs (call))
+	{
+	  lhs_reg = get_lvalue (lhs, ctxt);
+	  lhs_type = TREE_TYPE (lhs);
+	}
+
       if (is_named_call_p (callee_fndecl, "malloc", call, 1))
 	{
-	  // TODO: capture size as a svalue2?
-	  region2 *new_reg = add_new_malloc_region2 ();
+	  tree size_arg = gimple_call_arg (call, 0);
+	  svalue2 *size_sval = get_rvalue (size_arg, ctxt);
+	  region2 *new_reg = create_region_for_heap_alloc (size_sval);
 	  if (lhs_reg)
 	    {
-	      svalue2 *ptr_sval
-		= get_or_create_ptr_svalue2 (lhs_type, new_reg);
+	      svalue2 *ptr_sval = m_mgr->get_ptr_svalue2 (lhs_type, new_reg);
 	      set_value (lhs_reg, ptr_sval, ctxt);
 	    }
-	  return false;
+	  else
+	    {
+	      // FIXME: report a leak here?
+	    }
+	  return true;
 	}
       else if (is_named_call_p (callee_fndecl, "__builtin_alloca", call, 1))
 	{
-	  region2 *frame_reg = get_current_frame *();
-	  region2 *new_reg
-	    = add_region2 (new symbolic_region2 (frame_reg, NULL_TREE, false));
+	  tree size_arg = gimple_call_arg (call, 0);
+	  svalue2 *size_sval = get_rvalue (size_arg, ctxt);
+	  region2 *new_reg = create_region_for_alloca (size_sval);
 	  if (lhs_reg)
 	    {
-	      svalue2 *ptr_sval
-		= get_or_create_ptr_svalue2 (lhs_type, new_reg);
+	      svalue2 *ptr_sval = m_mgr->get_ptr_svalue2 (lhs_type, new_reg);
 	      set_value (lhs_reg, ptr_sval, ctxt);
 	    }
-	  return false;
+	  return true;
 	}
       else if (gimple_call_builtin_p (call, BUILT_IN_EXPECT)
 	       || gimple_call_builtin_p (call, BUILT_IN_EXPECT_WITH_PROBABILITY)
@@ -3933,35 +3949,34 @@ region_model2::on_call_pre (const gcall *call, region_model2_context *ctxt)
 	    {
 	      tree initial_arg = gimple_call_arg (call, 0);
 	      svalue2 *sval = get_rvalue (initial_arg, ctxt);
-	      set_value (lhs_reg, sid, ctxt);
+	      set_value (lhs_reg, sval, ctxt);
 	    }
 	  return false;
 	}
       else if (is_named_call_p (callee_fndecl, "strlen", call, 1))
 	{
-	  region2 *buf_reg = deref_rvalue (gimple_call_arg (call, 0), ctxt);
-	  svalue2 *buf_sval
-	    = get_region2 (buf_reg)->get_value (*this, true, ctxt);
-	  if (tree cst_expr = maybe_get_constant (buf_sval))
+	  tree arg = gimple_call_arg (call, 0);
+	  svalue2 *arg_sval = get_rvalue (arg, ctxt);
+	  region2 *buf_reg = deref_rvalue (arg_sval, ctxt);
+	  if (string_region2 *str_reg = buf_reg->dyn_cast_string_region2 ())
 	    {
-	      if (TREE_CODE (cst_expr) == STRING_CST
-		  && lhs_reg)
-		{
-		  /* TREE_STRING_LENGTH is sizeof, not strlen.  */
-		  int sizeof_cst = TREE_STRING_LENGTH (cst_expr);
-		  int strlen_cst = sizeof_cst - 1;
-		  tree t_cst = build_int_cst (lhs_type, strlen_cst);
-		  svalue2 *result_sval
-		    = get_or_create_constant_svalue2 (t_cst);
-		  set_value (lhs_reg, result_sval, ctxt);
-		  return false;
-		}
+	      tree str_cst = str_reg->get_string_cst ();
+	      /* TREE_STRING_LENGTH is sizeof, not strlen.  */
+	      int sizeof_cst = TREE_STRING_LENGTH (str_cst);
+	      int strlen_cst = sizeof_cst - 1;
+	      tree t_cst = build_int_cst (lhs_type, strlen_cst);
+	      svalue2 *result_sval
+		= m_mgr->get_or_create_constant_svalue2 (t_cst);
+	      set_value (lhs_reg, result_sval, ctxt);
+	      return true;
 	    }
 	  /* Otherwise an unknown value.  */
+	  return true;
 	}
       else if (is_named_call_p (callee_fndecl,
 				"__analyzer_dump_num_heap_region2s", call, 0))
 	{
+#if 0
 	  /* Handle the builtin "__analyzer_dump_num_heap_region2s" by emitting
 	     a warning (for use in DejaGnu tests).  */
 	  int num_heap_region2s = 0;
@@ -3975,19 +3990,22 @@ region_model2::on_call_pre (const gcall *call, region_model2_context *ctxt)
 	  warning_at (call->location, 0,
 		      "num heap region2s: %qi", num_heap_region2s);
 	  return false;
+#endif
 	}
       else if (!fndecl_has_gimple_body_p (callee_fndecl)
 	       && !DECL_PURE_P (callee_fndecl))
-	unknown_svale_effects = true;
+	unknown_side_effects = true;
     }
   else
-    unknown_svale_effects = true;
+    unknown_side_effects = true;
 
   /* Unknown return value.  */
+#if 0
   if (lhs_reg)
     set_to_new_unknown_value (lhs_reg, lhs_type, ctxt);
+#endif
 
-  return unknown_svale_effects;
+  return unknown_side_effects;
 }
 #endif
 
@@ -3998,14 +4016,15 @@ region_model2::on_call_pre (const gcall *call, region_model2_context *ctxt)
    are updated are done here; other updates to the region_model2 are done
    in region_model2::on_call_pre.
 
-   If UNKNOWN_SVALE_EFFECTS is true, also call handle_unrecognized_call
+   If UNKNOWN_SIDE_EFFECTS is true, also call handle_unrecognized_call
    to purge state.  */
-#if 0
+#if 1
 void
 region_model2::on_call_post (const gcall *call,
-			    bool unknown_svale_effects,
-			    region_model2_context *ctxt)
+			     bool unknown_side_effects,
+			     region_model2_context *ctxt)
 {
+#if 0
   /* Update for "free" here, after sm-handling.
 
      If the ptr points to an underlying heap region2, delete the region2,
@@ -4025,7 +4044,6 @@ region_model2::on_call_post (const gcall *call,
       {
 	tree ptr = gimple_call_arg (call, 0);
 	svalue2 *ptr_sval = get_rvalue (ptr, ctxt);
-	svalue2 *ptr_sval = get_svalue2 (ptr_sval);
 	if (region_svalue2 *ptr_to_region2_sval
 	    = ptr_sval->dyn_cast_region_svalue2 ())
 	  {
@@ -4047,8 +4065,9 @@ region_model2::on_call_post (const gcall *call,
 	  }
 	return;
       }
+#endif
 
-  if (unknown_svale_effects)
+  if (unknown_side_effects)
     handle_unrecognized_call (call, ctxt);
 }
 #endif
diff --git a/gcc/analyzer/region-model2.h b/gcc/analyzer/region-model2.h
index b7e5ea2e256..531927a996c 100644
--- a/gcc/analyzer/region-model2.h
+++ b/gcc/analyzer/region-model2.h
@@ -1676,7 +1676,7 @@ class region_model2
 		const gimple *stmt);
   bool on_gassign (const supernode *snode,
 		   const gassign *stmt);
-#if 1
+#if 0
   bool on_gcall (const supernode *snode,
 		 const gcall *stmt);
 #else
@@ -1927,7 +1927,9 @@ class region_model2_context
      has purged (potentially for use in selftests).
      MAP has already been applied to the IDs, but is provided in case
      the client needs to figure out the old IDs.  */
+#if 0
   virtual int on_svalue2_purge (svalue2 *purged_value) = 0;
+#endif
 
   virtual logger *get_logger () = 0;
 
@@ -1976,10 +1978,12 @@ class noop_region_model2_context : public region_model2_context
 {
 public:
   void warn (pending_diagnostic *) OVERRIDE {}
+#if 0
   int on_svalue2_purge (svalue2 *) OVERRIDE
   {
     return 0;
   }
+#endif
   logger *get_logger () OVERRIDE { return NULL; }
 #if 0
   void on_inherited_svalue2 (svalue2_id parent_sid ATTRIBUTE_UNUSED,
-- 
2.21.0

