From f0b934bd93be75c3b86adc8cc9c3842ab8b41574 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 9 Jul 2020 11:27:10 -0400
Subject: [PATCH 294/315] FIXME: add m_called_unknown_fn, fixing
 unknown-fns.c:test_2

---
 gcc/analyzer/region-model2.cc | 14 ++++++++++++++
 gcc/analyzer/region-model2.h  |  8 +++++++-
 gcc/analyzer/store2.cc        | 17 ++++++++++++++++-
 gcc/analyzer/store2.h         | 10 ++++++++++
 4 files changed, 47 insertions(+), 2 deletions(-)

diff --git a/gcc/analyzer/region-model2.cc b/gcc/analyzer/region-model2.cc
index 47682e31cac..f6be72983f0 100644
--- a/gcc/analyzer/region-model2.cc
+++ b/gcc/analyzer/region-model2.cc
@@ -5794,6 +5794,18 @@ region_model2::get_store_value (const region2 *reg)
 	  return m_mgr->get_or_create_cast (reg->get_type (), char_sval);
       }
 
+  /* Otherwise we implicitly have the initial value of the region.  */
+
+  /* Special-case: to avoid having to explicitly update all previously
+     untracked globals when calling an unknown fn, we instead change
+     the default here so we implicitly have an unknown value for such
+     regions.  */
+  // FIXME: what about symbolic writes also?
+  if (m_store.called_unknown_fn_p ())
+    if (reg->get_base_region ()->get_parent_region ()->get_kind ()
+	== region2::RK_GLOBALS)
+      return m_mgr->get_or_create_unknown_svalue2 (reg->get_type ());
+
   /* FIXME: not if touched by symbolic value; handled in
      binding_cluster2::get_any_binding.  */
   return m_mgr->get_or_create_initial_value (reg);
@@ -8247,11 +8259,13 @@ test_dump ()
 
   ASSERT_DUMP_EQ (model, false,
 		  "stack depth: 0\n"
+		  "m_called_unknown_fn: FALSE\n"
 		  "constraint_manager2:\n"
 		  "  equiv classes:\n"
 		  "  constraints:\n");
   ASSERT_DUMP_EQ (model, true,
 		  "stack depth: 0\n"
+		  "m_called_unknown_fn: FALSE\n"
 		  "constraint_manager2:\n"
 		  "  equiv classes:\n"
 		  "  constraints:\n");
diff --git a/gcc/analyzer/region-model2.h b/gcc/analyzer/region-model2.h
index fd5e3f66262..8abf888d7f5 100644
--- a/gcc/analyzer/region-model2.h
+++ b/gcc/analyzer/region-model2.h
@@ -505,7 +505,13 @@ template <> struct default_hash_traits<setjmp_svalue2::key_t>
 namespace ana {
 
 /* Concrete subclass of svalue2 representing the initial value of a
-   specific region  */
+   specific region.
+
+   FIXME: trying this approach:
+   This represents the initial value at the start of the analysis path,
+   as opposed to the first time the region is accessed during the path.
+   Hence as soon as we have a call to an unknown function, all previously
+   unmodelled globals become implicitly "unknown" rathen than "initial".  */
 
 class initial_svalue2 : public svalue2
 {
diff --git a/gcc/analyzer/store2.cc b/gcc/analyzer/store2.cc
index 75128e4bdc8..bbbbd71bc0b 100644
--- a/gcc/analyzer/store2.cc
+++ b/gcc/analyzer/store2.cc
@@ -1001,12 +1001,14 @@ store2_manager::get_symbolic_binding (const region2 *reg,
 /* FIXME.  */
 
 store2::store2 ()
+: m_called_unknown_fn (false)
 {
 }
 
 /* store2's copy ctor.  */
 
 store2::store2 (const store2 &other)
+: m_called_unknown_fn (other.m_called_unknown_fn)
 {
   /* Deep copy.  TODO: replace with functional approach using immutable_map.  */
   for (cluster_map_t::iterator iter = other.m_cluster_map.begin ();
@@ -1043,6 +1045,8 @@ store2::operator= (const store2 &other)
     delete (*iter).second;
   m_cluster_map.empty ();
 
+  m_called_unknown_fn = other.m_called_unknown_fn;
+
   /* Deep copy.  TODO: replace with functional approach using immutable_map.  */
   for (cluster_map_t::iterator iter = other.m_cluster_map.begin ();
        iter != other.m_cluster_map.end ();
@@ -1062,6 +1066,9 @@ store2::operator= (const store2 &other)
 bool
 store2::operator== (const store2 &other) const
 {
+  if (m_called_unknown_fn != other.m_called_unknown_fn)
+    return false;
+
   if (m_cluster_map.elements () != other.m_cluster_map.elements ())
     return false;
 
@@ -1205,6 +1212,10 @@ store2::dump_to_pp (pretty_printer *pp, bool simple, bool multiline,
       if (!multiline)
 	pp_string (pp, "}");
     }
+  pp_printf (pp, "m_called_unknown_fn: %s",
+	     m_called_unknown_fn ? "TRUE" : "FALSE");
+  if (multiline)
+    pp_newline (pp);
 }
 
 /* Dump a multiline representation of this store2 to stderr.  */
@@ -1597,7 +1608,9 @@ store2::mark_as_escaped (const region2 *base_reg)
   binding_cluster2 *cluster = get_or_create_cluster (base_reg);
   cluster->mark_as_escaped ();
   /* FIXME: but don't bother marking every global as escaped; instead,
-     clear these on unknown fncalls.  */
+     m_called_unknown_fn is set on these when on unknown fncalls,
+     changeing the implicit value on globals from "initial"
+     to "unknown".  */
 }
 
 /* Handle an unknown fncall by updating any clusters that have escaped
@@ -1606,6 +1619,8 @@ store2::mark_as_escaped (const region2 *base_reg)
 void
 store2::on_unknown_fncall ()
 {
+  m_called_unknown_fn = true;
+
   for (cluster_map_t::iterator iter = m_cluster_map.begin ();
        iter != m_cluster_map.end (); ++iter)
     (*iter).second->on_unknown_fncall ();
diff --git a/gcc/analyzer/store2.h b/gcc/analyzer/store2.h
index f1dcb6cc74a..bf27df97d59 100644
--- a/gcc/analyzer/store2.h
+++ b/gcc/analyzer/store2.h
@@ -473,6 +473,8 @@ public:
   const svalue2 *get_default_binding (store2_manager *mgr, const region2 *reg);
   const svalue2 *get_any_binding (store2_manager *mgr, const region2 *reg);
 
+  bool called_unknown_fn_p () const { return m_called_unknown_fn; }
+
   void set_value (store2_manager *mgr, const region2 *lhs_reg,
 		  const svalue2 *rhs_sval, enum binding_kind kind);
   void clobber_region (store2_manager *mgr, const region2 *reg);
@@ -525,6 +527,14 @@ private:
   void remove_overlapping_bindings (store2_manager *mgr, const region2 *reg);
 
   cluster_map_t m_cluster_map; // TODO: deep copy of this!
+
+  /* If this is true, then unknown code has been called, and so
+     any global variable that isn't currently modelled by the store
+     has unknown state, rather than being in an "initial state".
+     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.  */
+  bool m_called_unknown_fn;
 };
 
 class store2_manager
-- 
2.26.2

