PR analyzer/110112 notes that -fanalyzer is extremely slow on a source
file with large read-only static arrays, repeatedly building the
same compound_svalue representing the full initializer, and repeatedly
building svalues representing parts of the the full initialiazer.

This patch adds caches for both of these; together they reduce the time
taken by -fanalyzer -O2 on the testcase in the bug for an optimized
build:
  91.2s : no caches (status quo)
  32.4s : cache in decl_region::get_svalue_for_constructor
   3.7s : cache in region::get_initial_value_at_main
   3.1s : both caches (this patch)

gcc/analyzer/ChangeLog:
        PR analyzer/110112
        * region-model.cc (region_model::get_initial_value_for_global):
        Move code to region::calc_initial_value_at_main.
        * region.cc (region::get_initial_value_at_main): New function.
        (region::calc_initial_value_at_main): New function, based on code
        in region_model::get_initial_value_for_global.
        (region::region): Initialize m_cached_init_sval_at_main.
        (decl_region::get_svalue_for_constructor): Add a cache, splitting
        out body to...
        (decl_region::calc_svalue_for_constructor): ...this new function.
        * region.h (region::get_initial_value_at_main): New decl.
        (region::calc_initial_value_at_main): New decl.
        (region::m_cached_init_sval_at_main): New field.
        (decl_region::decl_region): Initialize m_ctor_svalue.
        (decl_region::calc_svalue_for_constructor): New decl.
        (decl_region::m_ctor_svalue): New field.

(cherry picked from commit r14-1664-gfe9771b59f576f)

Signed-off-by: David Malcolm <dmalc...@redhat.com>
---
 gcc/analyzer/region-model.cc | 25 +------------
 gcc/analyzer/region.cc       | 71 +++++++++++++++++++++++++++++++++---
 gcc/analyzer/region.h        | 14 ++++++-
 3 files changed, 79 insertions(+), 31 deletions(-)

diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index fb81d43f91b..18996c5e5e8 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -2355,30 +2355,7 @@ region_model::get_initial_value_for_global (const region 
*reg) const
      the initial value of REG can be taken from the initialization value
      of the decl.  */
   if (called_from_main_p () || TREE_READONLY (decl))
-    {
-      /* Attempt to get the initializer value for base_reg.  */
-      if (const svalue *base_reg_init
-           = base_reg->get_svalue_for_initializer (m_mgr))
-       {
-         if (reg == base_reg)
-           return base_reg_init;
-         else
-           {
-             /* Get the value for REG within base_reg_init.  */
-             binding_cluster c (base_reg);
-             c.bind (m_mgr->get_store_manager (), base_reg, base_reg_init);
-             const svalue *sval
-               = c.get_any_binding (m_mgr->get_store_manager (), reg);
-             if (sval)
-               {
-                 if (reg->get_type ())
-                   sval = m_mgr->get_or_create_cast (reg->get_type (),
-                                                     sval);
-                 return sval;
-               }
-           }
-       }
-    }
+    return reg->get_initial_value_at_main (m_mgr);
 
   /* Otherwise, return INIT_VAL(REG).  */
   return m_mgr->get_or_create_initial_value (reg);
diff --git a/gcc/analyzer/region.cc b/gcc/analyzer/region.cc
index a18bfa50d09..f982ce67509 100644
--- a/gcc/analyzer/region.cc
+++ b/gcc/analyzer/region.cc
@@ -272,6 +272,51 @@ region::can_have_initial_svalue_p () const
     }
 }
 
+/* For regions within a global decl, get the svalue for the initial
+   value of this region when the program starts, caching the result.  */
+
+const svalue *
+region::get_initial_value_at_main (region_model_manager *mgr) const
+{
+  if (!m_cached_init_sval_at_main)
+    m_cached_init_sval_at_main = calc_initial_value_at_main (mgr);
+  return m_cached_init_sval_at_main;
+}
+
+/* Implementation of region::get_initial_value_at_main.  */
+
+const svalue *
+region::calc_initial_value_at_main (region_model_manager *mgr) const
+{
+  const decl_region *base_reg = get_base_region ()->dyn_cast_decl_region ();
+  gcc_assert (base_reg);
+
+  /* Attempt to get the initializer value for base_reg.  */
+  if (const svalue *base_reg_init
+      = base_reg->get_svalue_for_initializer (mgr))
+    {
+      if (this == base_reg)
+       return base_reg_init;
+      else
+       {
+         /* Get the value for REG within base_reg_init.  */
+         binding_cluster c (base_reg);
+         c.bind (mgr->get_store_manager (), base_reg, base_reg_init);
+         const svalue *sval
+           = c.get_any_binding (mgr->get_store_manager (), this);
+         if (sval)
+           {
+             if (get_type ())
+               sval = mgr->get_or_create_cast (get_type (), sval);
+             return sval;
+           }
+       }
+    }
+
+  /* Otherwise, return INIT_VAL(REG).  */
+  return mgr->get_or_create_initial_value (this);
+}
+
 /* If this region is a decl_region, return the decl.
    Otherwise return NULL.  */
 
@@ -701,7 +746,7 @@ region::is_named_decl_p (const char *decl_name) const
 
 region::region (complexity c, unsigned id, const region *parent, tree type)
 : m_complexity (c), m_id (id), m_parent (parent), m_type (type),
-  m_cached_offset (NULL)
+  m_cached_offset (NULL), m_cached_init_sval_at_main (NULL)
 {
   gcc_assert (type == NULL_TREE || TYPE_P (type));
 }
@@ -1170,14 +1215,13 @@ decl_region::maybe_get_constant_value 
(region_model_manager *mgr) const
   return NULL;
 }
 
-/* Get an svalue for CTOR, a CONSTRUCTOR for this region's decl.  */
+/* Implementation of decl_region::get_svalue_for_constructor
+   for when the cached value hasn't yet been calculated.  */
 
 const svalue *
-decl_region::get_svalue_for_constructor (tree ctor,
-                                        region_model_manager *mgr) const
+decl_region::calc_svalue_for_constructor (tree ctor,
+                                         region_model_manager *mgr) const
 {
-  gcc_assert (!TREE_CLOBBER_P (ctor));
-
   /* Create a binding map, applying ctor to it, using this
      decl_region as the base region when building child regions
      for offset calculations.  */
@@ -1189,6 +1233,21 @@ decl_region::get_svalue_for_constructor (tree ctor,
   return mgr->get_or_create_compound_svalue (get_type (), map);
 }
 
+/* Get an svalue for CTOR, a CONSTRUCTOR for this region's decl.  */
+
+const svalue *
+decl_region::get_svalue_for_constructor (tree ctor,
+                                        region_model_manager *mgr) const
+{
+  gcc_assert (!TREE_CLOBBER_P (ctor));
+  gcc_assert (ctor == DECL_INITIAL (m_decl));
+
+  if (!m_ctor_svalue)
+    m_ctor_svalue = calc_svalue_for_constructor (ctor, mgr);
+
+  return m_ctor_svalue;
+}
+
 /* For use on decl_regions for global variables.
 
    Get an svalue for the initial value of this region at entry to
diff --git a/gcc/analyzer/region.h b/gcc/analyzer/region.h
index 5df0ae7487b..270e5042eeb 100644
--- a/gcc/analyzer/region.h
+++ b/gcc/analyzer/region.h
@@ -161,6 +161,7 @@ public:
   const frame_region *maybe_get_frame_region () const;
   enum memory_space get_memory_space () const;
   bool can_have_initial_svalue_p () const;
+  const svalue *get_initial_value_at_main (region_model_manager *mgr) const;
 
   tree maybe_get_decl () const;
 
@@ -240,6 +241,7 @@ public:
 
  private:
   region_offset calc_offset (region_model_manager *mgr) const;
+  const svalue *calc_initial_value_at_main (region_model_manager *mgr) const;
 
   complexity m_complexity;
   unsigned m_id; // purely for deterministic sorting at this stage, for dumps
@@ -247,6 +249,10 @@ public:
   tree m_type;
 
   mutable region_offset *m_cached_offset;
+
+  /* For regions within a global decl, a cache of the svalue for the initial
+     value of this region when the program starts.  */
+  mutable const svalue *m_cached_init_sval_at_main;
 };
 
 } // namespace ana
@@ -696,7 +702,8 @@ class decl_region : public region
 public:
   decl_region (unsigned id, const region *parent, tree decl)
   : region (complexity (parent), id, parent, TREE_TYPE (decl)), m_decl (decl),
-    m_tracked (calc_tracked_p (decl))
+    m_tracked (calc_tracked_p (decl)),
+    m_ctor_svalue (NULL)
   {}
 
   enum region_kind get_kind () const final override { return RK_DECL; }
@@ -716,6 +723,8 @@ public:
   const svalue *get_svalue_for_initializer (region_model_manager *mgr) const;
 
 private:
+  const svalue *calc_svalue_for_constructor (tree ctor,
+                                            region_model_manager *mgr) const;
   static bool calc_tracked_p (tree decl);
 
   tree m_decl;
@@ -725,6 +734,9 @@ private:
      store objects).
      This can be debugged using -fdump-analyzer-untracked.  */
   bool m_tracked;
+
+  /* Cached result of get_svalue_for_constructor.  */
+  mutable const svalue *m_ctor_svalue;
 };
 
 } // namespace ana
-- 
2.26.3

Reply via email to