From 4b92a203c9f7834eab556a1f22355b91813688f9 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 18 Mar 2021 09:46:00 -0400
Subject: [PATCH 19/20] FIXME: further work on call summaries

---
 gcc/analyzer/call-summary.cc                  | 474 +++++++++++++-----
 gcc/analyzer/call-summary.h                   |   7 +
 gcc/analyzer/region-model-manager.cc          |   9 +
 gcc/analyzer/store.h                          |   2 +
 .../analyzer/call-summaries-aliasing-1.c      |  40 ++
 .../analyzer/call-summaries-data-model-3.c    |  19 +
 .../analyzer/call-summaries-data-model-4.c    |  38 ++
 .../gcc.dg/analyzer/call-summaries-malloc-6.c |  19 +
 .../analyzer/call-summaries-noreturn-3.c      |  30 ++
 .../analyzer/call-summaries-return-expr-1.c   |  44 ++
 .../gcc.dg/analyzer/call-summaries-setjmp-1.c |   6 +-
 .../analyzer/call-summaries-stale-frame-1.c   |  15 +
 .../analyzer/call-summaries-unknown-fn-1.c    |   8 +-
 13 files changed, 573 insertions(+), 138 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/call-summaries-aliasing-1.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/call-summaries-data-model-3.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/call-summaries-data-model-4.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/call-summaries-malloc-6.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/call-summaries-noreturn-3.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/call-summaries-stale-frame-1.c

diff --git a/gcc/analyzer/call-summary.cc b/gcc/analyzer/call-summary.cc
index 5e341bedf05..cb3f5e6c051 100644
--- a/gcc/analyzer/call-summary.cc
+++ b/gcc/analyzer/call-summary.cc
@@ -276,6 +276,284 @@ summarization::apply_constraints (region_model *outcome_model,
   return v.valid_p ();
 }
 
+/* FIXME.  */
+
+void
+summarization::apply_store (const store *outcome_store,
+			    region_model *dest_model)
+{
+  store *dest_store = dest_model->get_store ();
+
+#if 1
+  /* Did the called function call an unknown function?
+     If so, handle it.  */
+  if (outcome_store->called_unknown_fn_p ())
+    {
+      /* FIXME: this creates conjured values for the call without
+	 specifyin that it's the call within the summary, so there
+	 could be false reuse of values.  */
+      /* FIXME: need to mark things as escaped for this to work, I think.
+	 region_model::handle_unrecognized_call normally does this for
+	 the direct call case.  */
+      /* FIXME: I *think* that we can regions that escaped in the
+	 outcome_store and update the dest_store accordingly.  */
+      // FIXME: probably do this first
+      dest_store->m_called_unknown_fn = true; //on_unknown_fncall (call_stmt, get_store_manager ());
+      //gcc_unreachable (); // FIXME
+    }
+#endif
+
+  /* Sort for determinism.  */
+  auto_vec <const region *> outcome_base_regs;
+  for (store::cluster_map_t::iterator iter = outcome_store->begin ();
+       iter != outcome_store->end (); ++iter)
+    {
+      const region *reg = (*iter).first;
+      outcome_base_regs.safe_push (reg);
+    }
+  outcome_base_regs.qsort (region::cmp_ptr_ptr);
+
+  const region *outcome_base_reg;
+  unsigned i;
+  FOR_EACH_VEC_ELT (outcome_base_regs, i, outcome_base_reg)
+    {
+      /* Ignore bindings within the frame.  */
+      // FIXME: could be another frame
+      if (outcome_base_reg->maybe_get_frame_region ())
+	continue;
+
+      const binding_cluster *outcome_cluster
+	= outcome_store->get_cluster (outcome_base_reg);
+#if 0
+      if (outcome_cluster->escaped_p ())
+	{
+	  gcc_unreachable ();
+	}
+#endif
+#if 0
+      if (outcome_cluster->touched_p ())
+	{
+	  gcc_unreachable ();
+	}
+#endif
+
+      switch (outcome_base_reg->get_kind ())
+	{
+	default:
+	  gcc_unreachable ();
+	case RK_FRAME:
+	  {
+	    gcc_unreachable ();
+	  };
+	  break;
+	case RK_GLOBALS:
+	  {
+	    gcc_unreachable ();
+	  };
+	  break;
+	case RK_CODE:
+	  {
+	    gcc_unreachable ();
+	  };
+	  break;
+	case RK_FUNCTION:
+	  {
+	    gcc_unreachable ();
+	  };
+	  break;
+	case RK_LABEL:
+	  {
+	    gcc_unreachable ();
+	  };
+	  break;
+	case RK_STACK:
+	  {
+	    gcc_unreachable ();
+	  };
+	  break;
+	case RK_HEAP:
+	  {
+	    gcc_unreachable ();
+	  };
+	  break;
+	case RK_ROOT:
+	  {
+	    gcc_unreachable ();
+	  };
+	  break;
+	case RK_SYMBOLIC:
+	  {
+	    const symbolic_region *sym_reg
+	      = as_a <const symbolic_region *> (outcome_base_reg);
+	    const svalue *outcome_ptr = sym_reg->get_pointer ();
+	    const svalue *dest_ptr
+	      = map_svalue_from_callee (outcome_ptr, dest_model);
+
+	    apply_binding_map (outcome_cluster->get_map (),
+			       dest_ptr,
+			       dest_model);
+	  }
+	  break;
+	case RK_DECL:
+	  {
+	    /* Update globals.  */
+	    if (tree decl = outcome_base_reg->maybe_get_decl ())
+	      if (is_global_var (decl))
+		{
+		  binding_cluster *dest_cluster
+		    = dest_store->get_or_create_cluster (outcome_base_reg);
+		  dest_cluster->purge_region (get_store_manager (),
+					      outcome_base_reg);
+
+		  // FIXME: make order deterministic
+		  for (binding_cluster::iterator_t iter
+			 = outcome_cluster->begin ();
+		       iter != outcome_cluster->end ();
+		       ++iter)
+		    {
+		      const binding_key *key = (*iter).first;
+		      const svalue *outcome_sval = (*iter).second;
+		      const svalue *dest_sval
+			= map_svalue_from_callee (outcome_sval,
+						  dest_model);
+		      dest_cluster->bind_key (key, dest_sval);
+		    }
+		  continue;
+		}
+	    gcc_unreachable ();
+	  }
+	  break;
+	case RK_FIELD:
+	  {
+	    gcc_unreachable ();
+	  };
+	  break;
+	case RK_ELEMENT:
+	  {
+	    gcc_unreachable ();
+	  };
+	  break;
+	case RK_OFFSET:
+	  {
+	    gcc_unreachable ();
+	  };
+	  break;
+	case RK_CAST:
+	  {
+	    gcc_unreachable ();
+	  };
+	  break;
+	case RK_HEAP_ALLOCATED:
+	  {
+	    const region *dest_base_reg
+	      = map_region_from_callee (outcome_base_reg,
+					dest_model);
+	    gcc_assert (dest_base_reg->base_region_p ());
+	    // FIXME: what if this isn't true?
+
+	    binding_cluster *dest_cluster
+	      = dest_store->get_or_create_cluster (dest_base_reg);
+#if 0
+	    dest_cluster->purge_region (get_store_manager (),
+					dest_base_reg);
+#endif
+
+	    // FIXME: make order deterministic
+	    for (binding_cluster::iterator_t iter
+		   = outcome_cluster->begin ();
+		 iter != outcome_cluster->end ();
+		 ++iter)
+	      {
+		const binding_key *key = (*iter).first;
+		const svalue *outcome_sval = (*iter).second;
+		const svalue *dest_sval
+		  = map_svalue_from_callee (outcome_sval, dest_model);
+		dest_cluster->bind_key (key, dest_sval);
+	      }
+	    // FIXME: maybe combine with global var code above
+	  }
+	  break;
+	case RK_ALLOCA:
+	  {
+	    gcc_unreachable ();
+	  };
+	  break;
+	case RK_STRING:
+	  {
+	    gcc_unreachable ();
+	  };
+	  break;
+	case RK_UNKNOWN:
+	  {
+	    gcc_unreachable ();
+	  };
+	  break;
+	}
+
+      /* FIXME: what about m_escaped and m_touched?  */
+    }
+}
+
+/* FIXME.  */
+
+void
+summarization::apply_binding_map (const binding_map &outcome_map,
+				  const svalue *dest_ptr,
+				  region_model *dest_model)
+{
+  region_model_manager *mgr = get_manager ();
+  store *dest_store = dest_model->get_store ();
+
+  /* Make order deterministic.  */
+  auto_vec <const binding_key *> outcome_binding_keys;
+  for (binding_map::iterator_t iter = outcome_map.begin ();
+       iter != outcome_map.end ();
+       ++iter)
+    {
+      const binding_key *key = (*iter).first;
+      outcome_binding_keys.safe_push (key);
+    }
+  outcome_binding_keys.qsort (binding_key::cmp_ptrs);
+
+  const binding_key *key;
+  unsigned i;
+  FOR_EACH_VEC_ELT (outcome_binding_keys, i, key)
+    {
+      const svalue *outcome_sval = outcome_map.get (key);
+      const svalue *dest_sval
+	= map_svalue_from_callee (outcome_sval, dest_model);
+      if (const concrete_binding *concrete_key
+	  = key->dyn_cast_concrete_binding ())
+	{
+	  auto_vec <const region *> dest_subregions;
+	  const region *dest_reg
+	    = mgr->get_symbolic_region (dest_ptr);
+	  dest_reg->get_subregions_for_binding
+	    (mgr,
+	     concrete_key->get_start_bit_offset (),
+	     concrete_key->get_size_in_bits (),
+	     dest_sval->get_type (), // FIXME
+	     &dest_subregions);
+	  unsigned i;
+	  const region *dest_subreg;
+	  FOR_EACH_VEC_ELT (dest_subregions, i, dest_subreg)
+	    {
+#if 0
+	      dest_subreg->dump (true);
+	      dest_sval->dump (true);
+#endif
+	      dest_store->set_value (get_store_manager (),
+				     dest_subreg, dest_sval,
+				     key->get_kind ());
+	    }
+	}
+      else
+	{
+	  //gcc_unreachable (); // FIXME
+	}
+    }
+}
+
 /* FIXME: remap values e.g. for the formal params
    back to the actual params.  */
 
@@ -294,13 +572,13 @@ summarization::map_svalue_from_callee (const svalue *callee_base_sval,
 	const region_svalue *callee_sval
 	  = static_cast<const region_svalue *> (callee_base_sval);
 	const region *callee_reg = callee_sval->get_pointee ();
-	if (const region *caller_reg
-	      = map_region_from_callee (callee_reg, dest_model))
-	  return mgr->get_ptr_svalue (type, caller_reg);
-	else
-	  {
-	    gcc_unreachable (); // FIXME
-	  }
+	/* Poison ptrs to locals.  */
+	if (callee_reg->maybe_get_frame_region ())
+	  return mgr->get_or_create_poisoned_svalue
+	    (POISON_KIND_POPPED_STACK, type);
+	const region *caller_reg
+	  = map_region_from_callee (callee_reg, dest_model);
+	return mgr->get_ptr_svalue (type, caller_reg);
       }
       break;
     case SK_CONSTANT:
@@ -329,7 +607,10 @@ summarization::map_svalue_from_callee (const svalue *callee_base_sval,
 	    /* An initial value of a region within an outcome means
 	       that value of that region at entry to the function.  */
 	    region_model *src_model = get_caller_state ().m_region_model;
-	    return src_model->get_store_value (callee_sval->get_region ());
+	    const region *outcome_reg = callee_sval->get_region ();
+	    const region *caller_reg
+	      = map_region_from_callee (outcome_reg, dest_model);
+	    return src_model->get_store_value (caller_reg);
 	    // FIXME: ctxt?
 	  }
       }
@@ -366,7 +647,26 @@ summarization::map_svalue_from_callee (const svalue *callee_base_sval,
       gcc_unreachable (); // FIXME
       break;
     case SK_COMPOUND:
-      gcc_unreachable (); // FIXME
+      {
+	const compound_svalue *callee_sval
+	  = static_cast<const compound_svalue *> (callee_base_sval);
+	const binding_map &outcome_map = callee_sval->get_map ();
+	binding_map caller_map;
+	for (compound_svalue::iterator_t iter = outcome_map.begin ();
+	     iter != outcome_map.end ();
+	     ++iter)
+	  {
+	    const binding_key *outcome_key = (*iter).first;
+	    const svalue *outcome_sval = (*iter).second;
+
+	    const svalue *caller_sval
+	      = map_svalue_from_callee (outcome_sval, dest_model);
+
+	    caller_map.put (outcome_key, caller_sval);
+	    // FIXME: do we need to remap the outcome_key?
+	  }
+	return mgr->get_or_create_compound_svalue (type, caller_map);
+      }
       break;
     case SK_CONJURED:
       return mgr->get_or_create_unknown_svalue (type);
@@ -376,10 +676,11 @@ summarization::map_svalue_from_callee (const svalue *callee_base_sval,
 /* FIXME.  */
 
 const region *
-summarization::map_region_from_callee (const region *callee_region,
-				       region_model */*dest_model*/)
+summarization::map_region_from_callee (const region *callee_reg,
+				       region_model *dest_model)
 {
-  switch (callee_region->get_kind ())
+  region_model_manager *mgr = get_manager ();
+  switch (callee_reg->get_kind ())
     {
     default:
       gcc_unreachable (); // FIXME
@@ -390,7 +691,7 @@ summarization::map_region_from_callee (const region *callee_region,
       break;
     case RK_GLOBALS:
     case RK_CODE:
-      return callee_region;
+      return callee_reg;
 
     case RK_FUNCTION:
       {
@@ -405,22 +706,32 @@ summarization::map_region_from_callee (const region *callee_region,
     case RK_STACK:
     case RK_HEAP:
     case RK_ROOT:
-      return callee_region;
+      return callee_reg;
 
     case RK_SYMBOLIC:
       {
-	gcc_unreachable (); // FIXME
+	const symbolic_region *symbolic_reg
+	  = as_a <const symbolic_region *> (callee_reg);
+	const svalue *caller_sval
+	  = map_svalue_from_callee (symbolic_reg->get_pointer (), dest_model);
+	return mgr->get_symbolic_region (caller_sval);
       }
       break;
     case RK_DECL:
       {
 	// FIXME: what if it's not a global?
-	return callee_region;
+	return callee_reg;
       }
       break;
     case RK_FIELD:
       {
-	gcc_unreachable (); // FIXME
+	const field_region *callee_field_reg
+	  = as_a <const field_region *> (callee_reg);
+	const region *caller_parent_reg
+	  = map_region_from_callee (callee_reg->get_parent_region (),
+				    dest_model);
+	return mgr->get_field_region (caller_parent_reg,
+				      callee_field_reg->get_field ());
       }
       break;
     case RK_ELEMENT:
@@ -441,14 +752,13 @@ summarization::map_region_from_callee (const region *callee_region,
     case RK_HEAP_ALLOCATED:
       {
 	// TODO: maintain a cached mapping of these
-	if (const region ** slot = m_region_map.get (callee_region))
+	if (const region ** slot = m_region_map.get (callee_reg))
 	  return *slot;
-	const region *caller_region
-	  = get_manager ()->create_region_for_heap_alloc ();
+	const region *caller_reg = mgr->create_region_for_heap_alloc ();
 	/* FIXME: eventually will want to propagate stored
 	   from callee_region.  */
-	m_region_map.put (callee_region, caller_region);
-	return caller_region;
+	m_region_map.put (callee_reg, caller_reg);
+	return caller_reg;
       }
       break;
     case RK_ALLOCA:
@@ -458,7 +768,7 @@ summarization::map_region_from_callee (const region *callee_region,
       break;
     case RK_STRING:
     case RK_UNKNOWN:
-      return callee_region;
+      return callee_reg;
     }
 }
 
@@ -556,7 +866,6 @@ call_outcome_return:: maybe_extend_egraph (summarization *cs) const
   program_state dest_state (cs->get_caller_state ());
 
   region_model *dest_model = dest_state.m_region_model;
-  store *dest_store = dest_model->get_store ();
 
   const program_state &outcome_state = m_enode->get_state ();
   region_model *outcome_model = m_enode->get_state ().m_region_model;
@@ -574,117 +883,8 @@ call_outcome_return:: maybe_extend_egraph (summarization *cs) const
       return;
     }
 
-  // TODO: apply store (e.g. updates to global variables)
-  {
-    for (store::cluster_map_t::iterator iter = outcome_store->begin ();
-	 iter != outcome_store->end (); ++iter)
-      {
-	const region *outcome_base_reg = (*iter).first;
-	/* Ignore bindings within the frame.  */
-	// FIXME: could be another frame
-	if (outcome_base_reg->maybe_get_frame_region ())
-	  continue;
-
-	binding_cluster *outcome_cluster = (*iter).second;
-
-	/* Update globals.  */
-	if (tree decl = outcome_base_reg->maybe_get_decl ())
-	  if (is_global_var (decl))
-	    {
-	      binding_cluster *dest_cluster
-		= dest_store->get_or_create_cluster (outcome_base_reg);
-	      dest_cluster->purge_region (cs->get_store_manager (),
-					  outcome_base_reg);
-
-	      for (binding_cluster::iterator_t iter
-		     = outcome_cluster->begin ();
-		   iter != outcome_cluster->end ();
-		   ++iter)
-		{
-		  const binding_key *key = (*iter).first;
-		  const svalue *outcome_sval = (*iter).second;
-		  const svalue *dest_sval
-		    = cs->map_svalue_from_callee (outcome_sval, dest_model);
-		  dest_cluster->bind_key (key, dest_sval);
-		}
-	      continue;
-	    }
-
-	/* Update symbolic regions.  */
-	if (const symbolic_region *sym_reg
-	      = outcome_base_reg->dyn_cast_symbolic_region ())
-	  {
-	    const svalue *outcome_ptr = sym_reg->get_pointer ();
-	    const svalue *dest_ptr
-	      = cs->map_svalue_from_callee (outcome_ptr, dest_model);
-	    for (binding_cluster::iterator_t iter
-		   = outcome_cluster->begin ();
-		 iter != outcome_cluster->end ();
-		 ++iter)
-	      {
-		const binding_key *key = (*iter).first;
-		const svalue *outcome_sval = (*iter).second;
-		const svalue *dest_sval
-		  = cs->map_svalue_from_callee (outcome_sval, dest_model);
-		//dest_cluster->bind_key (key, dest_sval);
-		//gcc_unreachable (); // FIXME
-	      }
-	    continue;
-	  }
-
-	if (outcome_base_reg->get_kind () == RK_HEAP_ALLOCATED)
-	  {
-	    const region *dest_base_reg
-	      = cs->map_region_from_callee (outcome_base_reg,
-					    dest_model);
-	    gcc_assert (dest_base_reg->base_region_p ());
-	    // FIXME: what if this isn't true?
-
-	    binding_cluster *dest_cluster
-	      = dest_store->get_or_create_cluster (dest_base_reg);
-#if 0
-	    dest_cluster->purge_region (cs->get_store_manager (),
-					dest_base_reg);
-#endif
-
-	    for (binding_cluster::iterator_t iter
-		   = outcome_cluster->begin ();
-		 iter != outcome_cluster->end ();
-		 ++iter)
-	      {
-		const binding_key *key = (*iter).first;
-		const svalue *outcome_sval = (*iter).second;
-		const svalue *dest_sval
-		  = cs->map_svalue_from_callee (outcome_sval, dest_model);
-		dest_cluster->bind_key (key, dest_sval);
-	      }
-	    continue;
-	    // FIXME: maybe combine with global var code above
-	  }
-
-	// FIXME: what about other base regions?
-	// FIXME: convert all of this to a switch stmt
-	gcc_unreachable (); // TODO
-
-	/* FIXME: what about m_escaped and m_touched?  */
-      }
-  }
-
-#if 0
-  /* Did the called function call an unknown function?
-     If so, handle it.  */
-  if (outcome_store->called_unknown_fn_p ())
-    {
-      /* FIXME: this creates conjured values for the call without
-	 specifyin that it's the call within the summary, so there
-	 could be false reuse of values.  */
-      /* FIXME: need to mark things as escaped for this to work, I think.
-	 region_model::handle_unrecognized_call normally does this for
-	 the direct call case.  */
-      dest_store->on_unknown_fncall (call_stmt, get_store_manager ());
-      gcc_unreachable (); // FIXME
-    }
-#endif
+  /* Apply store (e.g. updates to global variables).  */
+  cs->apply_store (outcome_store, dest_model);
 
   // TODO: apply sm-state
   {
@@ -800,13 +1000,17 @@ call_outcome_longjmp::maybe_extend_egraph (summarization *cs) const
 #endif
 
   program_state dest_state (cs->get_caller_state ()); // FIXME: update this
+#if 0
   dest_state.dump (cs->get_ext_state (), true); // FIXME
   m_state_at_longjmp.dump (cs->get_ext_state (), true); // FIXME
+#endif
 
   const svalue *actual_buf_content
     = cs->map_svalue_from_callee (m_buf_content_sval,
 				  dest_state.m_region_model);
+#if 0
   actual_buf_content->dump (true);
+#endif
 
   /* TODO:
      constraints
diff --git a/gcc/analyzer/call-summary.h b/gcc/analyzer/call-summary.h
index ddf8a971a19..3198248386d 100644
--- a/gcc/analyzer/call-summary.h
+++ b/gcc/analyzer/call-summary.h
@@ -71,6 +71,13 @@ public:
   bool apply_constraints (region_model *outcome_model,
 			  region_model *dest_model);
 
+  void apply_store (const store *outcome_store,
+		    region_model *dest_model);
+
+  void apply_binding_map (const binding_map &outcome_map,
+			  const svalue *dest_ptr,
+			  region_model *dest_model);
+
   const svalue *
   map_svalue_from_callee (const svalue *callee_sval,
 			  region_model *dest_model);
diff --git a/gcc/analyzer/region-model-manager.cc b/gcc/analyzer/region-model-manager.cc
index dfd2413e914..2de1c46023a 100644
--- a/gcc/analyzer/region-model-manager.cc
+++ b/gcc/analyzer/region-model-manager.cc
@@ -939,6 +939,15 @@ region_model_manager::get_frame_region (const frame_region *calling_frame,
 const region *
 region_model_manager::get_symbolic_region (const svalue *sval)
 {
+  /* Fold *(&REG) to REG.  */
+  if (const region_svalue *region_sval = sval->dyn_cast_region_svalue ())
+    {
+      const region *reg = region_sval->get_pointee ();
+      if (reg->get_type ()
+	  && TYPE_POINTER_TO (reg->get_type ()) == sval->get_type ())
+	return reg;
+    }
+
   symbolic_region::key_t key (&m_root_region, sval);
   if (symbolic_region *reg = m_symbolic_regions.get (key))
     return reg;
diff --git a/gcc/analyzer/store.h b/gcc/analyzer/store.h
index 1a10ccce7a2..d3e00008da1 100644
--- a/gcc/analyzer/store.h
+++ b/gcc/analyzer/store.h
@@ -605,7 +605,9 @@ private:
      This is to avoid having to mark (and thus explicitly track)
      every global when an unknown function is called; instead, they
      can be tracked implicitly.  */
+public: // FIXME
   bool m_called_unknown_fn;
+private: // FIXME
 };
 
 /* A class responsible for owning and consolidating binding keys
diff --git a/gcc/testsuite/gcc.dg/analyzer/call-summaries-aliasing-1.c b/gcc/testsuite/gcc.dg/analyzer/call-summaries-aliasing-1.c
new file mode 100644
index 00000000000..6e4e103ab74
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/call-summaries-aliasing-1.c
@@ -0,0 +1,40 @@
+#include "analyzer-decls.h"
+
+void __attribute__((noinline))
+called_by_test_1 (int *p, int *q, int i, int j)
+{
+  /* This program point should only be processed once;
+     summarization should do the rest.  */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+
+  *p = i;
+  *q = j;
+}
+
+void test_1 (void)
+{
+  int a, b;
+
+  called_by_test_1 (&a, &a, 0, 1);
+  __analyzer_eval (a == 1); /* { dg-warning "TRUE" } */
+
+  called_by_test_1 (&a, &b, 2, 3);
+  __analyzer_eval (a == 2); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+
+  __analyzer_eval (b == 3); /* { dg-warning "TRUE" } */
+
+  called_by_test_1 (&b, &b, 4, 5);
+  __analyzer_eval (b == 5); /* { dg-warning "TRUE" } */
+
+  called_by_test_1 (&b, &a, 6, 7);
+  __analyzer_eval (b == 6); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+  __analyzer_eval (a == 7); /* { dg-warning "TRUE" } */
+
+  /* FIXME: fix the xfails above.  */
+  /* FIXME: they may also depend on hash_map ordering,
+     depending on the order in which we unpack the outcome clusters into
+     the destination state.
+     FIXME: yes, it's nondeterministic.  */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/call-summaries-data-model-3.c b/gcc/testsuite/gcc.dg/analyzer/call-summaries-data-model-3.c
new file mode 100644
index 00000000000..1236dba7efa
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/call-summaries-data-model-3.c
@@ -0,0 +1,19 @@
+/* FIXME.  */
+
+#include "analyzer-decls.h"
+
+void
+called_by_test_1 (int *p, int a, int b)
+{
+  p[a] = b;
+}
+  
+void
+test_1 (int i, int j)
+{
+  int buf[1024];
+  called_by_test_1 (buf, i, j);
+  __analyzer_eval (buf[i] == j); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+  // TODO: xfail
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/call-summaries-data-model-4.c b/gcc/testsuite/gcc.dg/analyzer/call-summaries-data-model-4.c
new file mode 100644
index 00000000000..e62d193ffb9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/call-summaries-data-model-4.c
@@ -0,0 +1,38 @@
+/* Verify that we can correctly offset writes into a larger region
+   when processing a call summary.  */
+
+#include "analyzer-decls.h"
+
+struct s1
+{
+  int f1;
+  int f2; 
+};
+
+struct t1
+{
+  struct s1 g1;
+  struct s1 g2;
+};
+
+void
+called_by_test_1 (struct s1 *p, int a, int b)
+{
+  /* This program point should only be processed once;
+     summarization should do the rest.  */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+  p->f1 = a;
+  p->f2 = b;
+}
+  
+void
+test_1 (int i, int j, int k, int l)
+{
+  struct t1 t;
+  called_by_test_1 (&t.g1, i, j);
+  called_by_test_1 (&t.g2, k, l);
+  __analyzer_eval (t.g1.f1 == i); /* { dg-warning "TRUE" } */
+  __analyzer_eval (t.g1.f2 == j); /* { dg-warning "TRUE" } */
+  __analyzer_eval (t.g2.f1 == k); /* { dg-warning "TRUE" } */
+  __analyzer_eval (t.g2.f2 == l); /* { dg-warning "TRUE" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/call-summaries-malloc-6.c b/gcc/testsuite/gcc.dg/analyzer/call-summaries-malloc-6.c
new file mode 100644
index 00000000000..6250ddd952c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/call-summaries-malloc-6.c
@@ -0,0 +1,19 @@
+/* { dg-additional-options "-fanalyzer-call-summaries --param=analyzer-min-snodes-for-call-summary=0" } */
+
+#include <stdlib.h>
+#include "analyzer-decls.h"
+
+void  __attribute__((noinline))
+called_by_test_1 (void **p)
+{
+  *p = NULL;
+}
+
+void *
+test_1 (void)
+{
+  void *q = malloc (1024);
+  called_by_test_1 (&q);
+  return q; /* { dg-warning "leak" } */
+}
+/* FIXME: put the leak at a better location.  */
diff --git a/gcc/testsuite/gcc.dg/analyzer/call-summaries-noreturn-3.c b/gcc/testsuite/gcc.dg/analyzer/call-summaries-noreturn-3.c
new file mode 100644
index 00000000000..1a9c630590f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/call-summaries-noreturn-3.c
@@ -0,0 +1,30 @@
+#include "analyzer-decls.h"
+
+void __attribute__((noinline))
+ensure_equal (int a, int b)
+{
+  /* This program point should only be processed once;
+     summarization should do the rest.  */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+
+  if (a != b)
+    __builtin_abort ();
+}
+
+void test_1 (int i, int j)
+{
+  __analyzer_eval (i == j); /* { dg-warning "UNKNOWN" } */
+
+  ensure_equal (i, j);
+
+  __analyzer_eval (i == j); /* { dg-warning "TRUE" } */
+}
+
+void test_2 (int p, int q)
+{
+  __analyzer_eval (p == q); /* { dg-warning "UNKNOWN" } */
+
+  ensure_equal (p, q);
+
+  __analyzer_eval (p == q); /* { dg-warning "TRUE" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/call-summaries-return-expr-1.c b/gcc/testsuite/gcc.dg/analyzer/call-summaries-return-expr-1.c
index ffa24360bfc..5b6325f256e 100644
--- a/gcc/testsuite/gcc.dg/analyzer/call-summaries-return-expr-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/call-summaries-return-expr-1.c
@@ -43,3 +43,47 @@ void test_3 (int a, int b)
   __analyzer_eval (mult (4, 5) == 20);  /* { dg-warning "TRUE" } */
   __analyzer_eval (mult (a, b) == a * b);  /* { dg-warning "TRUE" } */
 }
+
+/* Summarization of a compound value.  */
+
+struct s4
+{
+  int f1;
+  int f2;
+};
+
+struct s4 __attribute__((noinline))
+called_by_test_4 (int i)
+{
+  struct s4 s;
+  s.f1 = 42;
+  s.f2 = i;
+  return s;
+}
+
+void test_4 (int j)
+{
+  struct s4 t = called_by_test_4 (j);
+  __analyzer_eval (t.f1 == 42);  /* { dg-warning "TRUE" } */  
+  __analyzer_eval (t.f2 == j);  /* { dg-warning "TRUE" } */  
+}
+
+/* Accessing a field by pointer.  */
+
+struct s5
+{
+  int f;
+};
+
+int __attribute__((noinline))
+called_by_test_5 (struct s5 *p)
+{
+  return p->f;
+}
+
+void test_5 (int i)
+{
+  struct s5 s;
+  s.f = i;
+  __analyzer_eval (called_by_test_5 (&s) == i); /* { dg-warning "TRUE" } */  
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/call-summaries-setjmp-1.c b/gcc/testsuite/gcc.dg/analyzer/call-summaries-setjmp-1.c
index e9abd7188b5..fa3724b4e23 100644
--- a/gcc/testsuite/gcc.dg/analyzer/call-summaries-setjmp-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/call-summaries-setjmp-1.c
@@ -1,6 +1,7 @@
 /* Verify that call summaries respect longjmp (PR analyzer/99391).  */
 
-/* { dg-additional-options "-fanalyzer-call-summaries --param=analyzer-min-snodes-for-call-summary=0" } */
+/* { dg-additional-options "-fanalyzer-call-summaries --param=analyzer-min-snodes-for-call-summary=0 -fno-analyzer-feasibility" } */
+// FIXME: need to properly handle feasibility 
 
 #include "test-setjmp.h"
 #include "analyzer-decls.h"
@@ -22,5 +23,6 @@ void outer (void)
 
 static void inner ()
 {
-  longjmp (env, 42); /* { dg-message "rewinding from 'longjmp' in 'inner'" } */
+  longjmp (env, 42); /* { dg-message "rewinding from 'longjmp' in 'inner'" "" { xfail *-*-*} } */
+  // FIXME: need to properly handle events/use correct edge info
 }
diff --git a/gcc/testsuite/gcc.dg/analyzer/call-summaries-stale-frame-1.c b/gcc/testsuite/gcc.dg/analyzer/call-summaries-stale-frame-1.c
new file mode 100644
index 00000000000..04221479bf9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/call-summaries-stale-frame-1.c
@@ -0,0 +1,15 @@
+
+int *global_ptr;
+
+static void __attribute__((noinline))
+called_by_test_1 (void)
+{
+  int i = 42;
+  global_ptr = &i;  
+}
+
+int test_1 (void)
+{
+  called_by_test_1 ();
+  return *global_ptr; /* { dg-warning "dereferencing pointer 'global_ptr' to within stale stack frame" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/call-summaries-unknown-fn-1.c b/gcc/testsuite/gcc.dg/analyzer/call-summaries-unknown-fn-1.c
index f44ab926cad..08c136ae1fb 100644
--- a/gcc/testsuite/gcc.dg/analyzer/call-summaries-unknown-fn-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/call-summaries-unknown-fn-1.c
@@ -4,14 +4,18 @@
 
 extern int unknown_fn (void *);
 
+int g = 17;
+
 int __attribute__((noinline))
 calls_unknown_fn (void *p)
 {
   return unknown_fn (p);
 }
 
-void test_1 (void)
+int main (void)
 {
+  __analyzer_eval (g == 17); /* { dg-warning "TRUE" } */
+
   int i = 42;
   int j = calls_unknown_fn (&i);
   __analyzer_eval (i == 42); /* { dg-warning "UNKNOWN" "" { xfail *-*-* } } */ // TODO
@@ -19,4 +23,6 @@ void test_1 (void)
 
   //__analyzer_describe (0, j); /* { d-not-g-warning "UNKNOWN" } */
   //__analyzer_dump ();
+
+  __analyzer_eval (g == 17); /* { dg-warning "UNKNOWN" } */
 }
-- 
2.26.2

