LCOV - code coverage report
Current view: top level - gcc/analyzer - engine.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 83.3 % 2263 1886
Test Date: 2026-05-30 15:37:04 Functions: 83.5 % 164 137
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* The analysis "engine".
       2              :    Copyright (C) 2019-2026 Free Software Foundation, Inc.
       3              :    Contributed by David Malcolm <dmalcolm@redhat.com>.
       4              : 
       5              : This file is part of GCC.
       6              : 
       7              : GCC is free software; you can redistribute it and/or modify it
       8              : under the terms of the GNU General Public License as published by
       9              : the Free Software Foundation; either version 3, or (at your option)
      10              : any later version.
      11              : 
      12              : GCC is distributed in the hope that it will be useful, but
      13              : WITHOUT ANY WARRANTY; without even the implied warranty of
      14              : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15              : General Public License for more details.
      16              : 
      17              : You should have received a copy of the GNU General Public License
      18              : along with GCC; see the file COPYING3.  If not see
      19              : <http://www.gnu.org/licenses/>.  */
      20              : 
      21              : #include "analyzer/common.h"
      22              : 
      23              : #include <zlib.h>
      24              : 
      25              : #include "cfg.h"
      26              : #include "gcc-rich-location.h"
      27              : #include "gimple-iterator.h"
      28              : #include "gimple-pretty-print.h"
      29              : #include "cgraph.h"
      30              : #include "fold-const.h"
      31              : #include "digraph.h"
      32              : #include "plugin.h"
      33              : #include "target.h"
      34              : #include "stringpool.h"
      35              : #include "attribs.h"
      36              : #include "tree-dfa.h"
      37              : #include "gimple-predict.h"
      38              : #include "context.h"
      39              : #include "channels.h"
      40              : #include "pretty-print-markup.h"
      41              : 
      42              : #include "text-art/dump.h"
      43              : 
      44              : #include "analyzer/analyzer-logging.h"
      45              : #include "analyzer/call-string.h"
      46              : #include "analyzer/program-point.h"
      47              : #include "analyzer/store.h"
      48              : #include "analyzer/region-model.h"
      49              : #include "analyzer/constraint-manager.h"
      50              : #include "analyzer/sm.h"
      51              : #include "analyzer/pending-diagnostic.h"
      52              : #include "analyzer/diagnostic-manager.h"
      53              : #include "analyzer/supergraph.h"
      54              : #include "analyzer/program-state.h"
      55              : #include "analyzer/exploded-graph.h"
      56              : #include "analyzer/exploded-path.h"
      57              : #include "analyzer/analysis-plan.h"
      58              : #include "analyzer/checker-path.h"
      59              : #include "analyzer/state-purge.h"
      60              : #include "analyzer/bar-chart.h"
      61              : #include "analyzer/call-info.h"
      62              : #include "analyzer/known-function-manager.h"
      63              : #include "analyzer/call-summary.h"
      64              : #include "analyzer/impl-sm-context.h"
      65              : 
      66              : /* For an overview, see gcc/doc/analyzer.texi.  */
      67              : 
      68              : #if ENABLE_ANALYZER
      69              : 
      70              : namespace ana {
      71              : 
      72              : /* class impl_region_model_context : public region_model_context.  */
      73              : 
      74      1401863 : impl_region_model_context::
      75              : impl_region_model_context (exploded_graph &eg,
      76              :                            exploded_node *enode_for_diag,
      77              :                            const program_state *old_state,
      78              :                            program_state *new_state,
      79              :                            uncertainty_t *uncertainty,
      80              :                            path_context *path_ctxt,
      81              :                            const gimple *stmt,
      82      1401863 :                            bool *out_could_have_done_work)
      83      1401863 : : m_eg (&eg), m_logger (eg.get_logger ()),
      84      1401863 :   m_enode_for_diag (enode_for_diag),
      85      1401863 :   m_old_state (old_state),
      86      1401863 :   m_new_state (new_state),
      87      1401863 :   m_stmt (stmt),
      88      1401863 :   m_ext_state (eg.get_ext_state ()),
      89      1401863 :   m_uncertainty (uncertainty),
      90      1401863 :   m_path_ctxt (path_ctxt),
      91      1401863 :   m_out_could_have_done_work (out_could_have_done_work)
      92              : {
      93      1401863 : }
      94              : 
      95            4 : impl_region_model_context::
      96              : impl_region_model_context (program_state *state,
      97              :                            const extrinsic_state &ext_state,
      98              :                            uncertainty_t *uncertainty,
      99            4 :                            logger *logger)
     100            4 : : m_eg (nullptr), m_logger (logger), m_enode_for_diag (nullptr),
     101            4 :   m_old_state (nullptr),
     102            4 :   m_new_state (state),
     103            4 :   m_stmt (nullptr),
     104            4 :   m_ext_state (ext_state),
     105            4 :   m_uncertainty (uncertainty),
     106            4 :   m_path_ctxt (nullptr),
     107            4 :   m_out_could_have_done_work (nullptr)
     108              : {
     109            4 : }
     110              : 
     111              : bool
     112         3209 : impl_region_model_context::warn_at (std::unique_ptr<pending_diagnostic> d,
     113              :                                     pending_location &&ploc)
     114              : {
     115         3209 :   LOG_FUNC (get_logger ());
     116         3209 :   if (m_eg)
     117              :     {
     118         3209 :       bool terminate_path = d->terminate_path_p ();
     119         3209 :       if (m_eg->get_diagnostic_manager ().add_diagnostic (std::move (ploc),
     120              :                                                           std::move (d)))
     121              :         {
     122         3170 :           if (m_path_ctxt
     123         2256 :               && terminate_path
     124          871 :               && flag_analyzer_suppress_followups)
     125          666 :             m_path_ctxt->terminate_path ();
     126         3170 :           return true;
     127              :         }
     128              :     }
     129              :   return false;
     130         3209 : }
     131              : 
     132              : void
     133          199 : impl_region_model_context::add_note (std::unique_ptr<pending_note> pn)
     134              : {
     135          199 :   LOG_FUNC (get_logger ());
     136          199 :   if (m_eg)
     137          199 :     m_eg->get_diagnostic_manager ().add_note (std::move (pn));
     138          199 : }
     139              : 
     140              : void
     141          164 : impl_region_model_context::add_event (std::unique_ptr<checker_event> event)
     142              : {
     143          164 :   LOG_FUNC (get_logger ());
     144          164 :   if (m_eg)
     145          164 :     m_eg->get_diagnostic_manager ().add_event (std::move (event));
     146          164 : }
     147              : 
     148              : void
     149        88715 : impl_region_model_context::on_svalue_leak (const svalue *sval)
     150              : 
     151              : {
     152       886825 :   for (sm_state_map *smap : m_new_state->m_checker_states)
     153       620680 :     smap->on_svalue_leak (sval, this);
     154        88715 : }
     155              : 
     156              : void
     157       506248 : impl_region_model_context::
     158              : on_liveness_change (const svalue_set &live_svalues,
     159              :                     const region_model *model)
     160              : {
     161      5060319 :   for (sm_state_map *smap : m_new_state->m_checker_states)
     162      3541575 :     smap->on_liveness_change (live_svalues, model, m_ext_state, this);
     163       506248 : }
     164              : 
     165              : void
     166        52343 : impl_region_model_context::on_unknown_change (const svalue *sval,
     167              :                                               bool is_mutable)
     168              : {
     169        52343 :   if (!sval->can_have_associated_state_p ())
     170              :     return;
     171       424805 :   for (sm_state_map *smap : m_new_state->m_checker_states)
     172       297338 :     smap->on_unknown_change (sval, is_mutable, m_ext_state);
     173              : }
     174              : 
     175              : void
     176          298 : impl_region_model_context::on_escaped_function (tree fndecl)
     177              : {
     178          298 :   m_eg->on_escaped_function (fndecl);
     179          298 : }
     180              : 
     181              : uncertainty_t *
     182      4295211 : impl_region_model_context::get_uncertainty ()
     183              : {
     184      4295211 :   return m_uncertainty;
     185              : }
     186              : 
     187              : /* Purge state involving SVAL.  The region_model has already been purged,
     188              :    so we only need to purge other state in the program_state:
     189              :    the sm-state.  */
     190              : 
     191              : void
     192        15923 : impl_region_model_context::purge_state_involving (const svalue *sval)
     193              : {
     194        15923 :   int i;
     195        15923 :   sm_state_map *smap;
     196       127384 :   FOR_EACH_VEC_ELT (m_new_state->m_checker_states, i, smap)
     197       111461 :     smap->purge_state_involving (sval, m_ext_state);
     198        15923 : }
     199              : 
     200              : void
     201         8602 : impl_region_model_context::bifurcate (std::unique_ptr<custom_edge_info> info)
     202              : {
     203         8602 :   if (m_path_ctxt)
     204         8602 :     m_path_ctxt->bifurcate (std::move (info));
     205         8602 : }
     206              : 
     207              : void
     208         1360 : impl_region_model_context::terminate_path ()
     209              : {
     210         1360 :   if (m_path_ctxt)
     211         1323 :     return m_path_ctxt->terminate_path ();
     212              : }
     213              : 
     214              : bool
     215       756424 : impl_region_model_context::
     216              : get_state_map_by_name (const char *name,
     217              :                        sm_state_map **out_smap,
     218              :                        const state_machine **out_sm,
     219              :                        unsigned *out_sm_idx,
     220              :                        std::unique_ptr<sm_context> *out_sm_context)
     221              : {
     222       756424 :   if (!m_new_state)
     223              :     return false;
     224              : 
     225       753413 :   unsigned sm_idx;
     226       753413 :   if (!m_ext_state.get_sm_idx_by_name (name, &sm_idx))
     227              :     return false;
     228              : 
     229       752946 :   const state_machine *sm = &m_ext_state.get_sm (sm_idx);
     230       752946 :   sm_state_map *new_smap = m_new_state->m_checker_states[sm_idx];
     231              : 
     232       752946 :   *out_smap = new_smap;
     233       752946 :   *out_sm = sm;
     234       752946 :   if (out_sm_idx)
     235       752128 :     *out_sm_idx = sm_idx;
     236       752946 :   if (out_sm_context)
     237              :     {
     238          748 :       const sm_state_map *old_smap = m_old_state->m_checker_states[sm_idx];
     239          748 :       *out_sm_context
     240          748 :         = std::make_unique<impl_sm_context> (*m_eg,
     241              :                                              sm_idx,
     242              :                                              *sm,
     243          748 :                                              m_enode_for_diag,
     244          748 :                                              m_old_state,
     245          748 :                                              m_new_state,
     246              :                                              old_smap,
     247              :                                              new_smap,
     248          748 :                                              m_path_ctxt,
     249         1496 :                                              false);
     250              :     }
     251              :   return true;
     252              : }
     253              : 
     254              : /* Subclass of pending_location::fixer_for_epath for finding the best stmt
     255              :    to report the leak at, given the emission path.  */
     256              : 
     257              : class leak_ploc_fixer_for_epath : public pending_location::fixer_for_epath
     258              : {
     259              : public:
     260         1310 :   leak_ploc_fixer_for_epath (const exploded_graph &eg, tree var)
     261         1310 :   : m_eg (eg), m_var (var)
     262              :   {}
     263              : 
     264              :   void
     265         1194 :   fixup_for_epath (const exploded_path &epath,
     266              :                    pending_location &ploc) const final override
     267              :   {
     268         1194 :     logger * const logger = m_eg.get_logger ();
     269         1194 :     LOG_FUNC (logger);
     270              : 
     271              :     /* Handle the interprocedural case where we leak the retval at a return
     272              :        because the caller discards the return value.  */
     273         1194 :     if (m_var
     274          974 :         && TREE_CODE (m_var) == RESULT_DECL)
     275              :       {
     276            6 :         auto &point = ploc.m_enode->get_point ();
     277           12 :         if (point.get_stack_depth () > 1)
     278            6 :           if (point.get_supernode ()->exit_p ())
     279              :             {
     280              :               /* Get the program_point for the call within the caller.  */
     281            6 :               auto &cs = point.get_call_string ();
     282            6 :               auto caller_snode = cs.get_return_node_in_caller ();
     283            6 :               gcc_assert (caller_snode);
     284            6 :               program_point caller_point (caller_snode, *cs.get_parent ());
     285            6 :               ploc.m_event_loc_info = event_loc_info (caller_point);
     286            6 :               return;
     287              :             }
     288              :       }
     289              : 
     290              :     /* Handle the case where we have e.g.:
     291              :        |   var = malloc (N);
     292              :        |   var = NULL;
     293              :        which with SSA becomes e.g.:
     294              :        |   var_0 = malloc (N);
     295              :        |   var_1 = nullptr;
     296              :        and thus leads to the leak being found at the enode where "var_0" goes
     297              :        out of scope.
     298              :        Fix up the location of the leak to report it at the write of NULL.  */
     299         1188 :     if (m_var && TREE_CODE (m_var) == SSA_NAME)
     300              :       {
     301          776 :         log_scope sentinel (logger, "looking for write to sibling SSA name");
     302              :         /* Locate the final write to this SSA name in the path.  */
     303          776 :         const gimple *def_stmt = SSA_NAME_DEF_STMT (m_var);
     304              : 
     305          776 :         int idx_of_def_stmt;
     306          776 :         if (epath.find_stmt_backwards (def_stmt, &idx_of_def_stmt))
     307              :           {
     308              :             /* What was the next write to the underlying var
     309              :                after the SSA name was set? (if any).  */
     310              : 
     311          668 :             for (unsigned idx = idx_of_def_stmt + 1;
     312         7560 :                  idx < epath.m_elements.size ();
     313              :                  ++idx)
     314              :               {
     315         6894 :                 const exploded_edge *eedge = epath.m_elements[idx].m_eedge;
     316         6894 :                 if (logger)
     317            0 :                   logger->log ("eedge[%i]: EN %i -> EN %i",
     318              :                                idx,
     319            0 :                                eedge->m_src->m_index,
     320            0 :                                eedge->m_dest->m_index);
     321         6894 :                 const gimple *stmt = eedge->maybe_get_stmt ();
     322         6894 :                 if (!stmt)
     323         2142 :                   continue;
     324         8660 :                 if (const gassign *assign = dyn_cast <const gassign *> (stmt))
     325              :                   {
     326         1768 :                     tree lhs = gimple_assign_lhs (assign);
     327         1768 :                     if (TREE_CODE (lhs) == SSA_NAME
     328         2938 :                         && SSA_NAME_VAR (lhs) == SSA_NAME_VAR (m_var))
     329              :                       {
     330            2 :                         if (logger)
     331            0 :                           logger->log ("using location 0%lx from gassign",
     332            0 :                                        assign->location);
     333            2 :                         ploc.m_event_loc_info.m_loc = assign->location;
     334            2 :                         return;
     335              :                       }
     336              :                   }
     337              :               }
     338              :           }
     339          776 :       }
     340              : 
     341              :     /* If the epath ends at a function exit node, the location is at
     342              :        the final "}".  Try walking backward along EPATH, looking for a
     343              :        the first suitable stmt with a better location.  */
     344         1186 :     gcc_assert (ploc.m_enode->get_supernode ());
     345         1186 :     const greturn *return_stmt = nullptr;
     346         1186 :     if (ploc.m_enode->get_supernode ()->exit_p ()
     347         1186 :         && has_return_stmt_p (epath, return_stmt, logger))
     348              :       {
     349              :         /* If we have "return SSA_NAME;" on EPATH, keep track of the
     350              :            pertinent SSA name as we walk backwards through EPATH.  */
     351          728 :         tree retval = NULL_TREE;
     352          728 :         if (return_stmt)
     353          728 :           retval = gimple_return_retval (return_stmt);
     354              : 
     355          728 :         log_scope sentinel (logger, "walking backward along epath");
     356         3022 :         for (int idx = epath.m_elements.size () - 1; idx >= 0; --idx)
     357              :           {
     358         2950 :             const exploded_path::element_t &element = epath.m_elements[idx];
     359         2950 :             const exploded_edge *eedge = element.m_eedge;
     360         2950 :             if (logger)
     361              :               {
     362            0 :                 logger->log ("eedge[%i]: EN %i -> EN %i",
     363              :                              idx,
     364            0 :                              eedge->m_src->m_index,
     365            0 :                              eedge->m_dest->m_index);
     366            0 :                 if (retval)
     367            0 :                   logger->log ("  retval: %qE", retval);
     368              :               }
     369         2950 :             if (auto op = eedge->maybe_get_op ())
     370              :               {
     371         1904 :                 if (retval)
     372          396 :                   if (auto phis = op->dyn_cast_phis_for_edge_op ())
     373              :                     {
     374          176 :                       for (auto iter : phis->get_pairs ())
     375           88 :                         if (retval == iter.m_dst)
     376              :                           {
     377              :                             /* We have "PHI(RETVAL = SRC);"
     378              :                                Track SRC instead  */
     379           88 :                             retval = iter.m_src;
     380           88 :                             if (logger)
     381            0 :                               logger->log ("updating retval to %qE", retval);
     382              :                           }
     383              :                     }
     384         1904 :                 if (const gimple *stmt = op->maybe_get_stmt ())
     385         1802 :                   if (consider_stmt_location_p (*stmt, retval))
     386          866 :                     if (useful_location_p (stmt->location))
     387              :                       {
     388          656 :                         if (logger)
     389            0 :                           logger->log ("using location 0x%lx from stmt",
     390            0 :                                        stmt->location);
     391          656 :                         ploc.m_event_loc_info.m_loc = stmt->location;
     392          656 :                         return;
     393              :                       }
     394              :               }
     395              :           }
     396          728 :       }
     397         1194 :   }
     398              : 
     399              : private:
     400              :   static bool
     401          728 :   has_return_stmt_p (const exploded_path &epath,
     402              :                      const greturn *&out_greturn,
     403              :                      logger *logger)
     404              :   {
     405          728 :     LOG_SCOPE (logger);
     406              : 
     407         1252 :     for (int idx = epath.m_elements.size () - 1; idx >= 0; --idx)
     408              :       {
     409         1252 :         const exploded_path::element_t &element = epath.m_elements[idx];
     410         1252 :         const exploded_edge *eedge = element.m_eedge;
     411         1252 :         if (eedge->m_src->get_stack_depth ()
     412         2504 :             != eedge->m_dest->get_stack_depth ())
     413              :           {
     414              :             /* We have interprocedural activity, and
     415              :                presumably are no longer in the function where
     416              :                EPATH terminates.
     417              :                Give up.  */
     418              :             return false;
     419              :           }
     420         1252 :         if (auto op = eedge->maybe_get_op ())
     421              :           {
     422          728 :             switch (op->get_kind ())
     423              :               {
     424              :               default:
     425              :                 break;
     426          728 :               case operation::kind::return_stmt:
     427          728 :                 if (logger)
     428            0 :                   logger->log ("found return_stmt");
     429          728 :                 out_greturn = &((const greturn_op *)op)->get_greturn ();
     430          728 :                 return true;
     431            0 :               case operation::kind::predict_stmt:
     432            0 :                 {
     433            0 :                   auto &stmt = ((const gimple_stmt_op *)op)->get_stmt ();
     434            0 :                   switch (gimple_predict_predictor (&stmt))
     435              :                     {
     436            0 :                     case PRED_TREE_EARLY_RETURN:
     437              :                       /* Assume this is due to a "return;" in the user's
     438              :                          code.  */
     439            0 :                       if (logger)
     440            0 :                         logger->log ("assuming a return: PRED_TREE_EARLY_RETURN");
     441            0 :                       return true;
     442              :                     default:
     443              :                       break;
     444              :                     }
     445              :                 }
     446              :                 break;
     447              :               }
     448              :           }
     449              :       }
     450              :     return false;
     451          728 :   }
     452              : 
     453              :   /* When certain statements show up on the epath of a leak
     454              :      at an exit node, if they have locations, these locations
     455              :      tend to be better locations for the leak.
     456              :      Return true for such statements (but without checking their
     457              :      locations).  */
     458              :   static bool
     459         1802 :   consider_stmt_location_p (const gimple &stmt,
     460              :                             tree retval)
     461              :   {
     462         1802 :     if (retval && TREE_CODE (retval) == SSA_NAME)
     463          306 :       if (&stmt == SSA_NAME_DEF_STMT (retval))
     464              :         return true;
     465              : 
     466         1778 :     switch (stmt.code)
     467              :       {
     468              :       default:
     469              :         break;
     470          526 :       case GIMPLE_CALL:
     471          526 :         {
     472          526 :           const gcall &call = *as_a <const gcall *> (&stmt);
     473          526 :           if (is_cxa_end_catch_p (call))
     474              :             return true;
     475              :         }
     476              :         break;
     477              :       case GIMPLE_PREDICT:
     478              :       case GIMPLE_RETURN:
     479              :         return true;
     480              :       }
     481              :     return false;
     482              :   }
     483              : 
     484              :   const exploded_graph &m_eg;
     485              :   tree m_var;
     486              : };
     487              : 
     488              : std::unique_ptr<pending_location::fixer_for_epath>
     489            0 : make_ploc_fixer_for_epath_for_leak_diagnostic (const exploded_graph &eg,
     490              :                                           tree var)
     491              : {
     492            0 :   return std::make_unique<leak_ploc_fixer_for_epath> (eg, var);
     493              : }
     494              : 
     495              : /* A measurement of how good EXPR is for presenting to the user, so
     496              :    that e.g. we can say prefer printing
     497              :      "leak of 'tmp.m_ptr'"
     498              :    over:
     499              :      "leak of '<unknown>'".  */
     500              : 
     501              : static int
     502        14764 : readability (const_tree expr)
     503              : {
     504              :   /* Arbitrarily-chosen "high readability" value.  */
     505        30522 :   const int HIGH_READABILITY = 65536;
     506              : 
     507        30522 :   gcc_assert (expr);
     508        30522 :   switch (TREE_CODE (expr))
     509              :     {
     510         5238 :     case COMPONENT_REF:
     511         5238 :     case MEM_REF:
     512              :       /* Impose a slight readability penalty relative to that of
     513              :          operand 0.  */
     514         5238 :       return readability (TREE_OPERAND (expr, 0)) - 16;
     515              : 
     516        10162 :     case SSA_NAME:
     517        10162 :       {
     518        10162 :         if (tree var = SSA_NAME_VAR (expr))
     519              :           {
     520         6835 :             if (DECL_ARTIFICIAL (var))
     521              :               {
     522              :                 /* If we have an SSA name for an artificial var,
     523              :                    only use it if it has a debug expr associated with
     524              :                    it that fixup_tree_for_diagnostic can use.  */
     525          132 :                 if (VAR_P (var) && DECL_HAS_DEBUG_EXPR_P (var))
     526            0 :                   return readability (DECL_DEBUG_EXPR (var)) - 1;
     527              :               }
     528              :             else
     529              :               {
     530              :                 /* Slightly favor the underlying var over the SSA name to
     531              :                    avoid having them compare equal.  */
     532         6703 :                 return readability (var) - 1;
     533              :               }
     534              :           }
     535              :         /* Avoid printing '<unknown>' for SSA names for temporaries.  */
     536              :         return -1;
     537              :       }
     538         9145 :       break;
     539              : 
     540         9145 :     case PARM_DECL:
     541         9145 :     case VAR_DECL:
     542         9145 :       if (DECL_NAME (expr))
     543              :         return HIGH_READABILITY;
     544              :       else
     545              :         /* We don't want to print temporaries.  For example, the C FE
     546              :            prints them as e.g. "<Uxxxx>" where "xxxx" is the low 16 bits
     547              :            of the tree pointer (see pp_c_tree_decl_identifier).  */
     548              :         return -1;
     549              : 
     550              :     case RESULT_DECL:
     551              :       /* Printing "<return-value>" isn't ideal, but is less awful than
     552              :          trying to print a temporary.  */
     553              :       return HIGH_READABILITY / 2;
     554              : 
     555         3817 :     case NOP_EXPR:
     556         3817 :       {
     557              :         /* Impose a moderate readability penalty for casts.  */
     558         3817 :         const int CAST_PENALTY = 32;
     559         3817 :         return readability (TREE_OPERAND (expr, 0)) - CAST_PENALTY;
     560              :       }
     561              : 
     562              :     case INTEGER_CST:
     563              :       return HIGH_READABILITY;
     564              : 
     565          184 :     default:
     566          184 :       return 0;
     567              :     }
     568              : 
     569              :   return 0;
     570              : }
     571              : 
     572              : /* A qsort comparator for trees to sort them into most user-readable to
     573              :    least user-readable.  */
     574              : 
     575              : int
     576         7382 : readability_comparator (const void *p1, const void *p2)
     577              : {
     578         7382 :   path_var pv1 = *(path_var const *)p1;
     579         7382 :   path_var pv2 = *(path_var const *)p2;
     580              : 
     581         7382 :   const int tree_r1 = readability (pv1.m_tree);
     582         7382 :   const int tree_r2 = readability (pv2.m_tree);
     583              : 
     584              :   /* Favor items that are deeper on the stack and hence more recent;
     585              :      this also favors locals over globals.  */
     586         7382 :   const int COST_PER_FRAME = 64;
     587         7382 :   const int depth_r1 = pv1.m_stack_depth * COST_PER_FRAME;
     588         7382 :   const int depth_r2 = pv2.m_stack_depth * COST_PER_FRAME;
     589              : 
     590              :   /* Combine the scores from the tree and from the stack depth.
     591              :      This e.g. lets us have a slightly penalized cast in the most
     592              :      recent stack frame "beat" an uncast value in an older stack frame.  */
     593         7382 :   const int sum_r1 = tree_r1 + depth_r1;
     594         7382 :   const int sum_r2 = tree_r2 + depth_r2;
     595         7382 :   if (int cmp = sum_r2 - sum_r1)
     596              :     return cmp;
     597              : 
     598              :   /* Otherwise, more readable trees win.  */
     599         1022 :   if (int cmp = tree_r2 - tree_r1)
     600              :     return cmp;
     601              : 
     602              :   /* Otherwise, if they have the same readability, then impose an
     603              :      arbitrary deterministic ordering on them.  */
     604              : 
     605         1022 :   if (int cmp = TREE_CODE (pv1.m_tree) - TREE_CODE (pv2.m_tree))
     606              :     return cmp;
     607              : 
     608          962 :   switch (TREE_CODE (pv1.m_tree))
     609              :     {
     610              :     default:
     611              :       break;
     612          858 :     case SSA_NAME:
     613          858 :       if (int cmp = (SSA_NAME_VERSION (pv1.m_tree)
     614          858 :                      - SSA_NAME_VERSION (pv2.m_tree)))
     615              :         return cmp;
     616              :       break;
     617            0 :     case PARM_DECL:
     618            0 :     case VAR_DECL:
     619            0 :     case RESULT_DECL:
     620            0 :       if (int cmp = DECL_UID (pv1.m_tree) - DECL_UID (pv2.m_tree))
     621              :         return cmp;
     622              :       break;
     623              :     }
     624              : 
     625              :   /* TODO: We ought to find ways of sorting such cases.  */
     626              :   return 0;
     627              : }
     628              : 
     629              : /* Return true is SNODE is the EXIT node of a function, or is one
     630              :    of the final snodes within its function.
     631              : 
     632              :    Specifically, handle the final supernodes before the EXIT node,
     633              :    for the case of clobbers that happen immediately before exiting.
     634              :    We need a run of snodes leading to the return_p snode, where all edges are
     635              :    intraprocedural, and every snode has just one successor.
     636              : 
     637              :    We use this when suppressing leak reports at the end of "main".  */
     638              : 
     639              : static bool
     640         1625 : returning_from_function_p (const supernode *snode)
     641              : {
     642         1625 :   if (!snode)
     643              :     return false;
     644              : 
     645              :   unsigned count = 0;
     646              :   const supernode *iter = snode;
     647         2626 :   while (true)
     648              :     {
     649         2626 :       if (iter->exit_p ())
     650              :         return true;
     651         1737 :       if (iter->m_succs.length () != 1)
     652              :         return false;
     653         1570 :       const superedge *sedge = iter->m_succs[0];
     654              : 
     655         1570 :       if (auto op = sedge->get_op ())
     656         1279 :         if (op->get_kind () == operation::kind::return_stmt)
     657              :           return true;
     658              : 
     659         1035 :       iter = sedge->m_dest;
     660              : 
     661              :       /* Impose a limit to ensure we terminate for pathological cases.
     662              : 
     663              :          We only care about the final 3 nodes, due to cases like:
     664              :            BB:
     665              :              (clobber causing leak)
     666              : 
     667              :            BB:
     668              :            <label>:
     669              :            return _val;
     670              : 
     671              :            EXIT BB.*/
     672         1035 :       if (++count > 4)
     673              :         return false;
     674              :     }
     675              : }
     676              : 
     677              : /* Find the best tree for SVAL and call SM's on_leak vfunc with it.
     678              :    If on_leak returns a pending_diagnostic, queue it up to be reported,
     679              :    so that we potentially complain about a leak of SVAL in the given STATE.  */
     680              : 
     681              : void
     682         1625 : impl_region_model_context::on_state_leak (const state_machine &sm,
     683              :                                           const svalue *sval,
     684              :                                           state_machine::state_t state)
     685              : {
     686         1625 :   logger * const logger = get_logger ();
     687         1625 :   LOG_SCOPE (logger);
     688         1625 :   if (logger)
     689              :     {
     690            0 :       logger->start_log_line ();
     691            0 :       logger->log_partial ("considering leak of ");
     692            0 :       sval->dump_to_pp (logger->get_printer (), true);
     693            0 :       logger->end_log_line ();
     694              :     }
     695              : 
     696         1625 :   if (!m_eg)
     697              :     return;
     698              : 
     699              :   /* m_old_state also needs to be non-NULL so that the sm_ctxt can look
     700              :      up the old state of SVAL.  */
     701         1625 :   gcc_assert (m_old_state);
     702              : 
     703              :   /* SVAL has leaked within the new state: it is not used by any reachable
     704              :      regions.
     705              :      We need to convert it back to a tree, but since it's likely no regions
     706              :      use it, we have to find the "best" tree for it in the old_state.  */
     707         1625 :   svalue_set visited;
     708         1625 :   path_var leaked_pv
     709         1625 :     = m_old_state->m_region_model->get_representative_path_var (sval,
     710              :                                                                 &visited,
     711              :                                                                 nullptr);
     712              : 
     713              :   /* Strip off top-level casts  */
     714         1625 :   if (leaked_pv.m_tree && TREE_CODE (leaked_pv.m_tree) == NOP_EXPR)
     715           94 :     leaked_pv.m_tree = TREE_OPERAND (leaked_pv.m_tree, 0);
     716              : 
     717              :   /* This might be NULL; the pending_diagnostic subclasses need to cope
     718              :      with this.  */
     719         1625 :   tree leaked_tree = leaked_pv.m_tree;
     720         1625 :   if (logger)
     721              :     {
     722            0 :       if (leaked_tree)
     723            0 :         logger->log ("best leaked_tree: %qE", leaked_tree);
     724              :       else
     725            0 :         logger->log ("best leaked_tree: NULL");
     726              :     }
     727              : 
     728         1625 :   gcc_assert (m_enode_for_diag);
     729              : 
     730              :   /* Don't complain about leaks when returning from "main".  */
     731         1625 :   if (returning_from_function_p (m_enode_for_diag->get_supernode ()))
     732              :     {
     733         1458 :       tree fndecl = m_enode_for_diag->get_function ()->decl;
     734         1458 :       if (id_equal (DECL_NAME (fndecl), "main"))
     735              :         {
     736           66 :           if (logger)
     737            0 :             logger->log ("not reporting leak from main");
     738           66 :           return;
     739              :         }
     740              :     }
     741              : 
     742         1559 :   tree leaked_tree_for_diag = fixup_tree_for_diagnostic (leaked_tree);
     743         1559 :   std::unique_ptr<pending_diagnostic> pd = sm.on_leak (leaked_tree_for_diag,
     744              :                                                        m_old_state,
     745         1559 :                                                        m_new_state);
     746         1559 :   if (pd)
     747              :     {
     748         1310 :       pending_location ploc (get_pending_location_for_diag ());
     749         1310 :       ploc.m_fixer_for_epath
     750         1310 :         = std::make_unique<leak_ploc_fixer_for_epath> (*m_eg, leaked_tree);
     751         1310 :       m_eg->get_diagnostic_manager ().add_diagnostic
     752         1310 :         (&sm, std::move (ploc),
     753              :          leaked_tree_for_diag, sval, state, std::move (pd));
     754         1310 :     }
     755         1625 : }
     756              : 
     757              : /* Implementation of region_model_context::on_condition vfunc.
     758              :    Notify all state machines about the condition, which could lead to
     759              :    state transitions.  */
     760              : 
     761              : void
     762        35788 : impl_region_model_context::on_condition (const svalue *lhs,
     763              :                                          enum tree_code op,
     764              :                                          const svalue *rhs)
     765              : {
     766        35788 :   int sm_idx;
     767        35788 :   sm_state_map *smap;
     768       286094 :   FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
     769              :     {
     770       250306 :       const state_machine &sm = m_ext_state.get_sm (sm_idx);
     771       500612 :       impl_sm_context sm_ctxt (*m_eg, sm_idx, sm, m_enode_for_diag,
     772              :                                m_old_state, m_new_state,
     773       500612 :                                m_old_state->m_checker_states[sm_idx],
     774       500612 :                                m_new_state->m_checker_states[sm_idx],
     775       250306 :                                m_path_ctxt);
     776       250306 :       sm.on_condition (sm_ctxt, lhs, op, rhs);
     777       250306 :     }
     778        35788 : }
     779              : 
     780              : /* Implementation of region_model_context::on_bounded_ranges vfunc.
     781              :    Notify all state machines about the ranges, which could lead to
     782              :    state transitions.  */
     783              : 
     784              : void
     785         6100 : impl_region_model_context::on_bounded_ranges (const svalue &sval,
     786              :                                               const bounded_ranges &ranges)
     787              : {
     788         6100 :   int sm_idx;
     789         6100 :   sm_state_map *smap;
     790        48800 :   FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
     791              :     {
     792        42700 :       const state_machine &sm = m_ext_state.get_sm (sm_idx);
     793        85400 :       impl_sm_context sm_ctxt (*m_eg, sm_idx, sm, m_enode_for_diag,
     794              :                                m_old_state, m_new_state,
     795        85400 :                                m_old_state->m_checker_states[sm_idx],
     796        85400 :                                m_new_state->m_checker_states[sm_idx],
     797        42700 :                                m_path_ctxt);
     798        42700 :       sm.on_bounded_ranges (sm_ctxt, sval, ranges);
     799        42700 :     }
     800         6100 : }
     801              : 
     802              : /* Implementation of region_model_context::on_pop_frame vfunc.
     803              :    Notify all state machines about the frame being popped, which
     804              :    could lead to states being discarded.  */
     805              : 
     806              : void
     807        25930 : impl_region_model_context::on_pop_frame (const frame_region *frame_reg)
     808              : {
     809        25930 :   int sm_idx;
     810        25930 :   sm_state_map *smap;
     811       207072 :   FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
     812              :     {
     813       181142 :       const state_machine &sm = m_ext_state.get_sm (sm_idx);
     814       181142 :       sm.on_pop_frame (smap, frame_reg);
     815              :     }
     816        25930 : }
     817              : 
     818              : /* Implementation of region_model_context::on_phi vfunc.
     819              :    Notify all state machines about the phi, which could lead to
     820              :    state transitions.  */
     821              : 
     822              : void
     823            0 : impl_region_model_context::on_phi (const gphi *phi, tree rhs)
     824              : {
     825            0 :   int sm_idx;
     826            0 :   sm_state_map *smap;
     827            0 :   FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
     828              :     {
     829            0 :       const state_machine &sm = m_ext_state.get_sm (sm_idx);
     830            0 :       impl_sm_context sm_ctxt (*m_eg, sm_idx, sm, m_enode_for_diag,
     831              :                                m_old_state, m_new_state,
     832            0 :                                m_old_state->m_checker_states[sm_idx],
     833            0 :                                m_new_state->m_checker_states[sm_idx],
     834            0 :                                m_path_ctxt);
     835            0 :       sm.on_phi (sm_ctxt, phi, rhs);
     836            0 :     }
     837            0 : }
     838              : 
     839              : /* Implementation of region_model_context::on_unexpected_tree_code vfunc.
     840              :    Mark the new state as being invalid for further exploration.
     841              :    TODO(stage1): introduce a warning for when this occurs.  */
     842              : 
     843              : void
     844           52 : impl_region_model_context::on_unexpected_tree_code (tree t,
     845              :                                                     const dump_location_t &loc)
     846              : {
     847           52 :   logger * const logger = get_logger ();
     848           52 :   if (logger)
     849              :     {
     850            2 :       const dump_impl_location_t &impl_loc = loc.get_impl_location ();
     851            2 :       const char *unknown = "<unknown>";
     852            2 :       logger->log ("unhandled tree code: %qs in %qs at %s:%i",
     853            2 :                    get_tree_code_name (TREE_CODE (t)),
     854            2 :                    impl_loc.m_function ? impl_loc.m_function : unknown,
     855            2 :                    impl_loc.m_file ? impl_loc.m_file : unknown,
     856            2 :                    impl_loc.m_line);
     857              :     }
     858           52 :   if (m_new_state)
     859           52 :     m_new_state->m_valid = false;
     860           52 : }
     861              : 
     862              : /* Implementation of region_model_context::maybe_did_work vfunc.
     863              :    Mark that "externally visible work" has occurred, and thus we
     864              :    shouldn't report an infinite loop here.  */
     865              : 
     866              : void
     867        24979 : impl_region_model_context::maybe_did_work ()
     868              : {
     869        24979 :   if (m_out_could_have_done_work)
     870        23440 :     *m_out_could_have_done_work = true;
     871        24979 : }
     872              : 
     873              : pending_location
     874         4519 : impl_region_model_context::get_pending_location_for_diag () const
     875              : {
     876         4519 :   if (m_stmt && useful_location_p (m_stmt->location))
     877         3193 :     return pending_location (m_enode_for_diag, m_stmt->location);
     878              :   else
     879         1326 :     return pending_location (m_enode_for_diag);
     880              : }
     881              : 
     882              : /* struct point_and_state.  */
     883              : 
     884              : /* Assert that this object is sane.  */
     885              : 
     886              : void
     887       832671 : point_and_state::validate (const extrinsic_state &ext_state) const
     888              : {
     889              :   /* Skip this in a release build.  */
     890              : #if !CHECKING_P
     891              :   return;
     892              : #endif
     893              : 
     894       832671 :   m_point.validate ();
     895              : 
     896       832671 :   m_state.validate (ext_state);
     897              : 
     898              :   /* Verify that the callstring's model of the stack corresponds to that
     899              :      of the region_model.  */
     900              :   /* They should have the same depth.  */
     901      1658398 :   gcc_assert (m_point.get_stack_depth ()
     902              :               == m_state.m_region_model->get_stack_depth ());
     903              :   /* Check the functions in the callstring vs those in the frames
     904              :      at each depth.  */
     905      1251331 :   for (const frame_region *iter_frame
     906       832671 :          = m_state.m_region_model->get_current_frame ();
     907      2084002 :        iter_frame; iter_frame = iter_frame->get_calling_frame ())
     908              :     {
     909      1251331 :       int index = iter_frame->get_index ();
     910      1251331 :       gcc_assert (m_point.get_function_at_depth (index)
     911              :                   == &iter_frame->get_function ());
     912              :     }
     913       832671 : }
     914              : 
     915              : /* Subroutine of print_enode_indices: print a run of indices from START_IDX
     916              :    to END_IDX to PP, using and updating *FIRST_RUN.  */
     917              : 
     918              : static void
     919         9879 : print_run (pretty_printer *pp, int start_idx, int end_idx,
     920              :            bool *first_run)
     921              : {
     922         9879 :   if (!(*first_run))
     923         6963 :     pp_string (pp, ", ");
     924         9879 :   *first_run = false;
     925         9879 :   if (start_idx == end_idx)
     926         6892 :     pp_printf (pp, "EN: %i", start_idx);
     927              :   else
     928         2987 :     pp_printf (pp, "EN: %i-%i", start_idx, end_idx);
     929         9879 : }
     930              : 
     931              : /* Print the indices within ENODES to PP, collecting them as
     932              :    runs/singletons e.g. "EN: 4-7, EN: 20-23, EN: 42".  */
     933              : 
     934              : static void
     935         2928 : print_enode_indices (pretty_printer *pp,
     936              :                      const auto_vec<exploded_node *> &enodes)
     937              : {
     938         2928 :   int cur_start_idx = -1;
     939         2928 :   int cur_finish_idx = -1;
     940         2928 :   bool first_run = true;
     941         2928 :   unsigned i;
     942         2928 :   exploded_node *enode;
     943        21553 :   FOR_EACH_VEC_ELT (enodes, i, enode)
     944              :     {
     945        18625 :       if (cur_start_idx == -1)
     946              :         {
     947         2916 :           gcc_assert (cur_finish_idx == -1);
     948         2916 :           cur_start_idx = cur_finish_idx = enode->m_index;
     949              :         }
     950              :       else
     951              :         {
     952        15709 :           if (enode->m_index == cur_finish_idx + 1)
     953              :             /* Continuation of a run.  */
     954              :             cur_finish_idx = enode->m_index;
     955              :           else
     956              :             {
     957              :               /* Finish existing run, start a new one.  */
     958         6963 :               gcc_assert (cur_start_idx >= 0);
     959         6963 :               gcc_assert (cur_finish_idx >= 0);
     960         6963 :               print_run (pp, cur_start_idx, cur_finish_idx,
     961              :                          &first_run);
     962         6963 :               cur_start_idx = cur_finish_idx = enode->m_index;
     963              :             }
     964              :         }
     965              :     }
     966              :   /* Finish any existing run.  */
     967         2928 :   if (cur_start_idx >= 0)
     968              :     {
     969         2916 :       gcc_assert (cur_finish_idx >= 0);
     970         2916 :       print_run (pp, cur_start_idx, cur_finish_idx,
     971              :                  &first_run);
     972              :     }
     973         2928 : }
     974              : 
     975              : /* struct eg_traits::dump_args_t.  */
     976              : 
     977              : /* The <FILENAME>.eg.dot output can quickly become unwieldy if we show
     978              :    full details for all enodes (both in terms of CPU time to render it,
     979              :    and in terms of being meaningful to a human viewing it).
     980              : 
     981              :    If we show just the IDs then the resulting graph is usually viewable,
     982              :    but then we have to keep switching back and forth between the .dot
     983              :    view and other dumps.
     984              : 
     985              :    This function implements a heuristic for showing detail at the enodes
     986              :    that (we hope) matter, and just the ID at other enodes, fixing the CPU
     987              :    usage of the .dot viewer, and drawing the attention of the viewer
     988              :    to these enodes.
     989              : 
     990              :    Return true if ENODE should be shown in detail in .dot output.
     991              :    Return false if no detail should be shown for ENODE.  */
     992              : 
     993              : bool
     994          507 : eg_traits::dump_args_t::show_enode_details_p (const exploded_node &enode) const
     995              : {
     996              :   /* If the number of exploded nodes isn't too large, we may as well show
     997              :      all enodes in full detail in the .dot output.  */
     998          507 :   if (m_eg.m_nodes.length ()
     999          507 :         <= (unsigned) param_analyzer_max_enodes_for_full_dump)
    1000              :     return true;
    1001              : 
    1002              :   /* Otherwise, assume that what's most interesting are state explosions,
    1003              :      and thus the places where this happened.
    1004              :      Expand enodes at program points where we hit the per-enode limit, so we
    1005              :      can investigate what exploded.  */
    1006            0 :   const per_program_point_data *per_point_data
    1007            0 :     = m_eg.get_per_program_point_data (enode.get_point ());
    1008            0 :   return per_point_data->m_excess_enodes > 0;
    1009              : }
    1010              : 
    1011              : /* class exploded_node : public dnode<eg_traits>.  */
    1012              : 
    1013              : const char *
    1014            0 : exploded_node::status_to_str (enum status s)
    1015              : {
    1016            0 :   switch (s)
    1017              :     {
    1018            0 :     default: gcc_unreachable ();
    1019              :     case status::worklist: return "worklist";
    1020            0 :     case status::processed: return "processed";
    1021            0 :     case status::special: return "special";
    1022            0 :     case status::merger: return "merger";
    1023            0 :     case status::bulk_merged: return "bulk_merged";
    1024              :     }
    1025              : }
    1026              : 
    1027              : /* exploded_node's ctor.  */
    1028              : 
    1029       412650 : exploded_node::exploded_node (const point_and_state &ps,
    1030       412650 :                               int index)
    1031       412650 : : m_ps (ps), m_status (status::worklist), m_index (index)
    1032              : {
    1033       412650 :   gcc_checking_assert (ps.get_state ().m_region_model->canonicalized_p ());
    1034       412650 : }
    1035              : 
    1036              : /* For use by dump_dot, get a value for the .dot "fillcolor" attribute.
    1037              :    Colorize by sm-state, to make it easier to see how sm-state propagates
    1038              :    through the exploded_graph.  */
    1039              : 
    1040              : const char *
    1041         1014 : exploded_node::get_dot_fillcolor () const
    1042              : {
    1043         1014 :   const program_state &state = get_state ();
    1044              : 
    1045              :   /* We want to be able to easily distinguish the no-sm-state case,
    1046              :      and to be able to distinguish cases where there's a single state
    1047              :      from each other.
    1048              : 
    1049              :      Sum the sm_states, and use the result to choose from a table,
    1050              :      modulo table-size, special-casing the "no sm-state" case.   */
    1051         1014 :   int total_sm_state = 0;
    1052         1014 :   int i;
    1053         1014 :   sm_state_map *smap;
    1054         8112 :   FOR_EACH_VEC_ELT (state.m_checker_states, i, smap)
    1055              :     {
    1056         8450 :       for (sm_state_map::iterator_t iter = smap->begin ();
    1057         8450 :            iter != smap->end ();
    1058         1352 :            ++iter)
    1059         1352 :         total_sm_state += (*iter).second.m_state->get_id ();
    1060         7098 :       total_sm_state += smap->get_global_state ()->get_id ();
    1061              :     }
    1062              : 
    1063         1014 :   if (total_sm_state > 0)
    1064              :     {
    1065              :       /* An arbitrarily-picked collection of light colors.  */
    1066          752 :       const char * const colors[]
    1067              :         = {"azure", "coral", "cornsilk", "lightblue", "yellow",
    1068              :            "honeydew", "lightpink", "lightsalmon", "palegreen1",
    1069              :            "wheat", "seashell"};
    1070          752 :       const int num_colors = ARRAY_SIZE (colors);
    1071          752 :       return colors[total_sm_state % num_colors];
    1072              :     }
    1073              :   else
    1074              :     /* No sm-state.   */
    1075              :     return "lightgrey";
    1076              : }
    1077              : 
    1078              : /* Implementation of dnode::dump_dot vfunc for exploded_node.  */
    1079              : 
    1080              : void
    1081          507 : exploded_node::dump_dot (graphviz_out *gv, const dump_args_t &args) const
    1082              : {
    1083          507 :   pretty_printer *pp = gv->get_pp ();
    1084              : 
    1085          507 :   dump_dot_id (pp);
    1086          507 :   pp_printf (pp, " [shape=none,margin=0,style=filled,fillcolor=%s,label=\"",
    1087              :              get_dot_fillcolor ());
    1088          507 :   pp_write_text_to_stream (pp);
    1089              : 
    1090          507 :   pp_printf (pp, "EN: %i", m_index);
    1091          507 :   if (m_status == status::merger)
    1092           12 :     pp_string (pp, " (merger)");
    1093          495 :   else if (m_status == status::bulk_merged)
    1094            0 :     pp_string (pp, " (bulk merged)");
    1095          507 :   pp_newline (pp);
    1096              : 
    1097          507 :   if (args.show_enode_details_p (*this))
    1098              :     {
    1099          507 :       format f (true);
    1100          507 :       m_ps.get_point ().print (pp, f);
    1101          507 :       pp_newline (pp);
    1102              : 
    1103          507 :       bool show_state = true;
    1104              : 
    1105              :       /* Don't show the state if we have a single predecessor
    1106              :          and the state hasn't changed.  */
    1107          507 :       if (m_preds.length () == 1
    1108          499 :           && get_state () == m_preds[0]->m_src->get_state ())
    1109              :         show_state = false;
    1110              : 
    1111          393 :       if (show_state)
    1112              :         {
    1113          393 :           const extrinsic_state &ext_state = args.m_eg.get_ext_state ();
    1114          393 :           const program_state &state = m_ps.get_state ();
    1115          393 :           state.dump_to_pp (ext_state, false, true, pp);
    1116          393 :           pp_newline (pp);
    1117              :         }
    1118              :     }
    1119              : 
    1120          507 :   dump_saved_diagnostics (pp);
    1121              : 
    1122          507 :   args.dump_extra_info (this, pp);
    1123              : 
    1124          507 :   pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true);
    1125              : 
    1126          507 :   pp_string (pp, "\"];\n\n");
    1127              : 
    1128              :   /* It can be hard to locate the saved diagnostics as text within the
    1129              :      enode nodes, so add extra nodes to the graph for each saved_diagnostic,
    1130              :      highlighted in red.
    1131              :      Compare with dump_saved_diagnostics.  */
    1132          507 :   {
    1133          507 :     unsigned i;
    1134          507 :     const saved_diagnostic *sd;
    1135         1022 :     FOR_EACH_VEC_ELT (m_saved_diagnostics, i, sd)
    1136              :       {
    1137            8 :         sd->dump_as_dot_node (pp);
    1138              : 
    1139              :         /* Add edge connecting this enode to the saved_diagnostic.  */
    1140            8 :         dump_dot_id (pp);
    1141            8 :         pp_string (pp, " -> ");
    1142            8 :         sd->dump_dot_id (pp);
    1143            8 :         pp_string (pp, " [style=\"dotted\" arrowhead=\"none\"];");
    1144            8 :         pp_newline (pp);
    1145              :       }
    1146              :   }
    1147              : 
    1148          507 :   pp_flush (pp);
    1149          507 : }
    1150              : 
    1151              : /* Dump any saved_diagnostics at this enode to PP.  */
    1152              : 
    1153              : void
    1154          581 : exploded_node::dump_saved_diagnostics (pretty_printer *pp) const
    1155              : {
    1156          581 :   unsigned i;
    1157          581 :   const saved_diagnostic *sd;
    1158          593 :   FOR_EACH_VEC_ELT (m_saved_diagnostics, i, sd)
    1159              :     {
    1160           12 :       pp_printf (pp, "DIAGNOSTIC: %s (sd: %i)",
    1161           12 :                  sd->m_d->get_kind (), sd->get_index ());
    1162           12 :       pp_newline (pp);
    1163              :     }
    1164          581 : }
    1165              : 
    1166              : /* Dump this to PP in a form suitable for use as an id in .dot output.  */
    1167              : 
    1168              : void
    1169         1545 : exploded_node::dump_dot_id (pretty_printer *pp) const
    1170              : {
    1171         1545 :   pp_printf (pp, "exploded_node_%i", m_index);
    1172         1545 : }
    1173              : 
    1174              : /* Dump a multiline representation of this node to PP.  */
    1175              : 
    1176              : void
    1177            0 : exploded_node::dump_to_pp (pretty_printer *pp,
    1178              :                            const extrinsic_state &ext_state) const
    1179              : {
    1180            0 :   pp_printf (pp, "EN: %i", m_index);
    1181            0 :   pp_newline (pp);
    1182              : 
    1183            0 :   format f (true);
    1184            0 :   m_ps.get_point ().print (pp, f);
    1185            0 :   pp_newline (pp);
    1186              : 
    1187            0 :   m_ps.get_state ().dump_to_pp (ext_state, false, true, pp);
    1188            0 :   pp_newline (pp);
    1189            0 : }
    1190              : 
    1191              : /* Dump a multiline representation of this node to FILE.  */
    1192              : 
    1193              : void
    1194            0 : exploded_node::dump (FILE *fp,
    1195              :                      const extrinsic_state &ext_state) const
    1196              : {
    1197            0 :   tree_dump_pretty_printer pp (fp);
    1198            0 :   dump_to_pp (&pp, ext_state);
    1199            0 : }
    1200              : 
    1201              : /* Dump a multiline representation of this node to stderr.  */
    1202              : 
    1203              : DEBUG_FUNCTION void
    1204            0 : exploded_node::dump (const extrinsic_state &ext_state) const
    1205              : {
    1206            0 :   dump (stderr, ext_state);
    1207            0 : }
    1208              : 
    1209              : /* Return a new json::object of the form
    1210              :    {"point"  : object for program_point,
    1211              :     "state"  : object for program_state,
    1212              :     "status" : str,
    1213              :     "idx"    : int}.  */
    1214              : 
    1215              : std::unique_ptr<json::object>
    1216            0 : exploded_node::to_json (const extrinsic_state &ext_state) const
    1217              : {
    1218            0 :   auto enode_obj = std::make_unique<json::object> ();
    1219              : 
    1220            0 :   enode_obj->set ("point", get_point ().to_json ());
    1221            0 :   enode_obj->set ("state", get_state ().to_json (ext_state));
    1222            0 :   enode_obj->set_string ("status", status_to_str (m_status));
    1223            0 :   enode_obj->set_integer ("idx", m_index);
    1224              : 
    1225            0 :   return enode_obj;
    1226              : }
    1227              : 
    1228              : } // namespace ana
    1229              : 
    1230              : /* Return true if FNDECL has a gimple body.  */
    1231              : // TODO: is there a pre-canned way to do this?
    1232              : 
    1233              : bool
    1234        18451 : fndecl_has_gimple_body_p (tree fndecl)
    1235              : {
    1236        18451 :   if (fndecl == NULL_TREE)
    1237              :     return false;
    1238              : 
    1239        18451 :   cgraph_node *n = cgraph_node::get (fndecl);
    1240        18451 :   if (!n)
    1241              :     return false;
    1242              : 
    1243        18451 :   return n->has_gimple_body_p ();
    1244              : }
    1245              : 
    1246              : namespace ana {
    1247              : 
    1248              : /* Subclass of call_info for exploded edges that express
    1249              :    a throw or rethrow of an exception (actually a call
    1250              :    to __cxa_throw or __cxa_rethrow).  */
    1251              : 
    1252              : class throw_custom_edge : public call_info
    1253              : {
    1254              : public:
    1255          187 :   throw_custom_edge (const call_details &cd,
    1256              :                      tree type,
    1257              :                      bool is_rethrow)
    1258          187 :   : call_info (cd),
    1259          187 :     m_type (type),
    1260          187 :     m_is_rethrow (is_rethrow)
    1261              :   {
    1262              :   }
    1263              : 
    1264            0 :   void print (pretty_printer *pp) const final override
    1265              :   {
    1266            0 :     if (m_is_rethrow)
    1267              :       {
    1268            0 :         if (m_type)
    1269            0 :           pp_printf (pp, "rethrowing %qT", m_type);
    1270              :         else
    1271            0 :           pp_printf (pp, "rethrowing");
    1272              :       }
    1273              :     else
    1274              :       {
    1275            0 :         if (m_type)
    1276            0 :           pp_printf (pp, "throwing %qT", m_type);
    1277              :         else
    1278            0 :           pp_printf (pp, "throwing");
    1279              :       }
    1280            0 :   }
    1281              : 
    1282            0 :   void print_desc (pretty_printer &pp) const final override
    1283              :   {
    1284            0 :     print (&pp);
    1285            0 :   }
    1286              : 
    1287          343 :   bool update_model (region_model *model,
    1288              :                      const exploded_edge *,
    1289              :                      region_model_context *ctxt) const final override
    1290              :   {
    1291          343 :     if (m_is_rethrow)
    1292              :       {
    1293          117 :         if (auto eh_node = model->get_current_caught_exception ())
    1294           84 :           model->push_thrown_exception (*eh_node);
    1295              :         else
    1296              :           {
    1297              :             /* We have a rethrow of some unknown exception.
    1298              :                We don't have a good way of representing this;
    1299              :                leave the exception stack empty.  */
    1300              :           }
    1301              :       }
    1302              :     else
    1303              :       {
    1304          226 :         call_details cd (get_call_details (model, ctxt));
    1305              : 
    1306          226 :         const svalue *exception_sval = cd.get_arg_svalue (0);
    1307          226 :         const svalue *tinfo_sval = cd.get_arg_svalue (1);
    1308          226 :         const svalue *destructor_sval = cd.get_arg_svalue (2);
    1309              : 
    1310              :         /* Push a new exception_node on the model's m_exception_stack.  */
    1311          226 :         exception_node eh_node (exception_sval, tinfo_sval, destructor_sval);
    1312          226 :         model->push_thrown_exception (eh_node);
    1313              :       }
    1314              : 
    1315          343 :     return true;
    1316              :   }
    1317              : 
    1318           69 :   void add_events_to_path (checker_path *emission_path,
    1319              :                            const exploded_edge &eedge,
    1320              :                            pending_diagnostic &,
    1321              :                            const state_transition *) const final override
    1322              :   {
    1323           69 :     const exploded_node *dst_node = eedge.m_dest;
    1324           69 :     const program_point &dst_point = dst_node->get_point ();
    1325           69 :     const int dst_stack_depth = dst_point.get_stack_depth ();
    1326              : 
    1327           69 :     const gcall &call = get_call_stmt ();
    1328              : 
    1329           69 :     emission_path->add_event
    1330           69 :       (std::make_unique<explicit_throw_event>
    1331           69 :          (event_loc_info (call.location,
    1332              :                           dst_point.get_fndecl (),
    1333           69 :                           dst_stack_depth),
    1334              :           dst_node,
    1335              :           call,
    1336           69 :           m_type,
    1337           69 :           m_is_rethrow));
    1338           69 :   }
    1339              : 
    1340              : private:
    1341              :   tree m_type;
    1342              :   bool m_is_rethrow;
    1343              : };
    1344              : 
    1345              : /* Subclass of custom_edge_info for an exploded edge that expresses
    1346              :    unwinding one stack frame during exception handling.  */
    1347              : 
    1348              : class unwind_custom_edge : public custom_edge_info
    1349              : {
    1350              : public:
    1351         6668 :   unwind_custom_edge (location_t loc)
    1352         6668 :   : m_loc (loc)
    1353              :   {
    1354              :   }
    1355              : 
    1356            0 :   void print (pretty_printer *pp) const final override
    1357              :   {
    1358            0 :     pp_printf (pp, "unwinding frame");
    1359            0 :   }
    1360              : 
    1361         6700 :   bool update_model (region_model *model,
    1362              :                      const exploded_edge *,
    1363              :                      region_model_context *ctxt) const final override
    1364              :   {
    1365         6700 :     model->pop_frame (NULL_TREE, nullptr, ctxt, nullptr, false);
    1366         6700 :     return true;
    1367              :   }
    1368              : 
    1369           16 :   void add_events_to_path (checker_path *emission_path,
    1370              :                            const exploded_edge &eedge,
    1371              :                            pending_diagnostic &,
    1372              :                            const state_transition *) const final override
    1373              :   {
    1374           16 :     const exploded_node *src_node = eedge.m_src;
    1375           16 :     const program_point &src_point = src_node->get_point ();
    1376           16 :     const int src_stack_depth = src_point.get_stack_depth ();
    1377           16 :     emission_path->add_event
    1378           32 :       (std::make_unique<unwind_event> (event_loc_info (m_loc,
    1379              :                                                        src_point.get_fndecl (),
    1380           16 :                                                        src_stack_depth)));
    1381           16 :   }
    1382              : 
    1383              : private:
    1384              :   location_t m_loc;
    1385              : };
    1386              : 
    1387              : /* Locate an SNODE that's a CFG edge with the EH flag,
    1388              :    or return nullptr. */
    1389              : 
    1390              : static const superedge *
    1391         7530 : get_eh_outedge (const supernode &snode)
    1392              : {
    1393        27360 :   for (auto out_sedge : snode.m_succs)
    1394         6934 :     if (::edge cfg_edge = out_sedge->get_any_cfg_edge ())
    1395         1316 :       if (cfg_edge->flags & EDGE_EH)
    1396              :         return out_sedge;
    1397              : 
    1398              :   // Not found
    1399              :   return nullptr;
    1400              : }
    1401              : 
    1402              : /* Given THROWN_ENODE, which expreses a throw or rethrow occurring at
    1403              :    THROW_STMT, unwind intraprocedurally and interprocedurally to find
    1404              :    the next eh_dispatch statement to handle exceptions, if any.
    1405              : 
    1406              :    Add eedges and enodes to this graph expressing the actions taken
    1407              :    to reach an enode containing the eh_dispatch stmt, if any.
    1408              :    Only the final enode is added to this graph's worklist.
    1409              : 
    1410              :    Use CTXT to warn about problems e.g. memory leaks due to stack frames
    1411              :    being unwound.  */
    1412              : 
    1413              : void
    1414         6232 : exploded_graph::unwind_from_exception (exploded_node &thrown_enode,
    1415              :                                        const gimple *throw_stmt,
    1416              :                                        region_model_context *ctxt)
    1417              : {
    1418         6232 :   logger * const logger = get_logger ();
    1419         6232 :   LOG_FUNC_1 (logger, "thrown EN: %i", thrown_enode.m_index);
    1420              : 
    1421              :   /* Iteratively unwind the stack looking for an out-cfg-edge
    1422              :      flagged EH.  */
    1423         6232 :   exploded_node *iter_enode = &thrown_enode;
    1424         6232 :   while (iter_enode)
    1425              :     {
    1426              :       /* If we have an out-cfg-edge flagged EH, follow that,
    1427              :          presumably to a bb with a label and an eh_dispatch stmt.
    1428              :          Otherwise assume no out-cfgs-edges, and we are unwinding to the
    1429              :          caller.  */
    1430         7530 :       if (auto sedge = get_eh_outedge (*iter_enode->get_supernode ()))
    1431              :         {
    1432              :           /* Intraprocedural case.
    1433              :              Assume we have an out-edge flagged with EH leading to
    1434              :              code for dispatch to catch handlers.  */
    1435          862 :           const program_point next_point
    1436          862 :             (sedge->m_dest,
    1437          862 :              iter_enode->get_point ().get_call_string ());
    1438          862 :           exploded_node *next_enode
    1439          862 :             = get_or_create_node (next_point,
    1440              :                                   iter_enode->get_state (),
    1441              :                                   iter_enode,
    1442              :                                   /* Add this enode to the worklist.  */
    1443              :                                   true);
    1444          862 :           if (!next_enode)
    1445              :             return;
    1446              : 
    1447          862 :           add_edge (iter_enode, next_enode, nullptr, false, nullptr);
    1448          862 :           return;
    1449              :         }
    1450              :       else
    1451              :         {
    1452              :           /* Interprocedural case.
    1453              :              No out-cfg-edge.  Unwind one stack frame.  */
    1454         6668 :           program_state unwound_state (iter_enode->get_state ());
    1455         6668 :           location_t loc = throw_stmt ? throw_stmt->location : UNKNOWN_LOCATION;
    1456         6668 :           auto unwind_edge_info
    1457         6668 :             = std::make_unique<unwind_custom_edge> (loc);
    1458         6668 :           unwind_edge_info->update_model (unwound_state.m_region_model, nullptr,
    1459              :                                           ctxt);
    1460              : 
    1461              :           /* Detect leaks in the new state relative to the old state.
    1462              :              Use an alternate ctxt that uses the original enode and the stmt
    1463              :              (if any) for the location of any diagnostics.  */
    1464         6668 :           {
    1465         6668 :             uncertainty_t uncertainty;
    1466         6668 :             impl_region_model_context ctxt (*this,
    1467              :                                             &thrown_enode,
    1468         6668 :                                             &iter_enode->get_state (),
    1469              :                                             &unwound_state,
    1470              :                                             &uncertainty,
    1471              :                                             nullptr,
    1472         6668 :                                             throw_stmt);
    1473         6668 :             program_state::detect_leaks (iter_enode->get_state (),
    1474              :                                          unwound_state,
    1475              :                                          nullptr,
    1476              :                                          get_ext_state (), &ctxt);
    1477         6668 :           }
    1478         6668 :           const call_string &cs = iter_enode->get_point ().get_call_string ();
    1479         6668 :           if (cs.empty_p ())
    1480              :             {
    1481              :               /* Top-level stack frame in analysis: unwinding
    1482              :                  to the outside world that called us.  */
    1483              :               return;
    1484              :             }
    1485              :           else
    1486              :             {
    1487              :               /* Nested function in analysis: unwinding to
    1488              :                  the callsite in the analysis (or beyond).  */
    1489         1714 :               program_point unwound_point (cs.get_return_node_in_caller (), cs);
    1490         1714 :               unwound_point.pop_from_call_stack ();
    1491              : 
    1492         1714 :               exploded_node *after_unwind_enode
    1493         1714 :                 = get_or_create_node (unwound_point,
    1494              :                                       std::move (unwound_state),
    1495              :                                       iter_enode,
    1496              :                                       /* Don't add this enode to the
    1497              :                                          worklist; we will process it
    1498              :                                          on the next iteration.  */
    1499              :                                       false);
    1500              : 
    1501         1714 :               if (!after_unwind_enode)
    1502          416 :                 return;
    1503              : 
    1504         1298 :               add_edge (iter_enode, after_unwind_enode, nullptr, true,
    1505         1298 :                         std::move (unwind_edge_info));
    1506         1298 :               iter_enode = after_unwind_enode;
    1507              :             }
    1508         6668 :         }
    1509              :     }
    1510         6232 : }
    1511              : 
    1512              : /* Handle THROW_CALL, a call to __cxa_throw or __cxa_rethrow.
    1513              : 
    1514              :    Create an eedge and destination enode for the throw/rethrow, adding
    1515              :    them to this egraph.  The new enode isn't added to the worklist, but
    1516              :    instead exploded_graph::unwind_from_exception is immediately called
    1517              :    on it, potentially creating more eedges and enodes leading to an
    1518              :    eh_handler stmt.  */
    1519              : 
    1520              : void
    1521          190 : exploded_node::on_throw (exploded_graph &eg,
    1522              :                          const gcall &throw_call,
    1523              :                          const program_point &after_throw_point,
    1524              :                          program_state *new_state,
    1525              :                          bool is_rethrow,
    1526              :                          region_model_context *ctxt)
    1527              : {
    1528          190 :   region_model *model = new_state->m_region_model;
    1529          190 :   call_details cd (throw_call, model, ctxt);
    1530              : 
    1531          190 :   if (!cd.get_fndecl_for_call ())
    1532            3 :     return;
    1533              : 
    1534              :   /* Create an enode and eedge for the "throw".  */
    1535          187 :   tree type = NULL_TREE;
    1536          187 :   if (is_rethrow)
    1537              :     {
    1538           81 :       const exception_node *eh_node = model->get_current_caught_exception ();
    1539           66 :       if (eh_node)
    1540           66 :         type = eh_node->maybe_get_type ();
    1541              :       else
    1542              :         {
    1543              :           /* We have a "throw;" but no exception to rethrow.
    1544              :              Presumably the top-level of the analysis is an
    1545              :              entrypoint for handling exceptions, so we will
    1546              :              simulate fully unwinding.  */
    1547              :         }
    1548              :     }
    1549              :   else
    1550              :     {
    1551          106 :       const svalue *tinfo_sval = cd.get_arg_svalue (1);
    1552          106 :       type = tinfo_sval->maybe_get_type_from_typeinfo ();
    1553              :     }
    1554              : 
    1555          187 :   auto throw_edge_info
    1556          187 :     = std::make_unique<throw_custom_edge> (cd, type, is_rethrow);
    1557          187 :   throw_edge_info->update_model (model, nullptr, ctxt);
    1558              : 
    1559          187 :   exploded_node *after_throw_enode
    1560          187 :     = eg.get_or_create_node (after_throw_point, *new_state, this,
    1561              :                              /* Don't add to worklist; we process
    1562              :                                 this immediately below.  */
    1563              :                              false);
    1564              : 
    1565          187 :   if (!after_throw_enode)
    1566            0 :     return;
    1567              : 
    1568              :   /* Create custom exploded_edge for a throw.  */
    1569          187 :   eg.add_edge (this, after_throw_enode, nullptr, true,
    1570          187 :                std::move (throw_edge_info));
    1571              : 
    1572          187 :   eg.unwind_from_exception (*after_throw_enode, &throw_call, ctxt);
    1573          187 : }
    1574              : 
    1575              : /* Subroutine of exploded_graph::process_node for finding the successors
    1576              :    of the supernode for a function exit basic block.
    1577              : 
    1578              :    Ensure that pop_frame is called, potentially queuing diagnostics about
    1579              :    leaks.  */
    1580              : 
    1581              : void
    1582        11896 : exploded_node::detect_leaks (exploded_graph &eg)
    1583              : {
    1584        11896 :   LOG_FUNC_1 (eg.get_logger (), "EN: %i", m_index);
    1585              : 
    1586        11896 :   gcc_assert (get_point ().get_supernode ()->exit_p ());
    1587              : 
    1588              :   /* If we're not a "top-level" function, do nothing; pop_frame
    1589              :      will be called when handling the return superedge.  */
    1590        11896 :   if (get_point ().get_stack_depth () > 1)
    1591            0 :     return;
    1592              : 
    1593              :   /* We have a "top-level" function.  */
    1594        11896 :   gcc_assert (get_point ().get_stack_depth () == 1);
    1595              : 
    1596        11896 :   const program_state &old_state = get_state ();
    1597              : 
    1598              :   /* Work with a temporary copy of the state: pop the frame, and see
    1599              :      what leaks (via purge_unused_svalues).  */
    1600        11896 :   program_state new_state (old_state);
    1601              : 
    1602        11896 :   gcc_assert (new_state.m_region_model);
    1603              : 
    1604        11896 :   uncertainty_t uncertainty;
    1605        11896 :   impl_region_model_context ctxt (eg, this,
    1606              :                                   &old_state, &new_state, &uncertainty, nullptr,
    1607        11896 :                                   nullptr);
    1608        11896 :   const svalue *result = nullptr;
    1609        11896 :   new_state.m_region_model->pop_frame (nullptr, &result, &ctxt, nullptr);
    1610        11896 :   program_state::detect_leaks (old_state, new_state, result,
    1611              :                                eg.get_ext_state (), &ctxt);
    1612        23792 : }
    1613              : 
    1614              : /* Dump the successors and predecessors of this enode to OUTF.  */
    1615              : 
    1616              : void
    1617            0 : exploded_node::dump_succs_and_preds (FILE *outf) const
    1618              : {
    1619            0 :   unsigned i;
    1620            0 :   exploded_edge *e;
    1621            0 :   {
    1622            0 :     auto_vec<exploded_node *> preds (m_preds.length ());
    1623            0 :     FOR_EACH_VEC_ELT (m_preds, i, e)
    1624            0 :       preds.quick_push (e->m_src);
    1625            0 :     pretty_printer pp;
    1626            0 :     print_enode_indices (&pp, preds);
    1627            0 :     fprintf (outf, "preds: %s\n",
    1628              :              pp_formatted_text (&pp));
    1629            0 :   }
    1630            0 :   {
    1631            0 :     auto_vec<exploded_node *> succs (m_succs.length ());
    1632            0 :     FOR_EACH_VEC_ELT (m_succs, i, e)
    1633            0 :       succs.quick_push (e->m_dest);
    1634            0 :     pretty_printer pp;
    1635            0 :     print_enode_indices (&pp, succs);
    1636            0 :     fprintf (outf, "succs: %s\n",
    1637              :              pp_formatted_text (&pp));
    1638            0 :   }
    1639            0 : }
    1640              : 
    1641              : // class interprocedural_call : public custom_edge_info
    1642              : 
    1643              : void
    1644            8 : interprocedural_call::print (pretty_printer *pp) const
    1645              : {
    1646            8 :   pp_string (pp, "call to ");
    1647            8 :   pp_gimple_stmt_1 (pp, &get_gcall (), 0, (dump_flags_t)0);
    1648            8 : }
    1649              : 
    1650              : void
    1651            8 : interprocedural_call::get_dot_attrs (const char *&/*out_style*/,
    1652              :                                      const char *&out_color) const
    1653              : {
    1654            8 :   out_color = "red";
    1655            8 : }
    1656              : 
    1657              : bool
    1658         8276 : interprocedural_call::update_state (program_state *state,
    1659              :                                     const exploded_edge *eedge,
    1660              :                                     region_model_context *ctxt) const
    1661              : {
    1662         8276 :   return update_model (state->m_region_model, eedge, ctxt);
    1663              : }
    1664              : 
    1665              : bool
    1666        14503 : interprocedural_call::update_model (region_model *model,
    1667              :                                     const exploded_edge */*eedge*/,
    1668              :                                     region_model_context *ctxt) const
    1669              : {
    1670        14503 :   model->update_for_gcall (get_gcall (), ctxt, &m_callee_fun);
    1671        14503 :   return true;
    1672              : }
    1673              : 
    1674              : void
    1675         1347 : interprocedural_call::add_events_to_path (checker_path *emission_path,
    1676              :                                           const exploded_edge &eedge,
    1677              :                                           pending_diagnostic &pd,
    1678              :                                           const state_transition *state_trans) const
    1679              : {
    1680         1370 :   pd.add_call_event (eedge, get_gcall (), *emission_path,
    1681              :                      (state_trans
    1682           23 :                       ? state_trans->dyn_cast_state_transition_at_call ()
    1683              :                       : nullptr));
    1684         1347 : }
    1685              : 
    1686              : bool
    1687         1193 : interprocedural_call::try_to_rewind_data_flow (rewind_context &ctxt) const
    1688              : {
    1689         1193 :   auto logger = ctxt.m_logger;
    1690              : 
    1691              :   // Rewind from params to arguments
    1692         1193 :   if (ctxt.m_input.m_region_holding_value)
    1693              :     {
    1694           57 :       const region_model &dst_enode_model = ctxt.get_dst_region_model ();
    1695           57 :       tree dst_tree
    1696              :         = dst_enode_model.get_representative_tree
    1697           57 :             (ctxt.m_input.m_region_holding_value);
    1698           57 :       if (dst_tree)
    1699              :         {
    1700           52 :           callsite_expr expr;
    1701           52 :           tree src_tree
    1702           52 :             = m_op.map_expr_from_callee_to_caller (m_callee_fun.decl,
    1703              :                                                    dst_tree,
    1704              :                                                    &expr);
    1705           52 :           if (src_tree)
    1706              :             {
    1707           23 :               const region_model &src_enode_model = ctxt.get_src_region_model ();
    1708           23 :               ctxt.m_output.m_region_holding_value
    1709           23 :                 = src_enode_model.get_lvalue (src_tree, nullptr);
    1710              : 
    1711           23 :               ctxt.add_state_transition
    1712           23 :                 (std::make_unique<state_transition_at_call> (expr));
    1713              : 
    1714           23 :               if (logger)
    1715              :                 {
    1716            0 :                   callsite_expr_element e (expr);
    1717            0 :                   logger->log ("updating m_region_holding_value from %qE to %qE"
    1718              :                                " (callsite_expr: %e)",
    1719              :                                dst_tree, src_tree, &e);
    1720            0 :                 }
    1721              :             }
    1722              :         }
    1723              :     }
    1724              : 
    1725         1193 :   return true;
    1726              : }
    1727              : 
    1728              : const gcall &
    1729        15858 : interprocedural_call::get_gcall () const
    1730              : {
    1731        15858 :   return m_op.get_gcall ();
    1732              : }
    1733              : 
    1734              : // class interprocedural_return : public custom_edge_info
    1735              : 
    1736              : void
    1737           16 : interprocedural_return::print (pretty_printer *pp) const
    1738              : {
    1739           16 :   pp_string (pp, "return from ");
    1740           16 :   pp_gimple_stmt_1 (pp, &m_call_stmt, 0, (dump_flags_t)0);
    1741           16 : }
    1742              : 
    1743              : void
    1744            8 : interprocedural_return::get_dot_attrs (const char *&/*out_style*/,
    1745              :                                        const char *&out_color) const
    1746              : {
    1747            8 :   out_color = "green";
    1748            8 : }
    1749              : 
    1750              : bool
    1751         7344 : interprocedural_return::update_state (program_state *state,
    1752              :                                       const exploded_edge *eedge,
    1753              :                                       region_model_context *ctxt) const
    1754              : {
    1755         7344 :   return update_model (state->m_region_model, eedge, ctxt);
    1756              : }
    1757              : 
    1758              : bool
    1759        10880 : interprocedural_return::update_model (region_model *model,
    1760              :                                       const exploded_edge */*eedge*/,
    1761              :                                       region_model_context *ctxt) const
    1762              : {
    1763        10880 :   model->update_for_return_gcall (m_call_stmt, ctxt);
    1764        10880 :   return true;
    1765              : }
    1766              : 
    1767              : void
    1768          737 : interprocedural_return::add_events_to_path (checker_path *emission_path,
    1769              :                                             const exploded_edge &eedge,
    1770              :                                             pending_diagnostic &,
    1771              :                                             const state_transition *state_trans) const
    1772              : {
    1773          737 :   const program_point &dst_point = eedge.m_dest->get_point ();
    1774          737 :   emission_path->add_event
    1775          737 :     (std::make_unique<return_event>
    1776          737 :        (eedge,
    1777         1474 :         event_loc_info (m_call_stmt.location,
    1778              :                         dst_point.get_fndecl (),
    1779         1474 :                         dst_point.get_stack_depth ()),
    1780              :         (state_trans
    1781          737 :          ? state_trans->dyn_cast_state_transition_at_return ()
    1782              :          : nullptr)));
    1783          737 : }
    1784              : 
    1785              : bool
    1786          671 : interprocedural_return::try_to_rewind_data_flow (rewind_context &ctxt) const
    1787              : {
    1788          671 :   auto logger = ctxt.m_logger;
    1789              : 
    1790          671 :   tree lhs = gimple_call_lhs (&m_call_stmt);
    1791          671 :   if (!lhs)
    1792              :     return true;
    1793              : 
    1794          419 :   const region_model &src_enode_model = ctxt.get_src_region_model ();
    1795          419 :   tree fndecl = src_enode_model.get_current_function ()->decl;
    1796          419 :   tree fn_result = DECL_RESULT (fndecl);
    1797              : 
    1798          419 :   ctxt.on_data_flow (DECL_RESULT (fndecl), lhs);
    1799              : 
    1800          419 :   return true;
    1801              : }
    1802              : 
    1803              : /* class exploded_edge : public dedge<eg_traits>.  */
    1804              : 
    1805              : /* exploded_edge's ctor.  */
    1806              : 
    1807       426907 : exploded_edge::exploded_edge (exploded_node *src, exploded_node *dest,
    1808              :                               const superedge *sedge, bool could_do_work,
    1809       426907 :                               std::unique_ptr<custom_edge_info> custom_info)
    1810       426907 : : dedge<eg_traits> (src, dest), m_sedge (sedge),
    1811       426907 :   m_custom_info (std::move (custom_info)),
    1812       426907 :   m_could_do_work_p (could_do_work)
    1813              : {
    1814       426907 : }
    1815              : 
    1816              : /* Implementation of dedge::dump_dot vfunc for exploded_edge.
    1817              :    Use the label of the underlying superedge, if any.  */
    1818              : 
    1819              : void
    1820          515 : exploded_edge::dump_dot (graphviz_out *gv, const dump_args_t &) const
    1821              : {
    1822          515 :   pretty_printer *pp = gv->get_pp ();
    1823              : 
    1824          515 :   m_src->dump_dot_id (pp);
    1825          515 :   pp_string (pp, " -> ");
    1826          515 :   m_dest->dump_dot_id (pp);
    1827          515 :   dump_dot_label (pp);
    1828          515 : }
    1829              : 
    1830              : /* Second half of exploded_edge::dump_dot.  This is split out
    1831              :    for use by trimmed_graph::dump_dot and base_feasible_edge::dump_dot.  */
    1832              : 
    1833              : void
    1834          585 : exploded_edge::dump_dot_label (pretty_printer *pp) const
    1835              : {
    1836          585 :   const char *style = "\"solid,bold\"";
    1837          585 :   const char *color = "black";
    1838          585 :   int weight = 10;
    1839          585 :   const char *constraint = "true";
    1840              : 
    1841          585 :   if (m_sedge)
    1842              :     {
    1843          539 :       if (m_sedge->get_op ())
    1844          426 :         style = "\"solid\"";
    1845              :       else
    1846          113 :         style = "\"dotted\"";
    1847              :     }
    1848          585 :   if (m_custom_info)
    1849           22 :     m_custom_info->get_dot_attrs (style, color);
    1850              : 
    1851          585 :   pp_printf (pp,
    1852              :              (" [style=%s, color=%s, weight=%d, constraint=%s,"
    1853              :               " headlabel=\""),
    1854              :              style, color, weight, constraint);
    1855          585 :   pp_flush (pp);
    1856              : 
    1857          585 :   if (m_sedge)
    1858          539 :     m_sedge->dump_label_to_pp (pp, false);
    1859           46 :   else if (m_custom_info)
    1860           14 :     m_custom_info->print (pp);
    1861              : 
    1862         1132 :   pp_printf (pp, "%s",
    1863          585 :              could_do_work_p () ? "(could do work)" : "DOES NO WORK");
    1864              : 
    1865          585 :   pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/false);
    1866              : 
    1867          585 :   pp_printf (pp, "\"];\n");
    1868          585 : }
    1869              : 
    1870              : /* Return a new json::object of the form
    1871              :    {"src_idx": int, the index of the source exploded edge,
    1872              :     "dst_idx": int, the index of the destination exploded edge,
    1873              :     "sedge": (optional) object for the superedge, if any,
    1874              :     "custom": (optional) str, a description, if this is a custom edge}.  */
    1875              : 
    1876              : std::unique_ptr<json::object>
    1877            0 : exploded_edge::to_json () const
    1878              : {
    1879            0 :   auto eedge_obj = std::make_unique<json::object> ();
    1880            0 :   eedge_obj->set_integer ("src_idx", m_src->m_index);
    1881            0 :   eedge_obj->set_integer ("dst_idx", m_dest->m_index);
    1882            0 :   if (m_sedge)
    1883            0 :     eedge_obj->set ("sedge", m_sedge->to_json ());
    1884            0 :   if (m_custom_info)
    1885              :     {
    1886            0 :       pretty_printer pp;
    1887            0 :       pp_format_decoder (&pp) = default_tree_printer;
    1888            0 :       m_custom_info->print (&pp);
    1889            0 :       eedge_obj->set_string ("custom", pp_formatted_text (&pp));
    1890            0 :     }
    1891            0 :   return eedge_obj;
    1892              : }
    1893              : 
    1894              : const gimple *
    1895        17851 : exploded_edge::maybe_get_stmt () const
    1896              : {
    1897        17851 :   auto op = maybe_get_op ();
    1898        17851 :   if (!op)
    1899              :     return nullptr;
    1900        13703 :   return op->maybe_get_stmt ();
    1901              : }
    1902              : 
    1903              : const operation *
    1904        57913 : exploded_edge::maybe_get_op () const
    1905              : {
    1906        57913 :   if (!m_sedge)
    1907              :     return nullptr;
    1908        52837 :   return m_sedge->get_op ();
    1909              : }
    1910              : 
    1911              : /* struct stats.  */
    1912              : 
    1913              : /* stats' ctor.  */
    1914              : 
    1915        27680 : stats::stats (int num_supernodes)
    1916        27680 : : m_num_nodes (0),
    1917        27680 :   m_node_reuse_count (0),
    1918        27680 :   m_node_reuse_after_merge_count (0),
    1919        27680 :   m_num_supernodes (num_supernodes)
    1920              : {
    1921        27680 : }
    1922              : 
    1923              : /* Log these stats in multiline form to LOGGER.  */
    1924              : 
    1925              : void
    1926           11 : stats::log (logger *logger) const
    1927              : {
    1928           11 :   gcc_assert (logger);
    1929           11 :   logger->log ("m_num_nodes: %i", m_num_nodes);
    1930           11 :   logger->log ("m_node_reuse_count: %i", m_node_reuse_count);
    1931           11 :   logger->log ("m_node_reuse_after_merge_count: %i",
    1932           11 :                m_node_reuse_after_merge_count);
    1933           11 : }
    1934              : 
    1935              : /* Dump these stats in multiline form to OUT.  */
    1936              : 
    1937              : void
    1938            0 : stats::dump (FILE *out) const
    1939              : {
    1940            0 :   fprintf (out, "m_num_nodes: %i\n", m_num_nodes);
    1941            0 :   fprintf (out, "m_node_reuse_count: %i\n", m_node_reuse_count);
    1942            0 :   fprintf (out, "m_node_reuse_after_merge_count: %i\n",
    1943            0 :            m_node_reuse_after_merge_count);
    1944              : 
    1945            0 :   if (m_num_supernodes > 0)
    1946            0 :     fprintf (out, "enodes per supernode: %.2f\n",
    1947            0 :              (float)m_num_nodes / (float)m_num_supernodes);
    1948            0 : }
    1949              : 
    1950              : /* Return the total number of enodes recorded within this object.  */
    1951              : 
    1952              : int
    1953            6 : stats::get_total_enodes () const
    1954              : {
    1955            6 :   return m_num_nodes;
    1956              : }
    1957              : 
    1958              : /* struct per_function_data.  */
    1959              : 
    1960          367 : per_function_data::~per_function_data ()
    1961              : {
    1962         1732 :   for (auto iter : m_summaries)
    1963          631 :     delete iter;
    1964          367 : }
    1965              : 
    1966              : void
    1967          631 : per_function_data::add_call_summary (exploded_node *node)
    1968              : {
    1969          631 :   m_summaries.safe_push (new call_summary (this, node));
    1970          631 : }
    1971              : 
    1972              : /* strongly_connected_components's ctor.  Tarjan's SCC algorithm.  */
    1973              : 
    1974         3474 : strongly_connected_components::
    1975         3474 : strongly_connected_components (const supergraph &sg, logger *logger)
    1976         6944 : : m_sg (sg), m_per_node (m_sg.m_nodes.length ())
    1977              : {
    1978         3474 :   LOG_SCOPE (logger);
    1979         3474 :   auto_timevar tv (TV_ANALYZER_SCC);
    1980              : 
    1981       194359 :   for (size_t i = 0; i < m_sg.m_nodes.length (); i++)
    1982       190885 :     m_per_node.quick_push (per_node_data ());
    1983              : 
    1984       201299 :   for (auto snode : m_sg.m_nodes)
    1985       190885 :     if (m_per_node[snode->m_id].m_id == -1)
    1986        10792 :       strong_connect (snode->m_id, logger);
    1987              : 
    1988         3474 :   if (0)
    1989              :     dump ();
    1990         3474 : }
    1991              : 
    1992              : /* Dump this object to stderr.  */
    1993              : 
    1994              : DEBUG_FUNCTION void
    1995            0 : strongly_connected_components::dump () const
    1996              : {
    1997            0 :   fprintf (stderr, "Stack: [");
    1998            0 :   bool first = true;
    1999            0 :   for (auto i : m_stack)
    2000              :     {
    2001            0 :       if (first)
    2002              :         first = false;
    2003              :       else
    2004            0 :         fprintf (stderr, ", ");
    2005            0 :       fprintf (stderr, "%i", i);
    2006              :     }
    2007            0 :   fprintf (stderr, "]\n");
    2008            0 :   for (size_t i = 0; i < m_sg.m_nodes.length (); i++)
    2009              :     {
    2010            0 :       const per_node_data &v = m_per_node[i];
    2011            0 :       fprintf (stderr, "SN %lu: index: %i lowlink: %i on_stack: %i\n",
    2012            0 :                i, v.m_id, v.m_lowlink, v.m_on_stack);
    2013              :     }
    2014            0 : }
    2015              : 
    2016              : /* Return a new json::array of per-snode SCC ids.  */
    2017              : 
    2018              : std::unique_ptr<json::array>
    2019            0 : strongly_connected_components::to_json () const
    2020              : {
    2021            0 :   auto scc_arr = std::make_unique<json::array> ();
    2022            0 :   for (size_t i = 0; i < m_sg.m_nodes.length (); i++)
    2023            0 :     scc_arr->append (std::make_unique<json::integer_number> (get_scc_id (i)));
    2024            0 :   return scc_arr;
    2025              : }
    2026              : 
    2027              : /* Subroutine of strongly_connected_components's ctor, part of Tarjan's
    2028              :    SCC algorithm.  */
    2029              : 
    2030              : void
    2031       190885 : strongly_connected_components::strong_connect (unsigned id,
    2032              :                                                logger *logger)
    2033              : {
    2034       190885 :   supernode *v_snode = m_sg.m_nodes[id];
    2035       190885 :   if (!v_snode)
    2036       190885 :     return;
    2037              : 
    2038              :   /* Set the depth index for v to the smallest unused index.  */
    2039       190885 :   per_node_data *v = &m_per_node[id];
    2040       190885 :   v->m_id = id;
    2041       190885 :   v->m_lowlink = id;
    2042       190885 :   m_stack.safe_push (id);
    2043       190885 :   v->m_on_stack = true;
    2044       190885 :   id++;
    2045              : 
    2046              :   /* Consider successors of v.  */
    2047       190885 :   unsigned i;
    2048       190885 :   superedge *sedge;
    2049       381242 :   FOR_EACH_VEC_ELT (v_snode->m_succs, i, sedge)
    2050              :     {
    2051       190357 :       supernode *w_snode = sedge->m_dest;
    2052       190357 :       per_node_data *w = &m_per_node[w_snode->m_id];
    2053       190357 :       if (w->m_id == -1)
    2054              :         {
    2055              :           /* Successor w has not yet been visited; recurse on it.  */
    2056       180093 :           strong_connect (w_snode->m_id, logger);
    2057       180093 :           v->m_lowlink = MIN (v->m_lowlink, w->m_lowlink);
    2058              :         }
    2059        10264 :       else if (w->m_on_stack)
    2060              :         {
    2061              :           /* Successor w is in stack S and hence in the current SCC
    2062              :              If w is not on stack, then (v, w) is a cross-edge in the DFS
    2063              :              tree and must be ignored.  */
    2064         2173 :           v->m_lowlink = MIN (v->m_lowlink, w->m_id);
    2065              :         }
    2066              :     }
    2067              : 
    2068              :   /* If v is a root node, pop the stack and generate an SCC.  */
    2069              : 
    2070       190885 :   if (v->m_lowlink == v->m_id)
    2071              :     {
    2072       174647 :       if (logger)
    2073           50 :         logger->log ("got SCC root node: SN %i", v->m_id);
    2074       190885 :       per_node_data *w;
    2075       190885 :       do {
    2076       190885 :         int id = m_stack.pop ();
    2077       190885 :         w = &m_per_node[id];
    2078       190885 :         w->m_on_stack = false;
    2079       190885 :         if (logger)
    2080           80 :           logger->log ("  popping SN %i", w->m_id);
    2081       190885 :       } while (w != v);
    2082              :     }
    2083              : }
    2084              : 
    2085              : /* worklist's ctor.  */
    2086              : 
    2087         3474 : worklist::worklist (const exploded_graph &eg, const analysis_plan &plan)
    2088         3474 : : m_scc (eg.get_supergraph (), eg.get_logger ()),
    2089         3474 :   m_plan (plan),
    2090         3474 :   m_queue (key_t (*this, nullptr))
    2091              : {
    2092         3474 : }
    2093              : 
    2094              : /* Return the number of nodes in the worklist.  */
    2095              : 
    2096              : unsigned
    2097       387222 : worklist::length () const
    2098              : {
    2099       387222 :   return m_queue.nodes ();
    2100              : }
    2101              : 
    2102              : /* Return the next node in the worklist, removing it.  */
    2103              : 
    2104              : exploded_node *
    2105       404157 : worklist::take_next ()
    2106              : {
    2107       404157 :   return m_queue.extract_min ();
    2108              : }
    2109              : 
    2110              : /* Return the next node in the worklist without removing it.  */
    2111              : 
    2112              : exploded_node *
    2113       426864 : worklist::peek_next ()
    2114              : {
    2115       426864 :   return m_queue.min ();
    2116              : }
    2117              : 
    2118              : /* Add ENODE to the worklist.  */
    2119              : 
    2120              : void
    2121       405195 : worklist::add_node (exploded_node *enode)
    2122              : {
    2123       405195 :   gcc_assert (enode->get_status () == exploded_node::status::worklist);
    2124       405195 :   m_queue.insert (key_t (*this, enode), enode);
    2125       405195 : }
    2126              : 
    2127              : /* Comparator for implementing worklist::key_t comparison operators.
    2128              :    Return negative if KA is before KB
    2129              :    Return positive if KA is after KB
    2130              :    Return 0 if they are equal.
    2131              : 
    2132              :    The ordering of the worklist is critical for performance and for
    2133              :    avoiding node explosions.  Ideally we want all enodes at a CFG join-point
    2134              :    with the same callstring to be sorted next to each other in the worklist
    2135              :    so that a run of consecutive enodes can be merged and processed "in bulk"
    2136              :    rather than individually or pairwise, minimizing the number of new enodes
    2137              :    created.  */
    2138              : 
    2139              : int
    2140      1970331 : worklist::key_t::cmp (const worklist::key_t &ka, const worklist::key_t &kb)
    2141              : {
    2142      1970331 :   const program_point &point_a = ka.m_enode->get_point ();
    2143      1970331 :   const program_point &point_b = kb.m_enode->get_point ();
    2144      1970331 :   const call_string &call_string_a = point_a.get_call_string ();
    2145      1970331 :   const call_string &call_string_b = point_b.get_call_string ();
    2146              : 
    2147              :   /* Order empty-callstring points with different functions based on the
    2148              :      analysis_plan, so that we generate summaries before they are used.  */
    2149      1970331 :   if (flag_analyzer_call_summaries
    2150       199898 :       && call_string_a.empty_p ()
    2151       176060 :       && call_string_b.empty_p ()
    2152       176060 :       && point_a.get_function () != nullptr
    2153      2095904 :       && point_b.get_function () != nullptr
    2154      2146011 :       && point_a.get_function () != point_b.get_function ())
    2155              :     {
    2156        50107 :       if (int cmp = ka.m_worklist.m_plan.cmp_function (point_a.get_function (),
    2157              :                                                        point_b.get_function ()))
    2158              :         return cmp;
    2159              :     }
    2160              : 
    2161              :   /* Sort by callstring, so that nodes with deeper call strings are processed
    2162              :      before those with shallower call strings.
    2163              :      If we have
    2164              :          splitting BB
    2165              :              /  \
    2166              :             /    \
    2167              :        fn call   no fn call
    2168              :             \    /
    2169              :              \  /
    2170              :             join BB
    2171              :      then we want the path inside the function call to be fully explored up
    2172              :      to the return to the join BB before we explore on the "no fn call" path,
    2173              :      so that both enodes at the join BB reach the front of the worklist at
    2174              :      the same time and thus have a chance of being merged.  */
    2175      1920224 :   int cs_cmp = call_string::cmp (call_string_a, call_string_b);
    2176      1920224 :   if (cs_cmp)
    2177              :     return cs_cmp;
    2178              : 
    2179              :   /* Order by SCC.  */
    2180      1717489 :   int scc_id_a = ka.get_scc_id (ka.m_enode);
    2181      1717489 :   int scc_id_b = kb.get_scc_id (kb.m_enode);
    2182      1717489 :   if (scc_id_a != scc_id_b)
    2183      1321579 :     return scc_id_a - scc_id_b;
    2184              : 
    2185              :   /* If in same SCC, order by supernode index (an arbitrary but stable
    2186              :      ordering).  */
    2187       395910 :   const supernode *snode_a = ka.m_enode->get_supernode ();
    2188       395910 :   const supernode *snode_b = kb.m_enode->get_supernode ();
    2189       395910 :   if (snode_a == nullptr)
    2190              :     {
    2191            0 :       if (snode_b != nullptr)
    2192              :         /* One is nullptr.  */
    2193              :         return -1;
    2194              :       else
    2195              :         /* Both are nullptr.  */
    2196            0 :         return 0;
    2197              :     }
    2198       395910 :   if (snode_b == nullptr)
    2199              :     /* One is nullptr.  */
    2200              :     return 1;
    2201              :   /* Neither are nullptr.  */
    2202       392446 :   gcc_assert (snode_a && snode_b);
    2203       392446 :   if (snode_a->m_bb->index != snode_b->m_bb->index)
    2204        34951 :     return snode_a->m_bb->index - snode_b->m_bb->index;
    2205       357495 :   if (snode_a->m_id != snode_b->m_id)
    2206        54513 :     return snode_a->m_id - snode_b->m_id;
    2207              : 
    2208       302982 :   gcc_assert (snode_a == snode_b);
    2209              : 
    2210              :   /* Otherwise, we ought to have the same program_point.  */
    2211       302982 :   gcc_assert (point_a == point_b);
    2212              : 
    2213              :   const program_state &state_a = ka.m_enode->get_state ();
    2214              :   const program_state &state_b = kb.m_enode->get_state ();
    2215              : 
    2216              :   /* Sort by sm-state, so that identical sm-states are grouped
    2217              :      together in the worklist.  */
    2218      1334057 :   for (unsigned sm_idx = 0; sm_idx < state_a.m_checker_states.length ();
    2219              :        ++sm_idx)
    2220              :     {
    2221      1221096 :       sm_state_map *smap_a = state_a.m_checker_states[sm_idx];
    2222      1221096 :       sm_state_map *smap_b = state_b.m_checker_states[sm_idx];
    2223              : 
    2224      1221096 :       if (int smap_cmp = sm_state_map::cmp (*smap_a, *smap_b))
    2225              :         return smap_cmp;
    2226              :     }
    2227              : 
    2228              :   /* Otherwise, we have two enodes at the same program point but with
    2229              :      different states.  We don't have a good total ordering on states,
    2230              :      so order them by enode index, so that we have at least have a
    2231              :      stable sort.  */
    2232       112961 :   return ka.m_enode->m_index - kb.m_enode->m_index;
    2233              : }
    2234              : 
    2235              : /* Return a new json::object of the form
    2236              :    {"scc" : [per-snode-IDs]},  */
    2237              : 
    2238              : std::unique_ptr<json::object>
    2239            0 : worklist::to_json () const
    2240              : {
    2241            0 :   auto worklist_obj = std::make_unique<json::object> ();
    2242              : 
    2243            0 :   worklist_obj->set ("scc", m_scc.to_json ());
    2244              : 
    2245              :   /* The following field isn't yet being JSONified:
    2246              :      queue_t m_queue;  */
    2247              : 
    2248            0 :   return worklist_obj;
    2249              : }
    2250              : 
    2251              : /* exploded_graph's ctor.  */
    2252              : 
    2253         3474 : exploded_graph::exploded_graph (const supergraph &sg, logger *logger,
    2254              :                                 const extrinsic_state &ext_state,
    2255              :                                 const state_purge_map *purge_map,
    2256              :                                 const analysis_plan &plan,
    2257         3474 :                                 int verbosity)
    2258         3474 : : m_sg (sg), m_logger (logger),
    2259         3474 :   m_worklist (*this, plan),
    2260         3474 :   m_ext_state (ext_state),
    2261         3474 :   m_purge_map (purge_map),
    2262         3474 :   m_plan (plan),
    2263         3474 :   m_diagnostic_manager (logger, ext_state.get_engine (), verbosity),
    2264         6944 :   m_global_stats (m_sg.m_nodes.length ()),
    2265        10418 :   m_functionless_stats (m_sg.m_nodes.length ())
    2266              : {
    2267         6948 :   m_origin = get_or_create_node
    2268         3474 :     (program_point::origin (*ext_state.get_model_manager ()),
    2269         6948 :      program_state (ext_state), nullptr);
    2270         3474 : }
    2271              : 
    2272              : /* exploded_graph's dtor.  */
    2273              : 
    2274         3474 : exploded_graph::~exploded_graph ()
    2275              : {
    2276       506240 :   for (auto iter : m_per_point_data)
    2277       502766 :     delete iter.second;
    2278         4208 :   for (auto iter : m_per_function_data)
    2279          367 :     delete iter.second;
    2280        17732 :   for (auto iter : m_per_function_stats)
    2281        10788 :     delete iter.second;
    2282        23362 :   for (auto iter : m_per_call_string_data)
    2283         9944 :     delete iter.second;
    2284         6948 : }
    2285              : 
    2286              : /* Subroutine for use when implementing __attribute__((tainted_args))
    2287              :    on functions and on function pointer fields in structs.
    2288              : 
    2289              :    Called on STATE representing a call to FNDECL.
    2290              :    Mark all params of FNDECL in STATE as "tainted".  Mark the value of all
    2291              :    regions pointed to by params of FNDECL as "tainted".
    2292              : 
    2293              :    Return true if successful; return false if the "taint" state machine
    2294              :    was not found.  */
    2295              : 
    2296              : static bool
    2297          184 : mark_params_as_tainted (program_state *state, tree fndecl,
    2298              :                         const extrinsic_state &ext_state)
    2299              : {
    2300          184 :   unsigned taint_sm_idx;
    2301          184 :   if (!ext_state.get_sm_idx_by_name ("taint", &taint_sm_idx))
    2302              :     return false;
    2303          184 :   sm_state_map *smap = state->m_checker_states[taint_sm_idx];
    2304              : 
    2305          184 :   const state_machine &sm = ext_state.get_sm (taint_sm_idx);
    2306          184 :   state_machine::state_t tainted = sm.get_state_by_name ("tainted");
    2307              : 
    2308          184 :   region_model_manager *mgr = ext_state.get_model_manager ();
    2309              : 
    2310          184 :   function *fun = DECL_STRUCT_FUNCTION (fndecl);
    2311          184 :   gcc_assert (fun);
    2312              : 
    2313          435 :   for (tree iter_parm = DECL_ARGUMENTS (fndecl); iter_parm;
    2314          251 :        iter_parm = DECL_CHAIN (iter_parm))
    2315              :     {
    2316          251 :       tree param = iter_parm;
    2317          251 :       if (tree parm_default_ssa = ssa_default_def (fun, iter_parm))
    2318          193 :         param = parm_default_ssa;
    2319          251 :       const region *param_reg = state->m_region_model->get_lvalue (param, nullptr);
    2320          251 :       const svalue *init_sval = mgr->get_or_create_initial_value (param_reg);
    2321          251 :       smap->set_state (state->m_region_model, init_sval,
    2322              :                        tainted, nullptr /*origin_new_sval*/, ext_state);
    2323          251 :       if (POINTER_TYPE_P (TREE_TYPE (param)))
    2324              :         {
    2325           48 :           const region *pointee_reg = mgr->get_symbolic_region (init_sval);
    2326              :           /* Mark "*param" as tainted.  */
    2327           48 :           const svalue *init_pointee_sval
    2328           48 :             = mgr->get_or_create_initial_value (pointee_reg);
    2329           48 :           smap->set_state (state->m_region_model, init_pointee_sval,
    2330              :                            tainted, nullptr /*origin_new_sval*/, ext_state);
    2331              :         }
    2332              :     }
    2333              : 
    2334              :   return true;
    2335              : }
    2336              : 
    2337              : /* Custom event for use by tainted_args_function_info when a function
    2338              :    has been marked with __attribute__((tainted_args)).  */
    2339              : 
    2340              : class tainted_args_function_custom_event : public custom_event
    2341              : {
    2342              : public:
    2343          109 :   tainted_args_function_custom_event (const event_loc_info &loc_info)
    2344          109 :   : custom_event (loc_info),
    2345          109 :     m_fndecl (loc_info.m_fndecl)
    2346              :   {
    2347              :   }
    2348              : 
    2349              :   void
    2350          218 :   print_desc (pretty_printer &pp) const final override
    2351              :   {
    2352          218 :     pp_printf (&pp,
    2353              :                "function %qE marked with %<__attribute__((tainted_args))%>",
    2354          218 :                m_fndecl);
    2355          218 :   }
    2356              : 
    2357              : private:
    2358              :   tree m_fndecl;
    2359              : };
    2360              : 
    2361              : /* Custom exploded_edge info for top-level calls to a function
    2362              :    marked with __attribute__((tainted_args)).  */
    2363              : 
    2364              : class tainted_args_function_info : public custom_edge_info
    2365              : {
    2366              : public:
    2367          174 :   tainted_args_function_info (tree fndecl)
    2368          174 :   : m_fndecl (fndecl)
    2369              :   {}
    2370              : 
    2371            0 :   void print (pretty_printer *pp) const final override
    2372              :   {
    2373            0 :     pp_string (pp, "call to tainted_args function");
    2374            0 :   };
    2375              : 
    2376          274 :   bool update_model (region_model *model,
    2377              :                      const exploded_edge *eedge,
    2378              :                      region_model_context *ctxt) const final override
    2379              :   {
    2380          274 :     function *fun = eedge->m_dest->get_function ();
    2381          274 :     gcc_assert (fun);
    2382          274 :     model->push_frame (*fun, nullptr, nullptr, ctxt);
    2383          274 :     return true;
    2384              :   }
    2385              : 
    2386          109 :   void add_events_to_path (checker_path *emission_path,
    2387              :                            const exploded_edge &,
    2388              :                            pending_diagnostic &,
    2389              :                            const state_transition *) const final override
    2390              :   {
    2391          109 :     emission_path->add_event
    2392          109 :       (std::make_unique<tainted_args_function_custom_event>
    2393          109 :          (event_loc_info (DECL_SOURCE_LOCATION (m_fndecl), m_fndecl, 0)));
    2394          109 :   }
    2395              : 
    2396              : private:
    2397              :   tree m_fndecl;
    2398              : };
    2399              : 
    2400              : /* Ensure that there is an exploded_node representing an external call to
    2401              :    FUN, adding it to the worklist if creating it.
    2402              : 
    2403              :    Add an edge from the origin exploded_node to the function entrypoint
    2404              :    exploded_node.
    2405              : 
    2406              :    Return the exploded_node for the entrypoint to the function.  */
    2407              : 
    2408              : exploded_node *
    2409        10936 : exploded_graph::add_function_entry (const function &fun)
    2410              : {
    2411        10936 :   gcc_assert (gimple_has_body_p (fun.decl));
    2412              : 
    2413              :   /* Be idempotent.  */
    2414        10936 :   function *key = const_cast<function *> (&fun);
    2415        10936 :   if (m_functions_with_enodes.contains (key))
    2416              :     {
    2417          294 :       logger * const logger = get_logger ();
    2418          294 :        if (logger)
    2419            0 :         logger->log ("entrypoint for %qE already exists", fun.decl);
    2420          294 :       return nullptr;
    2421              :     }
    2422              : 
    2423        10642 :   program_point point
    2424        10642 :     = program_point::from_function_entry (*m_ext_state.get_model_manager (),
    2425              :                                           m_sg, fun);
    2426        10642 :   program_state state (m_ext_state);
    2427        10642 :   state.push_frame (m_ext_state, fun);
    2428              : 
    2429        10642 :   std::unique_ptr<custom_edge_info> edge_info = nullptr;
    2430              : 
    2431        10642 :   if (lookup_attribute ("tainted_args", DECL_ATTRIBUTES (fun.decl)))
    2432              :     {
    2433          174 :       if (mark_params_as_tainted (&state, fun.decl, m_ext_state))
    2434          174 :         edge_info = std::make_unique<tainted_args_function_info> (fun.decl);
    2435              :     }
    2436              : 
    2437        10642 :   if (!state.m_valid)
    2438              :     return nullptr;
    2439              : 
    2440        10642 :   exploded_node *enode = get_or_create_node (point, state, nullptr);
    2441        10642 :   if (!enode)
    2442              :     return nullptr;
    2443              : 
    2444        10634 :   add_edge (m_origin, enode, nullptr, false, std::move (edge_info));
    2445              : 
    2446        10634 :   m_functions_with_enodes.add (key);
    2447              : 
    2448        10634 :   return enode;
    2449        10642 : }
    2450              : 
    2451              : /* Get or create an exploded_node for (POINT, STATE).
    2452              :    If a new node is created and ADD_TO_WORKLIST is true,
    2453              :    it is added to the worklist.
    2454              : 
    2455              :    Use ENODE_FOR_DIAG, a pre-existing enode, for any diagnostics
    2456              :    that need to be emitted (e.g. when purging state *before* we have
    2457              :    a new enode).  */
    2458              : 
    2459              : exploded_node *
    2460       420624 : exploded_graph::get_or_create_node (const program_point &point,
    2461              :                                     const program_state &state,
    2462              :                                     exploded_node *enode_for_diag,
    2463              :                                     bool add_to_worklist)
    2464              : {
    2465       420624 :   logger * const logger = get_logger ();
    2466       420624 :   LOG_FUNC (logger);
    2467       420624 :   if (logger)
    2468              :     {
    2469          214 :       format f (false);
    2470          214 :       pretty_printer *pp = logger->get_printer ();
    2471          214 :       logger->start_log_line ();
    2472          214 :       pp_string (pp, "point: ");
    2473          214 :       point.print (pp, f);
    2474          214 :       logger->end_log_line ();
    2475          214 :       logger->start_log_line ();
    2476          214 :       pp_string (pp, "state: ");
    2477          214 :       state.dump_to_pp (m_ext_state, true, false, pp);
    2478          214 :       logger->end_log_line ();
    2479              :     }
    2480              : 
    2481              :   /* Stop exploring paths for which we don't know how to effectively
    2482              :      model the state.  */
    2483       420624 :   if (!state.m_valid)
    2484              :     {
    2485            0 :       if (logger)
    2486            0 :         logger->log ("invalid state; not creating node");
    2487            0 :       return nullptr;
    2488              :     }
    2489              : 
    2490       420624 :   if (point.get_call_string ().calc_recursion_depth ()
    2491       420624 :       > param_analyzer_max_recursion_depth)
    2492              :     {
    2493          603 :       if (logger)
    2494            0 :         logger->log ("rejecting node: recursion limit exceeded");
    2495          603 :       return nullptr;
    2496              :     }
    2497              : 
    2498       836568 :   auto_cfun sentinel (point.get_function ());
    2499              : 
    2500       420021 :   state.validate (get_ext_state ());
    2501              : 
    2502              :   //state.dump (get_ext_state ());
    2503              : 
    2504              :   /* Prune state to try to improve the chances of a cache hit,
    2505              :      avoiding generating redundant nodes.  */
    2506       420021 :   uncertainty_t uncertainty;
    2507       420021 :   program_state pruned_state
    2508       420021 :     = state.prune_for_point (*this, point, enode_for_diag, &uncertainty);
    2509              : 
    2510       420021 :   pruned_state.validate (get_ext_state ());
    2511              : 
    2512              :   //pruned_state.dump (get_ext_state ());
    2513              : 
    2514       420021 :   if (logger)
    2515              :     {
    2516          214 :       pretty_printer *pp = logger->get_printer ();
    2517          214 :       logger->start_log_line ();
    2518          214 :       pp_string (pp, "pruned_state: ");
    2519          214 :       pruned_state.dump_to_pp (m_ext_state, true, false, pp);
    2520          214 :       logger->end_log_line ();
    2521          214 :       pruned_state.m_region_model->dump_to_pp (logger->get_printer (), true,
    2522              :                                                 false);
    2523              :     }
    2524              : 
    2525       836568 :   stats *per_fn_stats = get_or_create_function_stats (point.get_function ());
    2526              : 
    2527       420021 :   stats *per_cs_stats
    2528       420021 :     = &get_or_create_per_call_string_data (point.get_call_string ())->m_stats;
    2529              : 
    2530       420021 :   point_and_state ps (point, pruned_state);
    2531       420021 :   ps.validate (m_ext_state);
    2532       420021 :   if (exploded_node **slot = m_point_and_state_to_node.get (&ps))
    2533              :     {
    2534              :       /* An exploded_node for PS already exists.  */
    2535         3333 :       if (logger)
    2536           11 :         logger->log ("reused EN: %i", (*slot)->m_index);
    2537         3333 :       m_global_stats.m_node_reuse_count++;
    2538         3333 :       per_fn_stats->m_node_reuse_count++;
    2539         3333 :       per_cs_stats->m_node_reuse_count++;
    2540         3333 :       return *slot;
    2541              :     }
    2542              : 
    2543       416688 :   per_program_point_data *per_point_data
    2544       416688 :     = get_or_create_per_program_point_data (point);
    2545              : 
    2546              :   /* Consider merging state with another enode at this program_point.  */
    2547       982883 :   if (flag_analyzer_state_merge && point.state_merge_at_p ())
    2548              :     {
    2549              :       exploded_node *existing_enode;
    2550              :       unsigned i;
    2551       388359 :       FOR_EACH_VEC_ELT (per_point_data->m_enodes, i, existing_enode)
    2552              :         {
    2553       235041 :           if (logger)
    2554          130 :             logger->log ("considering merging with existing EN: %i for point",
    2555          130 :                          existing_enode->m_index);
    2556       235041 :           gcc_assert (existing_enode->get_point () == point);
    2557       235041 :           const program_state &existing_state = existing_enode->get_state ();
    2558              : 
    2559              :           /* This merges successfully within the loop.  */
    2560              : 
    2561       235041 :           program_state merged_state (m_ext_state);
    2562       235041 :           if (pruned_state.can_merge_with_p (existing_state, m_ext_state, point,
    2563              :                                              &merged_state))
    2564              :             {
    2565        27702 :               merged_state.validate (m_ext_state);
    2566        27702 :               if (logger)
    2567           63 :                 logger->log ("merging new state with that of EN: %i",
    2568           63 :                              existing_enode->m_index);
    2569              : 
    2570              :               /* Try again for a cache hit.
    2571              :                  Whether we get one or not, merged_state's value_ids have no
    2572              :                  relationship to those of the input state, and thus to those
    2573              :                  of CHANGE, so we must purge any svalue_ids from *CHANGE.  */
    2574        27702 :               ps.set_state (merged_state);
    2575              : 
    2576        27702 :               if (exploded_node **slot = m_point_and_state_to_node.get (&ps))
    2577              :                 {
    2578              :                   /* An exploded_node for PS already exists.  */
    2579         1839 :                   if (logger)
    2580            1 :                     logger->log ("reused EN: %i", (*slot)->m_index);
    2581         1839 :                   m_global_stats.m_node_reuse_after_merge_count++;
    2582         1839 :                   per_fn_stats->m_node_reuse_after_merge_count++;
    2583         1839 :                   per_cs_stats->m_node_reuse_after_merge_count++;
    2584         1839 :                   return *slot;
    2585              :                 }
    2586              :             }
    2587              :           else
    2588       207339 :             if (logger)
    2589           67 :               logger->log ("not merging new state with that of EN: %i",
    2590           67 :                            existing_enode->m_index);
    2591       235041 :         }
    2592              :     }
    2593              : 
    2594              :   /* Impose a limit on the number of enodes per program point, and
    2595              :      simply stop if we exceed it.  */
    2596       414849 :   if ((int)per_point_data->m_enodes.length ()
    2597       414849 :       >= param_analyzer_max_enodes_per_program_point)
    2598              :     {
    2599         2199 :       pretty_printer pp;
    2600         2199 :       point.print (&pp, format (false));
    2601         2199 :       print_enode_indices (&pp, per_point_data->m_enodes);
    2602         2199 :       if (logger)
    2603            0 :         logger->log ("not creating enode; too many at program point: %s",
    2604              :                      pp_formatted_text (&pp));
    2605         4394 :       warning_at (point.get_location (), OPT_Wanalyzer_too_complex,
    2606              :                   "terminating analysis for this program point: %s",
    2607              :                   pp_formatted_text (&pp));
    2608         2199 :       per_point_data->m_excess_enodes++;
    2609         2199 :       return nullptr;
    2610         2199 :     }
    2611              : 
    2612       412650 :   ps.validate (m_ext_state);
    2613              : 
    2614              :   /* An exploded_node for "ps" doesn't already exist; create one.  */
    2615       821830 :   exploded_node *node = new exploded_node (ps, m_nodes.length ());
    2616       412650 :   add_node (node);
    2617       412650 :   m_point_and_state_to_node.put (node->get_ps_key (), node);
    2618              : 
    2619              :   /* Update per-program_point data.  */
    2620       412650 :   per_point_data->m_enodes.safe_push (node);
    2621              : 
    2622       412650 :   m_global_stats.m_num_nodes++;
    2623       412650 :   per_fn_stats->m_num_nodes++;
    2624       412650 :   per_cs_stats->m_num_nodes++;
    2625              : 
    2626       412650 :   if (logger)
    2627              :     {
    2628          202 :       format f (false);
    2629          202 :       pretty_printer *pp = logger->get_printer ();
    2630          202 :       logger->log ("created EN: %i", node->m_index);
    2631          202 :       logger->start_log_line ();
    2632          202 :       pp_string (pp, "point: ");
    2633          202 :       point.print (pp, f);
    2634          202 :       logger->end_log_line ();
    2635          202 :       logger->start_log_line ();
    2636          202 :       pp_string (pp, "state: ");
    2637          202 :       ps.get_state ().dump_to_pp (m_ext_state, true, false, pp);
    2638          202 :       logger->end_log_line ();
    2639              :     }
    2640              : 
    2641              :   /* Add the new node to the worlist.  */
    2642       412650 :   if (add_to_worklist)
    2643       405189 :     m_worklist.add_node (node);
    2644              :   else
    2645         7461 :     node->set_status (exploded_node::status::special);
    2646              :   return node;
    2647       840645 : }
    2648              : 
    2649              : /* Add an exploded_edge from SRC to DEST, recording its association
    2650              :    with SEDGE (which may be NULL), and, if non-NULL, taking ownership
    2651              :    of CUSTOM_INFO.  COULD_DO_WORK is used for detecting infinite loops.
    2652              :    Return the newly-created eedge.  */
    2653              : 
    2654              : exploded_edge *
    2655       426907 : exploded_graph::add_edge (exploded_node *src, exploded_node *dest,
    2656              :                           const superedge *sedge, bool could_do_work,
    2657              :                           std::unique_ptr<custom_edge_info> custom_info)
    2658              : {
    2659       426907 :   if (get_logger ())
    2660          213 :     get_logger ()->log ("creating edge EN: %i -> EN: %i",
    2661          213 :                         src->m_index, dest->m_index);
    2662       426907 :   exploded_edge *e
    2663              :     = new exploded_edge (src, dest, sedge, could_do_work,
    2664       426907 :                          std::move (custom_info));
    2665       426907 :   digraph<eg_traits>::add_edge (e);
    2666       426907 :   return e;
    2667              : }
    2668              : 
    2669              : /* Ensure that this graph has per-program_point-data for POINT;
    2670              :    borrow a pointer to it.  */
    2671              : 
    2672              : per_program_point_data *
    2673       416688 : exploded_graph::
    2674              : get_or_create_per_program_point_data (const program_point &point)
    2675              : {
    2676       416688 :   if (per_program_point_data **slot = m_per_point_data.get (&point))
    2677       165305 :     return *slot;
    2678              : 
    2679       251383 :   per_program_point_data *per_point_data = new per_program_point_data (point);
    2680       251383 :   m_per_point_data.put (&per_point_data->m_key, per_point_data);
    2681       251383 :   return per_point_data;
    2682              : }
    2683              : 
    2684              : /* Get this graph's per-program-point-data for POINT if there is any,
    2685              :    otherwise nullptr.  */
    2686              : 
    2687              : per_program_point_data *
    2688            0 : exploded_graph::get_per_program_point_data (const program_point &point) const
    2689              : {
    2690            0 :   if (per_program_point_data **slot
    2691            0 :       = const_cast <point_map_t &> (m_per_point_data).get (&point))
    2692            0 :     return *slot;
    2693              : 
    2694              :   return nullptr;
    2695              : }
    2696              : 
    2697              : /* Ensure that this graph has per-call_string-data for CS;
    2698              :    borrow a pointer to it.  */
    2699              : 
    2700              : per_call_string_data *
    2701       420021 : exploded_graph::get_or_create_per_call_string_data (const call_string &cs)
    2702              : {
    2703       420021 :   if (per_call_string_data **slot = m_per_call_string_data.get (&cs))
    2704       410077 :     return *slot;
    2705              : 
    2706        19884 :   per_call_string_data *data = new per_call_string_data (cs, m_sg.m_nodes.length ());
    2707         9944 :   m_per_call_string_data.put (&data->m_key,
    2708              :                               data);
    2709         9944 :   return data;
    2710              : }
    2711              : 
    2712              : /* Ensure that this graph has per-function-data for FUN;
    2713              :    borrow a pointer to it.  */
    2714              : 
    2715              : per_function_data *
    2716          631 : exploded_graph::get_or_create_per_function_data (function *fun)
    2717              : {
    2718          631 :   if (per_function_data **slot = m_per_function_data.get (fun))
    2719          264 :     return *slot;
    2720              : 
    2721          367 :   per_function_data *data = new per_function_data ();
    2722          367 :   m_per_function_data.put (fun, data);
    2723          367 :   return data;
    2724              : }
    2725              : 
    2726              : /* Get this graph's per-function-data for FUN if there is any,
    2727              :    otherwise nullptr.  */
    2728              : 
    2729              : per_function_data *
    2730          789 : exploded_graph::get_per_function_data (function *fun) const
    2731              : {
    2732         1578 :   if (per_function_data **slot
    2733          789 :         = const_cast <per_function_data_t &> (m_per_function_data).get (fun))
    2734          751 :     return *slot;
    2735              : 
    2736              :   return nullptr;
    2737              : }
    2738              : 
    2739              : /* Return true if FUN should be traversed directly, rather than only as
    2740              :    called via other functions.  */
    2741              : 
    2742              : static bool
    2743        10792 : toplevel_function_p (const function &fun, logger *logger)
    2744              : {
    2745              :   /* Don't directly traverse into functions that have an "__analyzer_"
    2746              :      prefix.  Doing so is useful for the analyzer test suite, allowing
    2747              :      us to have functions that are called in traversals, but not directly
    2748              :      explored, thus testing how the analyzer handles calls and returns.
    2749              :      With this, we can have DejaGnu directives that cover just the case
    2750              :      of where a function is called by another function, without generating
    2751              :      excess messages from the case of the first function being traversed
    2752              :      directly.  */
    2753              : #define ANALYZER_PREFIX "__analyzer_"
    2754        10792 :   if (!strncmp (IDENTIFIER_POINTER (DECL_NAME (fun.decl)), ANALYZER_PREFIX,
    2755              :                 strlen (ANALYZER_PREFIX)))
    2756              :     {
    2757          150 :       if (logger)
    2758            0 :         logger->log ("not traversing %qE (starts with %qs)",
    2759              :                      fun.decl, ANALYZER_PREFIX);
    2760          150 :       return false;
    2761              :     }
    2762              : 
    2763        10642 :   if (logger)
    2764            6 :     logger->log ("traversing %qE (all checks passed)", fun.decl);
    2765              : 
    2766              :   return true;
    2767              : }
    2768              : 
    2769              : /* Custom event for use by tainted_call_info when a callback field has been
    2770              :    marked with __attribute__((tainted_args)), for labelling the field.  */
    2771              : 
    2772              : class tainted_args_field_custom_event : public custom_event
    2773              : {
    2774              : public:
    2775            4 :   tainted_args_field_custom_event (tree field)
    2776            4 :   : custom_event (event_loc_info (DECL_SOURCE_LOCATION (field), NULL_TREE, 0)),
    2777            4 :     m_field (field)
    2778              :   {
    2779            4 :   }
    2780              : 
    2781            8 :   void print_desc (pretty_printer &pp) const final override
    2782              :   {
    2783            8 :     pp_printf (&pp,
    2784              :                "field %qE of %qT"
    2785              :                " is marked with %<__attribute__((tainted_args))%>",
    2786            8 :                m_field, DECL_CONTEXT (m_field));
    2787            8 :   }
    2788              : 
    2789              : private:
    2790              :   tree m_field;
    2791              : };
    2792              : 
    2793              : /* Custom event for use by tainted_call_info when a callback field has been
    2794              :    marked with __attribute__((tainted_args)), for labelling the function used
    2795              :    in that callback.  */
    2796              : 
    2797              : class tainted_args_callback_custom_event : public custom_event
    2798              : {
    2799              : public:
    2800            4 :   tainted_args_callback_custom_event (const event_loc_info &loc_info,
    2801              :                                       tree field)
    2802            4 :   : custom_event (loc_info),
    2803            4 :     m_field (field)
    2804              :   {
    2805              :   }
    2806              : 
    2807            8 :   void print_desc (pretty_printer &pp) const final override
    2808              :   {
    2809            8 :     pp_printf (&pp,
    2810              :                "function %qE used as initializer for field %qE"
    2811              :                " marked with %<__attribute__((tainted_args))%>",
    2812            8 :                get_fndecl (), m_field);
    2813            8 :   }
    2814              : 
    2815              : private:
    2816              :   tree m_field;
    2817              : };
    2818              : 
    2819              : /* Custom edge info for use when adding a function used by a callback field
    2820              :    marked with '__attribute__((tainted_args))'.   */
    2821              : 
    2822              : class tainted_args_call_info : public custom_edge_info
    2823              : {
    2824              : public:
    2825           10 :   tainted_args_call_info (tree field, tree fndecl, location_t loc)
    2826           10 :   : m_field (field), m_fndecl (fndecl), m_loc (loc)
    2827              :   {}
    2828              : 
    2829            0 :   void print (pretty_printer *pp) const final override
    2830              :   {
    2831            0 :     pp_string (pp, "call to tainted field");
    2832            0 :   };
    2833              : 
    2834           13 :   bool update_model (region_model *model,
    2835              :                      const exploded_edge *eedge,
    2836              :                      region_model_context *) const final override
    2837              :   {
    2838           26 :     model->push_frame (*eedge->m_dest->get_function (),
    2839              :                        nullptr, nullptr, nullptr);
    2840           13 :     return true;
    2841              :   }
    2842              : 
    2843            4 :   void add_events_to_path (checker_path *emission_path,
    2844              :                            const exploded_edge &,
    2845              :                            pending_diagnostic &,
    2846              :                            const state_transition *) const final override
    2847              :   {
    2848              :     /* Show the field in the struct declaration, e.g.
    2849              :        "(1) field 'store' is marked with '__attribute__((tainted_args))'"  */
    2850            4 :     emission_path->add_event
    2851            4 :       (std::make_unique<tainted_args_field_custom_event> (m_field));
    2852              : 
    2853              :     /* Show the callback in the initializer
    2854              :        e.g.
    2855              :        "(2) function 'gadget_dev_desc_UDC_store' used as initializer
    2856              :        for field 'store' marked with '__attribute__((tainted_args))'".  */
    2857            4 :     emission_path->add_event
    2858            4 :       (std::make_unique<tainted_args_callback_custom_event>
    2859            4 :          (event_loc_info (m_loc, m_fndecl, 0),
    2860              :           m_field));
    2861            4 :   }
    2862              : 
    2863              : private:
    2864              :   tree m_field;
    2865              :   tree m_fndecl;
    2866              :   location_t m_loc;
    2867              : };
    2868              : 
    2869              : /* Given an initializer at LOC for FIELD marked with
    2870              :    '__attribute__((tainted_args))' initialized with FNDECL, add an
    2871              :    entrypoint to FNDECL to EG (and to its worklist) where the params to
    2872              :    FNDECL are marked as tainted.  */
    2873              : 
    2874              : static void
    2875           10 : add_tainted_args_callback (exploded_graph *eg, tree field, tree fndecl,
    2876              :                            location_t loc)
    2877              : {
    2878           10 :   logger *logger = eg->get_logger ();
    2879              : 
    2880           10 :   LOG_SCOPE (logger);
    2881              : 
    2882           10 :   if (!gimple_has_body_p (fndecl))
    2883              :     return;
    2884              : 
    2885           10 :   const extrinsic_state &ext_state = eg->get_ext_state ();
    2886              : 
    2887           10 :   function *fun = DECL_STRUCT_FUNCTION (fndecl);
    2888           10 :   gcc_assert (fun);
    2889              : 
    2890           10 :   program_point point
    2891           10 :     = program_point::from_function_entry (*ext_state.get_model_manager (),
    2892              :                                           eg->get_supergraph (), *fun);
    2893           10 :   program_state state (ext_state);
    2894           10 :   state.push_frame (ext_state, *fun);
    2895              : 
    2896           10 :   if (!mark_params_as_tainted (&state, fndecl, ext_state))
    2897              :     return;
    2898              : 
    2899           10 :   if (!state.m_valid)
    2900              :     return;
    2901              : 
    2902           10 :   exploded_node *enode = eg->get_or_create_node (point, state, nullptr);
    2903           10 :   if (logger)
    2904              :     {
    2905            0 :       if (enode)
    2906            0 :         logger->log ("created EN %i for tainted_args %qE entrypoint",
    2907            0 :                      enode->m_index, fndecl);
    2908              :       else
    2909              :         {
    2910            0 :           logger->log ("did not create enode for tainted_args %qE entrypoint",
    2911              :                        fndecl);
    2912            0 :           return;
    2913              :         }
    2914              :     }
    2915              : 
    2916           10 :   eg->add_edge (eg->get_origin (), enode, nullptr, false,
    2917           10 :                 std::make_unique<tainted_args_call_info> (field, fndecl, loc));
    2918           10 : }
    2919              : 
    2920              : /* Callback for walk_tree for finding callbacks within initializers;
    2921              :    ensure that any callback initializer where the corresponding field is
    2922              :    marked with '__attribute__((tainted_args))' is treated as an entrypoint
    2923              :    to the analysis, special-casing that the inputs to the callback are
    2924              :    untrustworthy.  */
    2925              : 
    2926              : static tree
    2927        27625 : add_any_callbacks (tree *tp, int *, void *data)
    2928              : {
    2929        27625 :   exploded_graph *eg = (exploded_graph *)data;
    2930        27625 :   if (TREE_CODE (*tp) == CONSTRUCTOR)
    2931              :     {
    2932              :       /* Find fields with the "tainted_args" attribute.
    2933              :          walk_tree only walks the values, not the index values;
    2934              :          look at the index values.  */
    2935              :       unsigned HOST_WIDE_INT idx;
    2936              :       constructor_elt *ce;
    2937              : 
    2938        18484 :       for (idx = 0; vec_safe_iterate (CONSTRUCTOR_ELTS (*tp), idx, &ce);
    2939              :            idx++)
    2940        13732 :         if (ce->index && TREE_CODE (ce->index) == FIELD_DECL)
    2941        12774 :           if (lookup_attribute ("tainted_args", DECL_ATTRIBUTES (ce->index)))
    2942              :             {
    2943           10 :               tree value = ce->value;
    2944           10 :               if (TREE_CODE (value) == ADDR_EXPR
    2945           10 :                   && TREE_CODE (TREE_OPERAND (value, 0)) == FUNCTION_DECL)
    2946           20 :                 add_tainted_args_callback (eg, ce->index,
    2947           10 :                                            TREE_OPERAND (value, 0),
    2948           10 :                                            EXPR_LOCATION (value));
    2949              :             }
    2950              :     }
    2951              : 
    2952        27625 :   return NULL_TREE;
    2953              : }
    2954              : 
    2955              : /* Add initial nodes to EG, with entrypoints for externally-callable
    2956              :    functions.  */
    2957              : 
    2958              : void
    2959         3474 : exploded_graph::build_initial_worklist ()
    2960              : {
    2961         3474 :   logger * const logger = get_logger ();
    2962         3474 :   LOG_SCOPE (logger);
    2963              : 
    2964         3474 :   cgraph_node *node;
    2965        14266 :   FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
    2966              :   {
    2967        10792 :     function *fun = node->get_fun ();
    2968        10792 :     gcc_assert (fun);
    2969        10792 :     if (!toplevel_function_p (*fun, logger))
    2970          150 :       continue;
    2971        10642 :     exploded_node *enode = add_function_entry (*fun);
    2972        10642 :     if (logger)
    2973              :       {
    2974            6 :         if (enode)
    2975            6 :           logger->log ("created EN %i for %qE entrypoint",
    2976            6 :                        enode->m_index, fun->decl);
    2977              :         else
    2978            0 :           logger->log ("did not create enode for %qE entrypoint", fun->decl);
    2979              :       }
    2980              :   }
    2981              : 
    2982              :   /* Find callbacks that are reachable from global initializers.  */
    2983         3474 :   varpool_node *vpnode;
    2984         9538 :   FOR_EACH_VARIABLE (vpnode)
    2985              :     {
    2986         6064 :       tree decl = vpnode->decl;
    2987         6064 :       tree init = DECL_INITIAL (decl);
    2988         6064 :       if (!init)
    2989         1293 :         continue;
    2990         4771 :       walk_tree (&init, add_any_callbacks, this, nullptr);
    2991              :     }
    2992         3474 : }
    2993              : 
    2994              : /* The main loop of the analysis.
    2995              :    Take freshly-created exploded_nodes from the worklist, calling
    2996              :    process_node on them to explore the <point, state> graph.
    2997              :    Add edges to their successors, potentially creating new successors
    2998              :    (which are also added to the worklist).  */
    2999              : 
    3000              : void
    3001         3474 : exploded_graph::process_worklist ()
    3002              : {
    3003         3474 :   logger * const logger = get_logger ();
    3004         3474 :   LOG_SCOPE (logger);
    3005         3474 :   auto_timevar tv (TV_ANALYZER_WORKLIST);
    3006              : 
    3007       390691 :   while (m_worklist.length () > 0)
    3008              :     {
    3009       383820 :       exploded_node *node = m_worklist.take_next ();
    3010       383820 :       gcc_assert (node->get_status () == exploded_node::status::worklist);
    3011              : 
    3012       383820 :       if (logger)
    3013          194 :         logger->log ("next to process: EN: %i", node->m_index);
    3014              : 
    3015              :       /* If we have a run of nodes at the same point, try merging and
    3016              :          processing them together, rather than pairwise or individually.  */
    3017       383820 :       if (flag_analyzer_state_merge
    3018       383820 :           && node->get_point ().state_merge_at_p ())
    3019       132373 :         if (maybe_process_run_of_enodes (node))
    3020         7439 :           goto handle_limit;
    3021              : 
    3022              :       /* Avoid exponential explosions of nodes by attempting to merge
    3023              :          nodes that are at the same program point and which have
    3024              :          sufficiently similar state.  */
    3025       376381 :       if (flag_analyzer_state_merge && node != m_origin)
    3026       369303 :         if (exploded_node *node_2 = m_worklist.peek_next ())
    3027              :           {
    3028       318013 :             gcc_assert (node_2->get_status ()
    3029              :                         == exploded_node::status::worklist);
    3030       318013 :             gcc_assert (node != node_2);
    3031              : 
    3032       318013 :             if (logger)
    3033          148 :               logger->log ("peek worklist: EN: %i", node_2->m_index);
    3034              : 
    3035       318013 :             if (node->get_point () == node_2->get_point ())
    3036              :               {
    3037        65386 :                 const program_point &point = node->get_point ();
    3038        65386 :                 if (logger)
    3039              :                   {
    3040           29 :                     format f (false);
    3041           29 :                     pretty_printer *pp = logger->get_printer ();
    3042           29 :                     logger->start_log_line ();
    3043           29 :                     logger->log_partial
    3044           29 :                       ("got potential merge EN: %i and EN: %i at ",
    3045           29 :                        node->m_index, node_2->m_index);
    3046           29 :                     point.print (pp, f);
    3047           29 :                     logger->end_log_line ();
    3048              :                   }
    3049        65386 :                 const program_state &state = node->get_state ();
    3050        65386 :                 const program_state &state_2 = node_2->get_state ();
    3051              : 
    3052              :                 /* They shouldn't be equal, or we wouldn't have two
    3053              :                    separate nodes.  */
    3054        65386 :                 gcc_assert (state != state_2);
    3055              : 
    3056        65386 :                 program_state merged_state (m_ext_state);
    3057        65386 :                 if (state.can_merge_with_p (state_2, m_ext_state,
    3058              :                                             point, &merged_state))
    3059              :                   {
    3060         2690 :                     if (logger)
    3061            4 :                       logger->log ("merging EN: %i and EN: %i",
    3062            4 :                                    node->m_index, node_2->m_index);
    3063              : 
    3064         2690 :                     if (merged_state == state)
    3065              :                       {
    3066              :                         /* Then merge node_2 into node by adding an edge.  */
    3067          447 :                         add_edge (node_2, node, nullptr, false);
    3068              : 
    3069              :                         /* Remove node_2 from the worklist.  */
    3070          447 :                         m_worklist.take_next ();
    3071          447 :                         node_2->set_status (exploded_node::status::merger);
    3072              : 
    3073              :                         /* Continue processing "node" below.  */
    3074              :                       }
    3075         2243 :                     else if (merged_state == state_2)
    3076              :                       {
    3077              :                         /* Then merge node into node_2, and leave node_2
    3078              :                            in the worklist, to be processed on the next
    3079              :                            iteration.  */
    3080         1296 :                         add_edge (node, node_2, nullptr, false);
    3081         1296 :                         node->set_status (exploded_node::status::merger);
    3082         1296 :                         continue;
    3083              :                       }
    3084              :                     else
    3085              :                       {
    3086              :                         /* We have a merged state that differs from
    3087              :                            both state and state_2.  */
    3088              : 
    3089              :                         /* Remove node_2 from the worklist.  */
    3090          947 :                         m_worklist.take_next ();
    3091              : 
    3092              :                         /* Create (or get) an exploded node for the merged
    3093              :                            states, adding to the worklist.  */
    3094          947 :                         exploded_node *merged_enode
    3095          947 :                           = get_or_create_node (node->get_point (),
    3096              :                                                 merged_state, node);
    3097          947 :                         if (merged_enode == nullptr)
    3098           85 :                           continue;
    3099              : 
    3100          862 :                         if (logger)
    3101            3 :                           logger->log ("merged EN: %i and EN: %i into EN: %i",
    3102            3 :                                        node->m_index, node_2->m_index,
    3103            3 :                                        merged_enode->m_index);
    3104              : 
    3105              :                         /* "node" and "node_2" have both now been removed
    3106              :                            from the worklist; we should not process them.
    3107              : 
    3108              :                            "merged_enode" may be a new node; if so it will be
    3109              :                            processed in a subsequent iteration.
    3110              :                            Alternatively, "merged_enode" could be an existing
    3111              :                            node; one way the latter can
    3112              :                            happen is if we end up merging a succession of
    3113              :                            similar nodes into one.  */
    3114              : 
    3115              :                         /* If merged_node is one of the two we were merging,
    3116              :                            add it back to the worklist to ensure it gets
    3117              :                            processed.
    3118              : 
    3119              :                            Add edges from the merged nodes to it (but not a
    3120              :                            self-edge).  */
    3121          862 :                         if (merged_enode == node)
    3122            0 :                           m_worklist.add_node (merged_enode);
    3123              :                         else
    3124              :                           {
    3125          862 :                             add_edge (node, merged_enode, nullptr, false);
    3126          862 :                             node->set_status (exploded_node::status::merger);
    3127              :                           }
    3128              : 
    3129          862 :                         if (merged_enode == node_2)
    3130            6 :                           m_worklist.add_node (merged_enode);
    3131              :                         else
    3132              :                           {
    3133          856 :                             add_edge (node_2, merged_enode, nullptr, false);
    3134          856 :                             node_2->set_status (exploded_node::status::merger);
    3135              :                           }
    3136              : 
    3137          862 :                         continue;
    3138          862 :                       }
    3139              :                   }
    3140              : 
    3141              :                 /* TODO: should we attempt more than two nodes,
    3142              :                    or just do pairs of nodes?  (and hope that we get
    3143              :                    a cascade of mergers).  */
    3144        65386 :               }
    3145              :         }
    3146              : 
    3147       374138 :       process_node (node);
    3148              : 
    3149       381577 :     handle_limit:
    3150              :       /* Impose a hard limit on the number of exploded nodes, to ensure
    3151              :          that the analysis terminates in the face of pathological state
    3152              :          explosion (or bugs).  */
    3153       381577 :       const int limit
    3154              :         = (// Per-supernode limit:
    3155       381577 :            (m_sg.num_nodes () * param_analyzer_supernode_explosion_factor)
    3156              :            // Allow one for the "origin" enode:
    3157       381577 :            + 1);
    3158       381577 :       if (m_global_stats.m_num_nodes > limit)
    3159              :         {
    3160           77 :           if (logger)
    3161            0 :             logger->log ("bailing out; too many nodes");
    3162          231 :           warning_at (node->get_point ().get_location (),
    3163           77 :                       OPT_Wanalyzer_too_complex,
    3164              :                       "analysis bailed out early"
    3165              :                       " (%i enodes for %i snodes)",
    3166              :                       m_nodes.length (),
    3167           77 :                       m_sg.num_nodes ());
    3168           77 :           return;
    3169              :         }
    3170              :     }
    3171         3474 : }
    3172              : 
    3173              : /* Attempt to process a consecutive run of sufficiently-similar nodes in
    3174              :    the worklist at a point flagged with state_merge_at_p (having already
    3175              :    popped ENODE from the head of the worklist).
    3176              : 
    3177              :    If we have a consecutive run of enodes in the worklist all of which have
    3178              :    a single out-edge where all these out-edges are supports_bulk_merge_p and
    3179              :    all have the same successor snode and call string, then
    3180              :    process them all together, setting their status to status::bulk_merged,
    3181              :    and return true.
    3182              :    Otherwise, return false, in which case ENODE must be processed in the
    3183              :    normal way.
    3184              : 
    3185              :    When processing them all together, generate successor states based
    3186              :    on the edge op update_state_for_bulk_merger, and then attempt to merge
    3187              :    these states into a minimal set of merged successor states, partitioning
    3188              :    the inputs by merged successor state.
    3189              : 
    3190              :    Create new exploded nodes for all of the merged states, and add edges
    3191              :    connecting the input enodes to the corresponding merger exploded nodes.
    3192              : 
    3193              :    We hope we have a much smaller number of merged successor states
    3194              :    compared to the number of input enodes - ideally just one,
    3195              :    if all successor states can be merged.
    3196              : 
    3197              :    Processing and merging many together as one operation rather than as
    3198              :    pairs avoids scaling issues where per-pair mergers could bloat the
    3199              :    graph with merger nodes (especially so after switch statements).  */
    3200              : 
    3201              : bool
    3202       132373 : exploded_graph::
    3203              : maybe_process_run_of_enodes (exploded_node *enode)
    3204              : {
    3205              :   /* A struct for tracking per-input state.  */
    3206        26382 :   struct item
    3207              :   {
    3208        26382 :     item (exploded_node *input_enode)
    3209        26382 :     : m_input_enode (input_enode),
    3210        26382 :       m_processed_state (input_enode->get_state ()),
    3211        26382 :       m_merger_idx (-1)
    3212              :     {}
    3213              : 
    3214              :     exploded_node *m_input_enode;
    3215              :     program_state m_processed_state;
    3216              :     int m_merger_idx;
    3217              :   };
    3218              : 
    3219       132373 :   gcc_assert (enode->get_status () == exploded_node::status::worklist);
    3220              : 
    3221       132373 :   const program_point &src_point = enode->get_point ();
    3222       132373 :   const supernode *src_snode = src_point.get_supernode ();
    3223              : 
    3224       132373 :   logger * const logger = get_logger ();
    3225       132373 :   LOG_SCOPE (logger);
    3226              : 
    3227       199465 :   if (src_snode->m_succs.length () != 1)
    3228              :     return false;
    3229              : 
    3230       105710 :   auto sedge = src_snode->m_succs[0];
    3231              : 
    3232       105710 :   if (!sedge->supports_bulk_merge_p ())
    3233              :     return false;
    3234              : 
    3235        38618 :   const supernode *dst_snode = src_snode->m_succs[0]->m_dest;
    3236              : 
    3237              :   /* Find a run of enodes in the worklist that all have single out-sedges
    3238              :      go to the same supernode, all of which are bulk-mergeable (i.e. have
    3239              :      a simple single intraprocedural outcome).  */
    3240        38618 :   auto_vec <exploded_node *> enodes;
    3241        38618 :   enodes.safe_push (enode);
    3242        57561 :   while (exploded_node *enode_2 = m_worklist.peek_next ())
    3243              :     {
    3244        51542 :       gcc_assert (enode_2->get_status ()
    3245              :                   == exploded_node::status::worklist);
    3246        51542 :       gcc_assert (enode_2->m_succs.length () == 0);
    3247              : 
    3248        51542 :       const program_point &point_2 = enode_2->get_point ();
    3249        51542 :       const supernode *src_snode_2 = point_2.get_supernode ();
    3250              : 
    3251        51542 :       if (src_snode_2->m_succs.length () != 1)
    3252              :         break;
    3253        50557 :       auto sedge_2 = src_snode_2->m_succs[0];
    3254        50557 :       if (sedge_2->m_dest != dst_snode)
    3255              :         break;
    3256        20365 :       if (&point_2.get_call_string () != &src_point.get_call_string ())
    3257              :         break;
    3258        18943 :       if (!sedge_2->supports_bulk_merge_p ())
    3259              :         break;
    3260              : 
    3261        18943 :       enodes.safe_push (enode_2);
    3262        18943 :       m_worklist.take_next ();
    3263        18943 :     }
    3264              : 
    3265              :   /* If the only node is ENODE, then give up.  */
    3266        38618 :   if (enodes.length () == 1)
    3267              :     return false;
    3268              : 
    3269         7439 :   if (logger)
    3270            4 :     logger->log ("got run of %i bulk-mergable enodes going to SN: %i",
    3271            4 :                  enodes.length (), dst_snode->m_id);
    3272              : 
    3273              :   /* All of these enodes have a shared intraprocedural successor point
    3274              :      (even if they were for different in-edges).  */
    3275         7439 :   program_point next_point (sedge->m_dest,
    3276         7439 :                             src_point.get_call_string ());
    3277              : 
    3278              :   /* Calculate the successor state for each enode in enodes.  */
    3279        14878 :   auto_delete_vec<item> items (enodes.length ());
    3280         7439 :   unsigned i;
    3281         7439 :   exploded_node *iter_enode;
    3282        33821 :   FOR_EACH_VEC_ELT (enodes, i, iter_enode)
    3283              :     {
    3284        26382 :       item *it = new item (iter_enode);
    3285        26382 :       items.quick_push (it);
    3286        26382 :       const program_state &state = iter_enode->get_state ();
    3287        26382 :       program_state *next_state = &it->m_processed_state;
    3288        26382 :       next_state->validate (m_ext_state);
    3289        26382 :       gcc_assert (iter_enode->get_supernode ()->m_succs.length () == 1);
    3290        26382 :       const superedge *iter_sedge = iter_enode->get_supernode ()->m_succs[0];
    3291        26382 :       if (auto op = iter_sedge->get_op ())
    3292          414 :         op->update_state_for_bulk_merger (state, *next_state);
    3293        26382 :       next_state->validate (m_ext_state);
    3294              :     }
    3295              : 
    3296              :   /* Attempt to partition the items into a set of merged states.
    3297              :      We hope we have a much smaller number of merged states
    3298              :      compared to the number of input enodes - ideally just one,
    3299              :      if all can be merged.  */
    3300         7439 :   auto_delete_vec <program_state> merged_states;
    3301         7439 :   auto_vec<item *> first_item_for_each_merged_state;
    3302         7439 :   item *it;
    3303        33821 :   FOR_EACH_VEC_ELT (items, i, it)
    3304              :     {
    3305        26382 :       const program_state &it_state = it->m_processed_state;
    3306        26382 :       program_state *merged_state;
    3307        26382 :       unsigned iter_merger_idx;
    3308        65176 :       FOR_EACH_VEC_ELT (merged_states, iter_merger_idx, merged_state)
    3309              :         {
    3310        48783 :           merged_state->validate (m_ext_state);
    3311        48783 :           program_state merge (m_ext_state);
    3312        48783 :           if (it_state.can_merge_with_p (*merged_state, m_ext_state,
    3313              :                                          next_point, &merge))
    3314              :             {
    3315         9989 :               *merged_state = merge;
    3316         9989 :               merged_state->validate (m_ext_state);
    3317         9989 :               it->m_merger_idx = iter_merger_idx;
    3318         9989 :               if (logger)
    3319            1 :                 logger->log ("reusing merger state %i for item %i (EN: %i)",
    3320            1 :                              it->m_merger_idx, i, it->m_input_enode->m_index);
    3321         9989 :               goto got_merger;
    3322              :             }
    3323        48783 :         }
    3324              :       /* If it couldn't be merged with any existing merged_states,
    3325              :          create a new one.  */
    3326        16393 :       if (it->m_merger_idx == -1)
    3327              :         {
    3328        16393 :           it->m_merger_idx = merged_states.length ();
    3329        16393 :           merged_states.safe_push (new program_state (it_state));
    3330        16393 :           first_item_for_each_merged_state.safe_push (it);
    3331        16393 :           if (logger)
    3332            8 :             logger->log ("using new merger state %i for item %i (EN: %i)",
    3333            8 :                          it->m_merger_idx, i, it->m_input_enode->m_index);
    3334              :         }
    3335            0 :     got_merger:
    3336        26382 :       gcc_assert (it->m_merger_idx >= 0);
    3337        26382 :       gcc_assert ((unsigned)it->m_merger_idx < merged_states.length ());
    3338              :     }
    3339              : 
    3340              :   /* Create merger nodes.  */
    3341        22317 :   auto_vec<exploded_node *> next_enodes (merged_states.length ());
    3342         7439 :   program_state *merged_state;
    3343        23832 :   FOR_EACH_VEC_ELT (merged_states, i, merged_state)
    3344              :     {
    3345        16393 :       exploded_node *src_enode
    3346        16393 :         = first_item_for_each_merged_state[i]->m_input_enode;
    3347        16393 :       exploded_node *next
    3348        16393 :         = get_or_create_node (next_point, *merged_state, src_enode);
    3349              :       /* "next" could be nullptr; we handle that when adding the edges below.  */
    3350        16393 :       next_enodes.quick_push (next);
    3351        16393 :       if (logger)
    3352              :         {
    3353            8 :           if (next)
    3354            8 :             logger->log ("using EN: %i for merger state %i", next->m_index, i);
    3355              :           else
    3356            0 :             logger->log ("using NULL enode for merger state %i", i);
    3357              :         }
    3358              :     }
    3359              : 
    3360              :   /* Create edges from each input enode to the appropriate successor enode.
    3361              :      Update the status of the now-processed input enodes.  */
    3362        33821 :   FOR_EACH_VEC_ELT (items, i, it)
    3363              :     {
    3364        26382 :       exploded_node *next = next_enodes[it->m_merger_idx];
    3365        26382 :       if (next)
    3366              :         {
    3367        25809 :           gcc_assert (it->m_input_enode->get_supernode ()->m_succs.length ()
    3368              :                       == 1);
    3369        25809 :           const superedge *sedge
    3370        25809 :             = it->m_input_enode->get_supernode ()->m_succs[0];
    3371        25809 :           add_edge (it->m_input_enode, next, sedge,
    3372              :                     false); /* no "work" is done during merger.  */
    3373              :         }
    3374        26382 :       it->m_input_enode->set_status (exploded_node::status::bulk_merged);
    3375              :     }
    3376              : 
    3377         7439 :   if (logger)
    3378            8 :     logger->log ("merged %i in-enodes into %i out-enode(s) at SN: %i",
    3379            4 :                  items.length (), merged_states.length (), dst_snode->m_id);
    3380              : 
    3381         7439 :   return true;
    3382       139812 : }
    3383              : 
    3384              : /* The core of exploded_graph::process_worklist (the main analysis loop),
    3385              :    handling one node in the worklist.
    3386              : 
    3387              :    Get successor <point, state> pairs for NODE, calling get_or_create on
    3388              :    them, and adding an exploded_edge to each successors.
    3389              : 
    3390              :    Freshly-created nodes will be added to the worklist.  */
    3391              : 
    3392              : void
    3393       374138 : exploded_graph::process_node (exploded_node *node)
    3394              : {
    3395       374138 :   logger * const logger = get_logger ();
    3396       374138 :   LOG_FUNC_1 (logger, "EN: %i", node->m_index);
    3397              : 
    3398       374138 :   node->set_status (exploded_node::status::processed);
    3399              : 
    3400       374138 :   const program_point &point = node->get_point ();
    3401              : 
    3402              :   /* Update cfun and input_location in case of an ICE: make it easier to
    3403              :      track down which source construct we're failing to handle.  */
    3404       744806 :   auto_cfun sentinel (node->get_function ());
    3405              : 
    3406       374138 :   input_location = node->get_location ();
    3407              : 
    3408       374138 :   const program_state &state = node->get_state ();
    3409       374138 :   if (logger)
    3410              :     {
    3411          187 :       pretty_printer *pp = logger->get_printer ();
    3412          187 :       logger->start_log_line ();
    3413          187 :       pp_string (pp, "point: ");
    3414          187 :       point.print (pp, format (false));
    3415          187 :       pp_string (pp, ", state: ");
    3416          187 :       state.dump_to_pp (m_ext_state, true, false, pp);
    3417          187 :       logger->end_log_line ();
    3418              :     }
    3419              : 
    3420              :   /* Don't do anything for the origin enode; the initial population of the
    3421              :      worklist has already added successor enodes.  */
    3422       374138 :   if (point.get_supernode () == nullptr)
    3423              :     return;
    3424              : 
    3425              :   /* Specialcase for EXIT BBs, which don't have out-edges.  */
    3426       370668 :   if (point.get_supernode ()->exit_p ())
    3427              :     {
    3428        19240 :       gcc_assert (point.get_supernode ()->m_succs.length () == 0);
    3429              : 
    3430        19240 :       if (point.get_stack_depth () > 1)
    3431              :         {
    3432              :           /* Interprocedural return.  */
    3433         7344 :           auto &src_call_string = point.get_call_string ();
    3434              : 
    3435         7344 :           const call_string::element_t &top_of_stack
    3436         7344 :             = src_call_string.get_top_of_stack ();
    3437         7344 :           const call_string *dst_call_string = src_call_string.get_parent ();
    3438         7344 :           const program_point dst_point
    3439              :             (top_of_stack.get_return_snode_in_caller (),
    3440         7344 :              *dst_call_string);
    3441         7344 :           auto edge_info
    3442         7344 :             = std::make_unique<interprocedural_return> (top_of_stack.get_call_stmt ());
    3443              : 
    3444         7344 :           const program_state &src_state (node->get_state ());
    3445         7344 :           program_state dst_state (src_state);
    3446         7344 :           uncertainty_t uncertainty;
    3447         7344 :           impl_region_model_context ctxt (*this, node,
    3448              :                                           &src_state, &dst_state, &uncertainty,
    3449              :                                           nullptr,
    3450         7344 :                                           nullptr);
    3451         7344 :           edge_info->update_state (&dst_state, nullptr, &ctxt);
    3452              : 
    3453         7344 :           program_state::detect_leaks (src_state, dst_state,
    3454              :                                        nullptr, get_ext_state (),
    3455              :                                        &ctxt);
    3456              : 
    3457        14688 :           if (exploded_node *next
    3458         7344 :               = get_or_create_node (dst_point, dst_state, node))
    3459         7232 :             add_edge (node, next, nullptr, false,
    3460         7232 :                       std::move (edge_info));
    3461        14688 :         }
    3462              :       else
    3463              :         {
    3464              :           /* End of top-level of analysis for this function.
    3465              :              Detect leaks, and potentially create a function summary.  */
    3466        11896 :           node->detect_leaks (*this);
    3467              : 
    3468        11896 :           if (flag_analyzer_call_summaries
    3469        11896 :               && point.get_call_string ().empty_p ())
    3470              :             {
    3471              :               /* TODO: create function summary
    3472              :                  There can be more than one; each corresponds to a different
    3473              :                  final enode in the function.  */
    3474          631 :               if (logger)
    3475              :                 {
    3476            0 :                   pretty_printer *pp = logger->get_printer ();
    3477            0 :                   logger->start_log_line ();
    3478            0 :                   logger->log_partial
    3479            0 :                     ("would create function summary for %qE; state: ",
    3480              :                      point.get_fndecl ());
    3481            0 :                   state.dump_to_pp (m_ext_state, true, false, pp);
    3482            0 :                   logger->end_log_line ();
    3483              :                 }
    3484          631 :               per_function_data *per_fn_data
    3485          631 :                 = get_or_create_per_function_data (point.get_function ());
    3486          631 :               per_fn_data->add_call_summary (node);
    3487              :             }
    3488              :         }
    3489              : 
    3490        19240 :       return;
    3491              :     }
    3492              : 
    3493              :   /* Traverse into successors of the supernode.  */
    3494              :   int i;
    3495              :   superedge *succ;
    3496      1082538 :   FOR_EACH_VEC_ELT (point.get_supernode ()->m_succs, i, succ)
    3497              :     {
    3498       380685 :       if (logger)
    3499              :         {
    3500          194 :           label_text succ_desc (succ->get_description (false));
    3501          194 :           logger->log ("considering SN: %i -> SN: %i (%s)",
    3502          194 :                        succ->m_src->m_id, succ->m_dest->m_id,
    3503              :                        succ_desc.get ());
    3504          194 :         }
    3505              : 
    3506       380685 :       program_point next_point (succ->m_dest, point.get_call_string ());
    3507       380685 :       program_state next_state (state);
    3508       380685 :       uncertainty_t uncertainty;
    3509              : 
    3510              :       /* Find the outcome(s) of any operation on the edge.  */
    3511       380685 :       operation_context op_ctxt (*this, *node, *succ);
    3512              : 
    3513              :       /* Skip EH edges.  */
    3514       380685 :       if (auto cfg_edge = succ->get_any_cfg_edge ())
    3515       122976 :         if (cfg_edge->flags & EDGE_EH)
    3516         1028 :           continue;
    3517              : 
    3518       379657 :       if (auto op = succ->get_op ())
    3519       317567 :         op->execute (op_ctxt);
    3520              :       else
    3521              :         {
    3522              :           /* No-op.
    3523              :              Unconditional goto to the dst point, which
    3524              :              must be in same function.
    3525              :              The supernode changes, but the callstring and
    3526              :              state do not change.  */
    3527        62090 :           if (logger)
    3528           34 :             logger->log ("handling no-op edge");
    3529        62090 :           auto dst_point (op_ctxt.get_next_intraprocedural_point ());
    3530        62090 :           if (exploded_node *next
    3531        62090 :               = get_or_create_node (dst_point,
    3532              :                                     node->get_state (),
    3533              :                                     node))
    3534        61920 :             add_edge (node, next, succ, false);
    3535              :         }
    3536       380685 :     }
    3537       374138 : }
    3538              : 
    3539              : /* Ensure that this graph has a stats instance for FN, return it.
    3540              :    FN can be nullptr, in which case a stats instances is returned covering
    3541              :    "functionless" parts of the graph (the origin node).  */
    3542              : 
    3543              : stats *
    3544       420021 : exploded_graph::get_or_create_function_stats (function *fn)
    3545              : {
    3546       420021 :   if (!fn)
    3547         3474 :     return &m_functionless_stats;
    3548              : 
    3549       416547 :   if (stats **slot = m_per_function_stats.get (fn))
    3550       405759 :     return *slot;
    3551              :   else
    3552              :     {
    3553        10788 :       int num_supernodes = 0;
    3554      1720785 :       for (auto snode : m_sg.m_nodes)
    3555      1688421 :         if (snode->get_function () == fn)
    3556       190851 :           ++num_supernodes;
    3557        10788 :       stats *new_stats = new stats (num_supernodes);
    3558        10788 :       m_per_function_stats.put (fn, new_stats);
    3559        10788 :       return new_stats;
    3560              :     }
    3561              : }
    3562              : 
    3563              : /* Print bar charts to PP showing:
    3564              :    - the number of enodes per function, and
    3565              :    - for each function:
    3566              :      - the number of enodes per supernode/BB
    3567              :      - the number of excess enodes per supernode/BB beyond the
    3568              :        per-program-point limit, if there were any.  */
    3569              : 
    3570              : void
    3571            5 : exploded_graph::print_bar_charts (pretty_printer *pp) const
    3572              : {
    3573            5 :   cgraph_node *cgnode;
    3574              : 
    3575            5 :   pp_string (pp, "enodes per function:");
    3576            5 :   pp_newline (pp);
    3577            5 :   bar_chart enodes_per_function;
    3578           11 :   FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (cgnode)
    3579              :     {
    3580            6 :       function *fn = cgnode->get_fun ();
    3581            6 :       const stats * const *s_ptr
    3582            6 :         = const_cast <function_stat_map_t &> (m_per_function_stats).get (fn);
    3583           12 :       enodes_per_function.add_item (function_name (fn),
    3584            6 :                                     s_ptr ? (*s_ptr)->get_total_enodes () : 0);
    3585              :     }
    3586            5 :   enodes_per_function.print (pp);
    3587              : 
    3588              :   /* Accumulate number of enodes per supernode.  */
    3589           10 :   auto_vec<unsigned> enodes_per_supernode (m_sg.m_nodes.length ());
    3590          170 :   for (size_t i = 0; i < m_sg.m_nodes.length (); i++)
    3591           80 :     enodes_per_supernode.quick_push (0);
    3592              :   int i;
    3593              :   exploded_node *enode;
    3594          207 :   FOR_EACH_VEC_ELT (m_nodes, i, enode)
    3595              :     {
    3596          202 :       const supernode *iter_snode = enode->get_supernode ();
    3597          202 :       if (!iter_snode)
    3598            5 :         continue;
    3599          197 :       enodes_per_supernode[iter_snode->m_id]++;
    3600              :     }
    3601              : 
    3602              :   /* Accumulate excess enodes per supernode.  */
    3603           10 :   auto_vec<unsigned> excess_enodes_per_supernode (m_sg.m_nodes.length ());
    3604           85 :   for (size_t i = 0; i < m_sg.m_nodes.length (); i++)
    3605           80 :     excess_enodes_per_supernode.quick_push (0);
    3606           83 :   for (point_map_t::iterator iter = m_per_point_data.begin ();
    3607          161 :        iter != m_per_point_data.end (); ++iter)
    3608              :     {
    3609           78 :       const program_point *point = (*iter).first;
    3610           78 :       const supernode *iter_snode = point->get_supernode ();
    3611           78 :       if (!iter_snode)
    3612            5 :         continue;
    3613           73 :       const per_program_point_data *point_data = (*iter).second;
    3614           73 :       excess_enodes_per_supernode[iter_snode->m_id]
    3615           73 :         += point_data->m_excess_enodes;
    3616              :     }
    3617              : 
    3618              :   /* Show per-function bar_charts of enodes per supernode/BB.  */
    3619            5 :   pp_string (pp, "per-function enodes per supernode/BB:");
    3620            5 :   pp_newline (pp);
    3621           11 :   FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (cgnode)
    3622              :     {
    3623            6 :       function *fn = cgnode->get_fun ();
    3624            6 :       pp_printf (pp, "function: %qs", function_name (fn));
    3625            6 :       pp_newline (pp);
    3626              : 
    3627            6 :       bar_chart enodes_per_snode;
    3628            6 :       bar_chart excess_enodes_per_snode;
    3629            6 :       bool have_excess_enodes = false;
    3630          112 :       for (size_t i = 0; i < m_sg.m_nodes.length (); i++)
    3631              :         {
    3632          106 :           const supernode *iter_snode = m_sg.m_nodes[i];
    3633          106 :           if (iter_snode->get_function () != fn)
    3634           26 :             continue;
    3635           80 :           pretty_printer tmp_pp;
    3636           80 :           pp_printf (&tmp_pp, "sn %i (bb %i)",
    3637           80 :                      iter_snode->m_id, iter_snode->m_bb->index);
    3638           80 :           enodes_per_snode.add_item (pp_formatted_text (&tmp_pp),
    3639           80 :                                      enodes_per_supernode[iter_snode->m_id]);
    3640           80 :           const int num_excess
    3641           80 :             = excess_enodes_per_supernode[iter_snode->m_id];
    3642           80 :           excess_enodes_per_snode.add_item (pp_formatted_text (&tmp_pp),
    3643              :                                             num_excess);
    3644           80 :           if (num_excess)
    3645            0 :             have_excess_enodes = true;
    3646           80 :         }
    3647            6 :       enodes_per_snode.print (pp);
    3648            6 :       if (have_excess_enodes)
    3649              :         {
    3650            0 :           pp_printf (pp, "EXCESS ENODES:");
    3651            0 :           pp_newline (pp);
    3652            0 :           excess_enodes_per_snode.print (pp);
    3653              :         }
    3654            6 :     }
    3655            5 : }
    3656              : 
    3657              : /* Write all stats information to this graph's logger, if any.  */
    3658              : 
    3659              : void
    3660         3474 : exploded_graph::log_stats () const
    3661              : {
    3662         3474 :   logger * const logger = get_logger ();
    3663         3474 :   if (!logger)
    3664         3469 :     return;
    3665              : 
    3666            5 :   LOG_SCOPE (logger);
    3667              : 
    3668            5 :   m_ext_state.get_engine ()->log_stats (logger);
    3669              : 
    3670           10 :   logger->log ("m_sg.num_nodes (): %i", m_sg.num_nodes ());
    3671           10 :   logger->log ("m_nodes.length (): %i", m_nodes.length ());
    3672           10 :   logger->log ("m_edges.length (): %i", m_edges.length ());
    3673            5 :   logger->log ("remaining enodes in worklist: %i", m_worklist.length ());
    3674              : 
    3675            5 :   logger->log ("global stats:");
    3676            5 :   m_global_stats.log (logger);
    3677              : 
    3678            5 :   for (function_stat_map_t::iterator iter = m_per_function_stats.begin ();
    3679           22 :        iter != m_per_function_stats.end ();
    3680            6 :        ++iter)
    3681              :     {
    3682            6 :       function *fn = (*iter).first;
    3683            6 :       log_scope s (logger, function_name (fn));
    3684            6 :       (*iter).second->log (logger);
    3685            6 :     }
    3686              : 
    3687            5 :   print_bar_charts (logger->get_printer ());
    3688            5 : }
    3689              : 
    3690              : /* Dump all stats information to OUT.  */
    3691              : 
    3692              : void
    3693            0 : exploded_graph::dump_stats (FILE *out) const
    3694              : {
    3695            0 :   fprintf (out, "m_sg.num_nodes (): %i\n", m_sg.num_nodes ());
    3696            0 :   fprintf (out, "m_nodes.length (): %i\n", m_nodes.length ());
    3697            0 :   fprintf (out, "m_edges.length (): %i\n", m_edges.length ());
    3698            0 :   fprintf (out, "remaining enodes in worklist: %i", m_worklist.length ());
    3699              : 
    3700            0 :   fprintf (out, "global stats:\n");
    3701            0 :   m_global_stats.dump (out);
    3702              : 
    3703            0 :   for (function_stat_map_t::iterator iter = m_per_function_stats.begin ();
    3704            0 :        iter != m_per_function_stats.end ();
    3705            0 :        ++iter)
    3706              :     {
    3707            0 :       function *fn = (*iter).first;
    3708            0 :       fprintf (out, "function: %s\n", function_name (fn));
    3709            0 :       (*iter).second->dump (out);
    3710              :     }
    3711            0 : }
    3712              : 
    3713              : /* Return a new json::object of the form
    3714              :    {"nodes" : [objs for enodes],
    3715              :     "edges" : [objs for eedges],
    3716              :     "ext_state": object for extrinsic_state,
    3717              :     "diagnostic_manager": object for diagnostic_manager}.  */
    3718              : 
    3719              : std::unique_ptr<json::object>
    3720            0 : exploded_graph::to_json () const
    3721              : {
    3722            0 :   auto egraph_obj = std::make_unique<json::object> ();
    3723              : 
    3724              :   /* Nodes.  */
    3725            0 :   {
    3726            0 :     auto nodes_arr = std::make_unique<json::array> ();
    3727            0 :     unsigned i;
    3728            0 :     exploded_node *n;
    3729            0 :     FOR_EACH_VEC_ELT (m_nodes, i, n)
    3730            0 :       nodes_arr->append (n->to_json (m_ext_state));
    3731            0 :     egraph_obj->set ("nodes", std::move (nodes_arr));
    3732            0 :   }
    3733              : 
    3734              :   /* Edges.  */
    3735            0 :   {
    3736            0 :     auto edges_arr = std::make_unique<json::array> ();
    3737            0 :     unsigned i;
    3738            0 :     exploded_edge *n;
    3739            0 :     FOR_EACH_VEC_ELT (m_edges, i, n)
    3740            0 :       edges_arr->append (n->to_json ());
    3741            0 :     egraph_obj->set ("edges", std::move (edges_arr));
    3742            0 :   }
    3743              : 
    3744              :   /* m_sg is JSONified at the top-level.  */
    3745              : 
    3746            0 :   egraph_obj->set ("ext_state", m_ext_state.to_json ());
    3747            0 :   egraph_obj->set ("worklist", m_worklist.to_json ());
    3748            0 :   egraph_obj->set ("diagnostic_manager", m_diagnostic_manager.to_json ());
    3749              : 
    3750              :   /* The following fields aren't yet being JSONified:
    3751              :      const state_purge_map *const m_purge_map;
    3752              :      const analysis_plan &m_plan;
    3753              :      stats m_global_stats;
    3754              :      function_stat_map_t m_per_function_stats;
    3755              :      stats m_functionless_stats;
    3756              :      call_string_data_map_t m_per_call_string_data;  */
    3757              : 
    3758            0 :   return egraph_obj;
    3759              : }
    3760              : 
    3761              : /* class feasibility_problem.  */
    3762              : 
    3763              : void
    3764            4 : feasibility_problem::dump_to_pp (pretty_printer *pp) const
    3765              : {
    3766            4 :   pp_printf (pp, "edge from EN: %i to EN: %i",
    3767            4 :              m_eedge.m_src->m_index, m_eedge.m_dest->m_index);
    3768            4 :   if (m_rc)
    3769              :     {
    3770            4 :       pp_string (pp, "; rejected constraint: ");
    3771            4 :       m_rc->dump_to_pp (pp);
    3772            4 :       pp_string (pp, "; rmodel: ");
    3773            4 :       m_rc->get_model ().dump_to_pp (pp, true, false);
    3774              :     }
    3775            4 : }
    3776              : 
    3777              : /* class feasibility_state.  */
    3778              : 
    3779              : /* Ctor for feasibility_state, at the beginning of a path.  */
    3780              : 
    3781         6642 : feasibility_state::feasibility_state (region_model_manager *manager,
    3782         6642 :                                       const supergraph &sg)
    3783         6642 : : m_model (manager),
    3784        13284 :   m_snodes_visited (sg.m_nodes.length ())
    3785              : {
    3786         6642 :   bitmap_clear (m_snodes_visited);
    3787         6642 : }
    3788              : 
    3789              : /* Copy ctor for feasibility_state, for extending a path.  */
    3790              : 
    3791       475911 : feasibility_state::feasibility_state (const feasibility_state &other)
    3792       475911 : : m_model (other.m_model),
    3793       475911 :   m_snodes_visited (const_sbitmap (other.m_snodes_visited)->n_bits)
    3794              : {
    3795       475911 :   bitmap_copy (m_snodes_visited, other.m_snodes_visited);
    3796       475911 : }
    3797              : 
    3798        32615 : feasibility_state::feasibility_state (const region_model &model,
    3799        32615 :                                       const supergraph &sg)
    3800        32615 : : m_model (model),
    3801        65230 :   m_snodes_visited (sg.m_nodes.length ())
    3802              : {
    3803        32615 :   bitmap_clear (m_snodes_visited);
    3804        32615 : }
    3805              : 
    3806              : feasibility_state &
    3807        58683 : feasibility_state::operator= (const feasibility_state &other)
    3808              : {
    3809        58683 :   m_model = other.m_model;
    3810        58683 :   bitmap_copy (m_snodes_visited, other.m_snodes_visited);
    3811        58683 :   return *this;
    3812              : }
    3813              : 
    3814              : /* The heart of feasibility-checking.
    3815              : 
    3816              :    Attempt to update this state in-place based on traversing EEDGE
    3817              :    in a path.
    3818              :    Update the model for the stmts in the src enode.
    3819              :    Attempt to add constraints for EEDGE.
    3820              : 
    3821              :    If feasible, return true.
    3822              :    Otherwise, return false and write to *OUT_RC.  */
    3823              : 
    3824              : bool
    3825       208036 : feasibility_state::
    3826              : maybe_update_for_edge (logger *logger,
    3827              :                        const exploded_edge *eedge,
    3828              :                        region_model_context *ctxt,
    3829              :                        std::unique_ptr<rejected_constraint> *out_rc)
    3830              : {
    3831       208036 :   const exploded_node &src_enode = *eedge->m_src;
    3832       208036 :   const program_point &src_point = src_enode.get_point ();
    3833       208036 :   if (logger)
    3834              :     {
    3835           82 :       logger->start_log_line ();
    3836           82 :       src_point.print (logger->get_printer (), format (false));
    3837           82 :       logger->end_log_line ();
    3838              :     }
    3839              : 
    3840       208036 :   if (eedge->m_custom_info)
    3841         9814 :     eedge->m_custom_info->update_model (&m_model, eedge, ctxt);
    3842              :   else
    3843              :     {
    3844       198222 :       const superedge *sedge = eedge->m_sedge;
    3845       198222 :       if (sedge)
    3846              :         {
    3847       190060 :           if (logger)
    3848              :             {
    3849           80 :               label_text desc (sedge->get_description (false));
    3850           80 :               logger->log ("  sedge: SN:%i -> SN:%i %s",
    3851           80 :                            sedge->m_src->m_id,
    3852           80 :                            sedge->m_dest->m_id,
    3853              :                            desc.get ());
    3854           80 :             }
    3855              : 
    3856       190060 :           if (sedge->get_op ())
    3857       150964 :             if (!sedge->get_op ()->execute_for_feasibility (*eedge,
    3858              :                                                             *this,
    3859              :                                                             ctxt,
    3860              :                                                             out_rc))
    3861              :               {
    3862         6759 :                 if (logger)
    3863              :                   {
    3864            8 :                     logger->start_log_line ();
    3865            8 :                     logger->log_partial ("rejecting due to region model: ");
    3866            8 :                     m_model.dump_to_pp (logger->get_printer (), true, false);
    3867            8 :                     logger->end_log_line ();
    3868              :                   }
    3869         6759 :                 return false;
    3870              :               }
    3871              :         }
    3872              :       else
    3873              :         {
    3874              :           /* Special-case the initial eedge from the origin node to the
    3875              :              initial function by pushing a frame for it.  */
    3876         8162 :           if (eedge->m_src->m_index == 0)
    3877              :             {
    3878         6468 :               function *fun = eedge->m_dest->get_function ();
    3879         6468 :               gcc_assert (fun);
    3880         6468 :               m_model.push_frame (*fun, nullptr, nullptr, ctxt);
    3881         6468 :               if (logger)
    3882            2 :                 logger->log ("  pushing frame for %qD", fun->decl);
    3883              :             }
    3884              :         }
    3885              :     }
    3886              : 
    3887              : 
    3888       201277 :   {
    3889       201277 :     const exploded_node &dst_enode = *eedge->m_dest;
    3890       201277 :     const unsigned dst_snode_idx = dst_enode.get_supernode ()->m_id;
    3891       201277 :     bitmap_set_bit (m_snodes_visited, dst_snode_idx);
    3892              :   }
    3893              : 
    3894       201277 :   return true;
    3895              : }
    3896              : 
    3897              : /* Dump this object to PP.  */
    3898              : 
    3899              : void
    3900           70 : feasibility_state::dump_to_pp (pretty_printer *pp,
    3901              :                                bool simple, bool multiline) const
    3902              : {
    3903           70 :   m_model.dump_to_pp (pp, simple, multiline);
    3904           70 : }
    3905              : 
    3906              : /* A family of cluster subclasses for use when generating .dot output for
    3907              :    exploded graphs (-fdump-analyzer-exploded-graph), for grouping the
    3908              :    enodes into hierarchical boxes.
    3909              : 
    3910              :    All functionless enodes appear in the top-level graph.
    3911              :    Every (function, call_string) pair gets its own cluster.  Within that
    3912              :    cluster, each supernode gets its own cluster.
    3913              : 
    3914              :    Hence all enodes relating to a particular function with a particular
    3915              :    callstring will be in a cluster together; all enodes for the same
    3916              :    function but with a different callstring will be in a different
    3917              :    cluster.  */
    3918              : 
    3919              : /* Base class of cluster for clustering exploded_node instances in .dot
    3920              :    output, based on various subclass-specific criteria.  */
    3921              : 
    3922         1291 : class exploded_cluster : public cluster<eg_traits>
    3923              : {
    3924              : };
    3925              : 
    3926              : /* Cluster containing all exploded_node instances for one supernode.  */
    3927              : 
    3928              : class supernode_cluster : public exploded_cluster
    3929              : {
    3930              : public:
    3931          429 :   supernode_cluster (const supernode *supernode) : m_supernode (supernode) {}
    3932              : 
    3933              :   // TODO: dtor?
    3934              : 
    3935          429 :   void dump_dot (graphviz_out *gv, const dump_args_t &args) const final override
    3936              :   {
    3937          429 :     gv->println ("subgraph \"cluster_supernode_%i\" {", m_supernode->m_id);
    3938          429 :     gv->indent ();
    3939          429 :     gv->println ("style=\"dashed\";");
    3940          858 :     gv->println ("label=\"SN: %i (bb: %i; scc: %i)\";",
    3941          429 :                  m_supernode->m_id, m_supernode->m_bb->index,
    3942          429 :                  args.m_eg.get_scc_id (*m_supernode));
    3943              : 
    3944          429 :     int i;
    3945          429 :     exploded_node *enode;
    3946         1287 :     FOR_EACH_VEC_ELT (m_enodes, i, enode)
    3947          429 :       enode->dump_dot (gv, args);
    3948              : 
    3949              :     /* Terminate subgraph.  */
    3950          429 :     gv->outdent ();
    3951          429 :     gv->println ("}");
    3952          429 :   }
    3953              : 
    3954          429 :   void add_node (exploded_node *en) final override
    3955              :   {
    3956            0 :     m_enodes.safe_push (en);
    3957            0 :   }
    3958              : 
    3959              :   /* Comparator for use by auto_vec<supernode_cluster *>::qsort.  */
    3960              : 
    3961            0 :   static int cmp_ptr_ptr (const void *p1, const void *p2)
    3962              :   {
    3963            0 :     const supernode_cluster *c1
    3964              :       = *(const supernode_cluster * const *)p1;
    3965            0 :     const supernode_cluster *c2
    3966              :       = *(const supernode_cluster * const *)p2;
    3967            0 :     return c1->m_supernode->m_id - c2->m_supernode->m_id;
    3968              :   }
    3969              : 
    3970              : private:
    3971              :   const supernode *m_supernode;
    3972              :   auto_vec <exploded_node *> m_enodes;
    3973              : };
    3974              : 
    3975              : /* Cluster containing all supernode_cluster instances for one
    3976              :    (function, call_string) pair.  */
    3977              : 
    3978              : class function_call_string_cluster : public exploded_cluster
    3979              : {
    3980              : public:
    3981          429 :   function_call_string_cluster (function *fun, const call_string &cs)
    3982          429 :   : m_fun (fun), m_cs (cs) {}
    3983              : 
    3984          858 :   ~function_call_string_cluster ()
    3985          429 :   {
    3986          429 :     for (map_t::iterator iter = m_map.begin ();
    3987         1716 :          iter != m_map.end ();
    3988          429 :          ++iter)
    3989          429 :       delete (*iter).second;
    3990          858 :   }
    3991              : 
    3992          429 :   void dump_dot (graphviz_out *gv, const dump_args_t &args) const final override
    3993              :   {
    3994          429 :     const char *funcname = function_name (m_fun);
    3995              : 
    3996          858 :     gv->println ("subgraph \"cluster_function_%s\" {",
    3997          429 :                  IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (m_fun->decl)));
    3998          429 :     gv->indent ();
    3999          429 :     gv->write_indent ();
    4000          429 :     gv->print ("label=\"call string: ");
    4001          429 :     m_cs.print (gv->get_pp ());
    4002          429 :     gv->print (" function: %s \";", funcname);
    4003          429 :     gv->print ("\n");
    4004              : 
    4005              :     /* Dump m_map, sorting it to avoid churn when comparing dumps.  */
    4006          429 :     auto_vec<supernode_cluster *> child_clusters (m_map.elements ());
    4007          429 :     for (map_t::iterator iter = m_map.begin ();
    4008         1716 :          iter != m_map.end ();
    4009          429 :          ++iter)
    4010          429 :       child_clusters.quick_push ((*iter).second);
    4011              : 
    4012          429 :     child_clusters.qsort (supernode_cluster::cmp_ptr_ptr);
    4013              : 
    4014              :     unsigned i;
    4015              :     supernode_cluster *child_cluster;
    4016          858 :     FOR_EACH_VEC_ELT (child_clusters, i, child_cluster)
    4017          429 :       child_cluster->dump_dot (gv, args);
    4018              : 
    4019              :     /* Terminate subgraph.  */
    4020          429 :     gv->outdent ();
    4021          429 :     gv->println ("}");
    4022          429 :   }
    4023              : 
    4024          429 :   void add_node (exploded_node *en) final override
    4025              :   {
    4026          429 :     const supernode *supernode = en->get_supernode ();
    4027          429 :     gcc_assert (supernode);
    4028          429 :     supernode_cluster **slot = m_map.get (supernode);
    4029          429 :     if (slot)
    4030            0 :       (*slot)->add_node (en);
    4031              :     else
    4032              :       {
    4033          429 :         supernode_cluster *child = new supernode_cluster (supernode);
    4034          429 :         m_map.put (supernode, child);
    4035          429 :         child->add_node (en);
    4036              :       }
    4037          429 :   }
    4038              : 
    4039              :   /* Comparator for use by auto_vec<function_call_string_cluster *>.  */
    4040              : 
    4041        17678 :   static int cmp_ptr_ptr (const void *p1, const void *p2)
    4042              :   {
    4043        17678 :     const function_call_string_cluster *c1
    4044              :       = *(const function_call_string_cluster * const *)p1;
    4045        17678 :     const function_call_string_cluster *c2
    4046              :       = *(const function_call_string_cluster * const *)p2;
    4047        35356 :     if (int cmp_names
    4048        17678 :         = strcmp (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (c1->m_fun->decl)),
    4049        17678 :                   IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (c2->m_fun->decl))))
    4050              :       return cmp_names;
    4051         5037 :     return call_string::cmp (c1->m_cs, c2->m_cs);
    4052              :   }
    4053              : 
    4054              : private:
    4055              :   function *m_fun;
    4056              :   const call_string &m_cs;
    4057              :   typedef ordered_hash_map<const supernode *, supernode_cluster *> map_t;
    4058              :   map_t m_map;
    4059              : };
    4060              : 
    4061              : /* Keys for root_cluster.  */
    4062              : 
    4063              : struct function_call_string
    4064              : {
    4065          429 :   function_call_string (function *fun, const call_string *cs)
    4066          429 :   : m_fun (fun), m_cs (cs)
    4067              :   {
    4068          429 :     gcc_assert (fun);
    4069          429 :     gcc_assert (cs);
    4070          429 :   }
    4071              : 
    4072              :   function *m_fun;
    4073              :   const call_string *m_cs;
    4074              : };
    4075              : 
    4076              : } // namespace ana
    4077              : 
    4078              : template <> struct default_hash_traits<function_call_string>
    4079              : : public pod_hash_traits<function_call_string>
    4080              : {
    4081              :   static const bool empty_zero_p = false;
    4082              : };
    4083              : 
    4084              : template <>
    4085              : inline hashval_t
    4086         6043 : pod_hash_traits<function_call_string>::hash (value_type v)
    4087              : {
    4088         6043 :   return (pointer_hash <function>::hash (v.m_fun)
    4089         6043 :           ^ pointer_hash <const call_string>::hash (v.m_cs));
    4090              : }
    4091              : 
    4092              : template <>
    4093              : inline bool
    4094        29984 : pod_hash_traits<function_call_string>::equal (const value_type &existing,
    4095              :                                               const value_type &candidate)
    4096              : {
    4097        29984 :   return existing.m_fun == candidate.m_fun && &existing.m_cs == &candidate.m_cs;
    4098              : }
    4099              : template <>
    4100              : inline void
    4101              : pod_hash_traits<function_call_string>::mark_deleted (value_type &v)
    4102              : {
    4103              :   v.m_fun = reinterpret_cast<function *> (1);
    4104              : }
    4105              : template <>
    4106              : inline void
    4107         1932 : pod_hash_traits<function_call_string>::mark_empty (value_type &v)
    4108              : {
    4109         1932 :   v.m_fun = nullptr;
    4110              : }
    4111              : template <>
    4112              : inline bool
    4113        47620 : pod_hash_traits<function_call_string>::is_deleted (value_type v)
    4114              : {
    4115        47620 :   return v.m_fun == reinterpret_cast<function *> (1);
    4116              : }
    4117              : template <>
    4118              : inline bool
    4119       104995 : pod_hash_traits<function_call_string>::is_empty (value_type v)
    4120              : {
    4121       104566 :   return v.m_fun == nullptr;
    4122              : }
    4123              : 
    4124              : namespace ana {
    4125              : 
    4126              : /* Top-level cluster for generating .dot output for exploded graphs,
    4127              :    handling the functionless nodes, and grouping the remaining nodes by
    4128              :    callstring.  */
    4129              : 
    4130              : class root_cluster : public exploded_cluster
    4131              : {
    4132              : public:
    4133            4 :   ~root_cluster ()
    4134            4 :   {
    4135          433 :     for (map_t::iterator iter = m_map.begin ();
    4136          433 :          iter != m_map.end ();
    4137          429 :          ++iter)
    4138          429 :       delete (*iter).second;
    4139            4 :   }
    4140              : 
    4141            4 :   void dump_dot (graphviz_out *gv, const dump_args_t &args) const final override
    4142              :   {
    4143            4 :     int i;
    4144            4 :     exploded_node *enode;
    4145            8 :     FOR_EACH_VEC_ELT (m_functionless_enodes, i, enode)
    4146            4 :       enode->dump_dot (gv, args);
    4147              : 
    4148              :     /* Dump m_map, sorting it to avoid churn when comparing dumps.  */
    4149            4 :     auto_vec<function_call_string_cluster *> child_clusters (m_map.elements ());
    4150            4 :     for (map_t::iterator iter = m_map.begin ();
    4151          433 :          iter != m_map.end ();
    4152          429 :          ++iter)
    4153          429 :       child_clusters.quick_push ((*iter).second);
    4154              : 
    4155            4 :     child_clusters.qsort (function_call_string_cluster::cmp_ptr_ptr);
    4156              : 
    4157              :     function_call_string_cluster *child_cluster;
    4158          437 :     FOR_EACH_VEC_ELT (child_clusters, i, child_cluster)
    4159          429 :       child_cluster->dump_dot (gv, args);
    4160            4 :   }
    4161              : 
    4162          433 :   void add_node (exploded_node *en) final override
    4163              :   {
    4164          433 :     function *fun = en->get_function ();
    4165          429 :     if (!fun)
    4166              :       {
    4167            4 :         m_functionless_enodes.safe_push (en);
    4168            4 :         return;
    4169              :       }
    4170              : 
    4171          429 :     const call_string &cs = en->get_point ().get_call_string ();
    4172          429 :     function_call_string key (fun, &cs);
    4173          429 :     function_call_string_cluster **slot = m_map.get (key);
    4174          429 :     if (slot)
    4175            0 :       (*slot)->add_node (en);
    4176              :     else
    4177              :       {
    4178          429 :         function_call_string_cluster *child
    4179          429 :           = new function_call_string_cluster (fun, cs);
    4180          429 :         m_map.put (key, child);
    4181          429 :         child->add_node (en);
    4182              :       }
    4183              :   }
    4184              : 
    4185              : private:
    4186              :   typedef hash_map<function_call_string, function_call_string_cluster *> map_t;
    4187              :   map_t m_map;
    4188              : 
    4189              :   /* This should just be the origin exploded_node.  */
    4190              :   auto_vec <exploded_node *> m_functionless_enodes;
    4191              : };
    4192              : 
    4193              : /* Subclass of range_label for use within
    4194              :    exploded_graph::dump_exploded_nodes for implementing
    4195              :    -fdump-analyzer-exploded-nodes: a label for a specific
    4196              :    exploded_node.  */
    4197              : 
    4198              : class enode_label : public range_label
    4199              : {
    4200              :  public:
    4201            0 :   enode_label (const extrinsic_state &ext_state,
    4202              :                exploded_node *enode)
    4203            0 :   : m_ext_state (ext_state), m_enode (enode) {}
    4204              : 
    4205            0 :   label_text get_text (unsigned) const final override
    4206              :   {
    4207            0 :     pretty_printer pp;
    4208            0 :     pp_format_decoder (&pp) = default_tree_printer;
    4209            0 :     m_enode->get_state ().dump_to_pp (m_ext_state, true, false, &pp);
    4210            0 :     return make_label_text (false, "EN: %i: %s",
    4211            0 :                             m_enode->m_index, pp_formatted_text (&pp));
    4212            0 :   }
    4213              : 
    4214              : private:
    4215              :   const extrinsic_state &m_ext_state;
    4216              :   exploded_node *m_enode;
    4217              : };
    4218              : 
    4219              : /* Postprocessing support for dumping the exploded nodes.
    4220              :    Handle -fdump-analyzer-exploded-nodes,
    4221              :    -fdump-analyzer-exploded-nodes-2, and the
    4222              :    "__analyzer_dump_exploded_nodes" builtin.  */
    4223              : 
    4224              : void
    4225         3474 : exploded_graph::dump_exploded_nodes () const
    4226              : {
    4227              :   // TODO
    4228              :   /* Locate calls to __analyzer_dump_exploded_nodes.  */
    4229              :   // Print how many egs there are for them?
    4230              :   /* Better: log them as we go, and record the exploded nodes
    4231              :      in question.  */
    4232              : 
    4233              :   /* Show every enode.  */
    4234              : 
    4235              :   /* Gather them by stmt, so that we can more clearly see the
    4236              :      "hotspots" requiring numerous exploded nodes.  */
    4237              : 
    4238              :   /* Alternatively, simply throw them all into one big rich_location
    4239              :      and see if the label-printing will sort it out...
    4240              :      This requires them all to be in the same source file.  */
    4241              : 
    4242         3474 :   if (flag_dump_analyzer_exploded_nodes)
    4243              :     {
    4244            0 :       auto_timevar tv (TV_ANALYZER_DUMP);
    4245            0 :       gcc_rich_location richloc (UNKNOWN_LOCATION);
    4246            0 :       unsigned i;
    4247            0 :       exploded_node *enode;
    4248            0 :       FOR_EACH_VEC_ELT (m_nodes, i, enode)
    4249              :         {
    4250            0 :           location_t loc = enode->get_location ();
    4251            0 :           if (get_pure_location (richloc.get_loc ()) == UNKNOWN_LOCATION)
    4252            0 :             richloc.set_range (0, loc, SHOW_RANGE_WITH_CARET);
    4253              :           else
    4254            0 :             richloc.add_range (loc,
    4255              :                                SHOW_RANGE_WITHOUT_CARET,
    4256            0 :                                new enode_label (m_ext_state, enode));
    4257              :         }
    4258            0 :       warning_at (&richloc, 0, "%i exploded nodes", m_nodes.length ());
    4259              : 
    4260              :       /* Repeat the warning without all the labels, so that message is visible
    4261              :          (the other one may well have scrolled past the terminal limit).  */
    4262            0 :       warning_at (richloc.get_loc (), 0,
    4263              :                   "%i exploded nodes", m_nodes.length ());
    4264              : 
    4265            0 :       if (m_worklist.length () > 0)
    4266            0 :         warning_at (richloc.get_loc (), 0,
    4267              :                     "worklist still contains %i nodes", m_worklist.length ());
    4268            0 :     }
    4269              : 
    4270              :   /* Dump the egraph in textual form to a dump file.  */
    4271         3474 :   if (flag_dump_analyzer_exploded_nodes_2)
    4272              :     {
    4273            0 :       auto_timevar tv (TV_ANALYZER_DUMP);
    4274            0 :       char *filename
    4275            0 :         = concat (dump_base_name, ".eg.txt", nullptr);
    4276            0 :       FILE *outf = fopen (filename, "w");
    4277            0 :       if (!outf)
    4278            0 :         error_at (UNKNOWN_LOCATION, "unable to open %qs for writing", filename);
    4279            0 :       free (filename);
    4280              : 
    4281            0 :       fprintf (outf, "exploded graph for %s\n", dump_base_name);
    4282            0 :       fprintf (outf, "  nodes: %i\n", m_nodes.length ());
    4283            0 :       fprintf (outf, "  edges: %i\n", m_edges.length ());
    4284              : 
    4285            0 :       unsigned i;
    4286            0 :       exploded_node *enode;
    4287            0 :       FOR_EACH_VEC_ELT (m_nodes, i, enode)
    4288              :         {
    4289            0 :           fprintf (outf, "\nEN %i:\n", enode->m_index);
    4290            0 :           enode->dump_succs_and_preds (outf);
    4291            0 :           pretty_printer pp;
    4292            0 :           enode->get_point ().print (&pp, format (true));
    4293            0 :           fprintf (outf, "%s\n", pp_formatted_text (&pp));
    4294            0 :           text_art::dump_to_file (enode->get_state (), outf);
    4295            0 :         }
    4296              : 
    4297            0 :       fclose (outf);
    4298            0 :     }
    4299              : 
    4300              :   /* Dump the egraph in textual form to multiple dump files, one per enode.  */
    4301         3474 :   if (flag_dump_analyzer_exploded_nodes_3)
    4302              :     {
    4303            0 :       auto_timevar tv (TV_ANALYZER_DUMP);
    4304              : 
    4305            0 :       unsigned i;
    4306            0 :       exploded_node *enode;
    4307            0 :       FOR_EACH_VEC_ELT (m_nodes, i, enode)
    4308              :         {
    4309            0 :           char *filename
    4310            0 :             = xasprintf ("%s.en-%i.txt", dump_base_name, i);
    4311            0 :           FILE *outf = fopen (filename, "w");
    4312            0 :           if (!outf)
    4313            0 :             error_at (UNKNOWN_LOCATION, "unable to open %qs for writing",
    4314              :                       filename);
    4315            0 :           free (filename);
    4316              : 
    4317            0 :           fprintf (outf, "EN %i:\n", enode->m_index);
    4318            0 :           enode->dump_succs_and_preds (outf);
    4319            0 :           pretty_printer pp;
    4320            0 :           enode->get_point ().print (&pp, format (true));
    4321            0 :           fprintf (outf, "%s\n", pp_formatted_text (&pp));
    4322            0 :           text_art::dump_to_file (enode->get_state (), outf);
    4323              : 
    4324            0 :           fclose (outf);
    4325            0 :         }
    4326            0 :     }
    4327              : 
    4328              :   /* Emit a warning at any call to "__analyzer_dump_exploded_nodes",
    4329              :      giving the number of processed exploded nodes at the snode before
    4330              :      the call, and the IDs of processed, merger, and worklist enodes.
    4331              : 
    4332              :      We highlight the count of *processed* enodes since this is of most
    4333              :      interest in DejaGnu tests for ensuring that state merger has
    4334              :      happened.
    4335              : 
    4336              :      We don't show the count of merger and worklist enodes, as this is
    4337              :      more of an implementation detail of the merging/worklist that we
    4338              :      don't want to bake into our expected DejaGnu messages.  */
    4339              : 
    4340         3474 :   unsigned i;
    4341         3474 :   exploded_node *enode;
    4342         3474 :   hash_set<const gimple *> seen;
    4343       419594 :   FOR_EACH_VEC_ELT (m_nodes, i, enode)
    4344              :     {
    4345       412650 :       const supernode *snode = enode->get_supernode ();
    4346       412650 :       if (!snode)
    4347         3470 :         continue;
    4348       409180 :       if (snode->m_succs.length () != 1)
    4349        46954 :         continue;
    4350       362226 :       const superedge *sedge = snode->m_succs[0];
    4351       362226 :       if (!sedge->get_op ())
    4352        88310 :         continue;
    4353       273916 :       const call_and_return_op *op
    4354       273916 :         = sedge->get_op ()->dyn_cast_call_and_return_op ();
    4355       273916 :       if (!op)
    4356       209594 :         continue;
    4357        64322 :       const gcall &call = op->get_gcall ();
    4358        64322 :       if (is_special_named_call_p (call, "__analyzer_dump_exploded_nodes", 1))
    4359              :         {
    4360         1129 :           if (seen.contains (&call))
    4361          444 :             continue;
    4362              : 
    4363          685 :           auto_vec<exploded_node *> processed_enodes;
    4364          685 :           auto_vec<exploded_node *> merger_enodes;
    4365          685 :           auto_vec<exploded_node *> worklist_enodes;
    4366              :           /* This is O(N^2).  */
    4367          685 :           unsigned j;
    4368          685 :           exploded_node *other_enode;
    4369       121428 :           FOR_EACH_VEC_ELT (m_nodes, j, other_enode)
    4370              :             {
    4371       120743 :               if (other_enode->get_supernode () == snode)
    4372         1129 :                 switch (other_enode->get_status ())
    4373              :                   {
    4374            0 :                   default:
    4375            0 :                     gcc_unreachable ();
    4376            0 :                   case exploded_node::status::worklist:
    4377            0 :                     worklist_enodes.safe_push (other_enode);
    4378            0 :                     break;
    4379         1060 :                   case exploded_node::status::processed:
    4380         1060 :                     processed_enodes.safe_push (other_enode);
    4381         1060 :                     break;
    4382           69 :                   case exploded_node::status::merger:
    4383           69 :                     merger_enodes.safe_push (other_enode);
    4384           69 :                     break;
    4385              :                   }
    4386              :             }
    4387              : 
    4388         1370 :           pretty_printer pp;
    4389          685 :           pp_character (&pp, '[');
    4390          685 :           print_enode_indices (&pp, processed_enodes);
    4391          685 :           if (merger_enodes.length () > 0)
    4392              :             {
    4393           44 :               pp_string (&pp, "] merger(s): [");
    4394           44 :               print_enode_indices (&pp, merger_enodes);
    4395              :             }
    4396          685 :           if (worklist_enodes.length () > 0)
    4397              :             {
    4398            0 :               pp_string (&pp, "] worklist: [");
    4399            0 :               print_enode_indices (&pp, worklist_enodes);
    4400              :             }
    4401          685 :           pp_character (&pp, ']');
    4402              : 
    4403         1370 :           warning_n (call.location, 0, processed_enodes.length (),
    4404              :                      "%i processed enode: %s",
    4405              :                      "%i processed enodes: %s",
    4406              :                      processed_enodes.length (), pp_formatted_text (&pp));
    4407          685 :           seen.add (&call);
    4408              : 
    4409              :           /* If the argument is non-zero, then print all of the states
    4410              :              of the various enodes.  */
    4411          685 :           tree t_arg = fold (gimple_call_arg (&call, 0));
    4412          685 :           if (TREE_CODE (t_arg) != INTEGER_CST)
    4413              :             {
    4414            0 :               error_at (snode->m_loc,
    4415              :                         "integer constant required for arg 1");
    4416            0 :               return;
    4417              :             }
    4418          685 :           int i_arg = TREE_INT_CST_LOW (t_arg);
    4419          685 :           if (i_arg)
    4420              :             {
    4421              :               exploded_node *other_enode;
    4422          685 :               FOR_EACH_VEC_ELT (processed_enodes, j, other_enode)
    4423              :                 {
    4424            0 :                   fprintf (stderr, "%i of %i: EN %i:\n",
    4425              :                            j + 1, processed_enodes.length (),
    4426            0 :                            other_enode->m_index);
    4427            0 :                   other_enode->dump_succs_and_preds (stderr);
    4428              :                   /* Dump state.  */
    4429            0 :                   other_enode->get_state ().dump (m_ext_state, false);
    4430              :                 }
    4431              :             }
    4432          685 :         }
    4433              :     }
    4434         3474 : }
    4435              : 
    4436              : DEBUG_FUNCTION exploded_node *
    4437            0 : exploded_graph::get_node_by_index (int idx) const
    4438              : {
    4439            0 :   exploded_node *enode = m_nodes[idx];
    4440            0 :   gcc_assert (enode->m_index == idx);
    4441            0 :   return enode;
    4442              : }
    4443              : 
    4444              : /* Ensure that there is an exploded_node for a top-level call to FNDECL.  */
    4445              : 
    4446              : void
    4447          298 : exploded_graph::on_escaped_function (tree fndecl)
    4448              : {
    4449          298 :   logger * const logger = get_logger ();
    4450          298 :   LOG_FUNC_1 (logger, "%qE", fndecl);
    4451              : 
    4452          298 :   cgraph_node *cgnode = cgraph_node::get (fndecl);
    4453          298 :   if (!cgnode)
    4454              :     return;
    4455              : 
    4456          298 :   function *fun = cgnode->get_fun ();
    4457          298 :   if (!fun)
    4458              :     return;
    4459              : 
    4460          294 :   if (!gimple_has_body_p (fndecl))
    4461              :     return;
    4462              : 
    4463          294 :   exploded_node *enode = add_function_entry (*fun);
    4464          294 :   if (logger)
    4465              :     {
    4466            0 :       if (enode)
    4467            0 :         logger->log ("created EN %i for %qE entrypoint",
    4468            0 :                      enode->m_index, fun->decl);
    4469              :       else
    4470            0 :         logger->log ("did not create enode for %qE entrypoint", fun->decl);
    4471              :     }
    4472          298 : }
    4473              : 
    4474              : /* Subclass of dot_annotator for implementing
    4475              :    DUMP_BASE_NAME.supergraph.N.eg.dot, a post-analysis dump of the supergraph.
    4476              : 
    4477              :    Annotate the supergraph nodes by printing the exploded nodes in concise
    4478              :    form within them, colorizing the exploded nodes based on sm-state.
    4479              :    Also show saved diagnostics within the exploded nodes, giving information
    4480              :    on whether they were feasible, and, if infeasible, where the problem
    4481              :    was.  */
    4482              : 
    4483            4 : class exploded_graph_annotator : public dot_annotator
    4484              : {
    4485              : public:
    4486            4 :   exploded_graph_annotator (const exploded_graph &eg)
    4487            4 :   : m_eg (eg)
    4488              :   {
    4489              :     /* Avoid O(N^2) by prepopulating m_enodes_per_snode_id.  */
    4490          390 :     for (size_t i = 0; i < eg.get_supergraph ().m_nodes.length (); ++i)
    4491          191 :       m_enodes_per_snode_id.push_back (std::vector<exploded_node *> ());
    4492              :     exploded_node *enode;
    4493              :     unsigned i;
    4494          437 :     FOR_EACH_VEC_ELT (m_eg.m_nodes, i, enode)
    4495          433 :       if (enode->get_supernode ())
    4496          429 :         m_enodes_per_snode_id[enode->get_supernode ()->m_id].push_back (enode);
    4497            4 :   }
    4498              : 
    4499              :   /* Show exploded nodes for N.  */
    4500          191 :   void add_node_annotations (graphviz_out *gv, const supernode &n)
    4501              :     const final override
    4502              :   {
    4503          191 :     gv->begin_tr ();
    4504          191 :     pretty_printer *pp = gv->get_pp ();
    4505              : 
    4506          191 :     if (m_enodes_per_snode_id[n.m_id].empty ())
    4507            4 :       pp_string (pp, "<TD BGCOLOR=\"red\">UNREACHED</TD>");
    4508              :     else
    4509              :       {
    4510              :         /* Adding an empty TD here makes the actual enodes
    4511              :            be right-aligned and tightly packed, greatly
    4512              :            improving the readability of the graph.  */
    4513          187 :         pp_string (pp, "<TD></TD>");
    4514          616 :         for (auto enode : m_enodes_per_snode_id[n.m_id])
    4515              :           {
    4516          429 :             gcc_assert (enode->get_supernode () == &n);
    4517          429 :             print_enode (gv, enode);
    4518              :           }
    4519              :       }
    4520              : 
    4521          191 :     pp_flush (pp);
    4522          191 :     gv->end_tr ();
    4523          191 :   }
    4524              : 
    4525              :   void
    4526            4 :   add_extra_objects (graphviz_out *gv) const final override
    4527              :   {
    4528            4 :     pretty_printer *pp = gv->get_pp ();
    4529              : 
    4530            4 :     pp_string (pp, "en_0 [shape=none,margin=0,style=filled,label=<<TABLE><TR>");
    4531            4 :     print_enode (gv, m_eg.m_nodes[0]);
    4532            4 :     pp_string (pp, "</TR></TABLE>>];\n\n");
    4533            4 :     pp_flush (pp);
    4534              : 
    4535            4 :     unsigned i;
    4536            4 :     exploded_edge *eedge;
    4537          449 :     FOR_EACH_VEC_ELT (m_eg.m_edges, i, eedge)
    4538              :       {
    4539          445 :         print_enode_port (pp, *eedge->m_src, "s");
    4540          445 :         pp_string (pp, " -> ");
    4541          445 :         print_enode_port (pp, *eedge->m_dest, "n");
    4542          445 :         dot::attr_list attrs;
    4543          445 :         attrs.add (dot::id ("style"), dot::id ("dotted"));
    4544          445 :         if (eedge->m_custom_info)
    4545              :           {
    4546           22 :             pretty_printer info_pp;
    4547           22 :             pp_format_decoder (&info_pp) = default_tree_printer;
    4548           22 :             eedge->m_custom_info->print (&info_pp);
    4549           44 :             attrs.add (dot::id ("label"),
    4550           44 :                        dot::id (pp_formatted_text (&info_pp)));
    4551           22 :           }
    4552          445 :         dot::writer w (*pp);
    4553          445 :         attrs.print (w);
    4554          445 :         pp_newline (pp);
    4555          445 :       }
    4556            4 :   }
    4557              : 
    4558              : private:
    4559              :   void
    4560          890 :   print_enode_port (pretty_printer *pp,
    4561              :                     const exploded_node &enode,
    4562              :                     const char *compass_pt) const
    4563              :   {
    4564          890 :     if (const supernode *snode = enode.get_supernode ())
    4565          878 :       pp_printf (pp, "node_%i:en_%i:%s",
    4566          878 :                  snode->m_id, enode.m_index, compass_pt);
    4567              :     else
    4568           12 :       pp_printf (pp, "en_%i:%s",
    4569           12 :                  enode.m_index, compass_pt);
    4570          890 :   }
    4571              : 
    4572              :   /* Concisely print a TD element for ENODE, showing the index, status,
    4573              :      and any saved_diagnostics at the enode.  Colorize it to show sm-state.
    4574              : 
    4575              :      Ideally we'd dump ENODE's state here, hidden behind some kind of
    4576              :      interactive disclosure method like a tooltip, so that the states
    4577              :      can be explored without overwhelming the graph.
    4578              :      However, I wasn't able to get graphviz/xdot to show tooltips on
    4579              :      individual elements within a HTML-like label.  */
    4580          433 :   void print_enode (graphviz_out *gv, const exploded_node *enode) const
    4581              :   {
    4582          433 :     pretty_printer *pp = gv->get_pp ();
    4583          433 :     pp_printf (pp, "<TD BGCOLOR=\"%s\">",
    4584              :                enode->get_dot_fillcolor ());
    4585          433 :     pp_printf (pp, "<TABLE BORDER=\"0\" PORT=\"en_%i\">", enode->m_index);
    4586          433 :     gv->begin_trtd ();
    4587          433 :     pp_printf (pp, "EN: %i", enode->m_index);
    4588          433 :     switch (enode->get_status ())
    4589              :       {
    4590            0 :       default:
    4591            0 :         gcc_unreachable ();
    4592            0 :       case exploded_node::status::worklist:
    4593            0 :         pp_string (pp, "(W)");
    4594            0 :         break;
    4595              :       case exploded_node::status::processed:
    4596              :         break;
    4597            6 :       case exploded_node::status::special:
    4598            6 :         pp_string (pp, "(S)");
    4599            6 :         break;
    4600           12 :       case exploded_node::status::merger:
    4601           12 :         pp_string (pp, "(M)");
    4602           12 :         break;
    4603            0 :       case exploded_node::status::bulk_merged:
    4604            0 :         pp_string (pp, "(BM)");
    4605            0 :         break;
    4606              :       }
    4607          433 :     gv->end_tdtr ();
    4608              : 
    4609              :     /* Dump any saved_diagnostics at this enode.  */
    4610          437 :     for (unsigned i = 0; i < enode->get_num_diagnostics (); i++)
    4611              :       {
    4612            4 :         const saved_diagnostic *sd = enode->get_saved_diagnostic (i);
    4613            4 :         print_saved_diagnostic (gv, sd);
    4614              :       }
    4615          433 :     pp_printf (pp, "</TABLE>");
    4616          433 :     pp_printf (pp, "</TD>");
    4617          433 :   }
    4618              : 
    4619              :   /* Print a TABLE element for SD, showing the kind, the length of the
    4620              :      exploded_path, whether the path was feasible, and if infeasible,
    4621              :      what the problem was.  */
    4622            4 :   void print_saved_diagnostic (graphviz_out *gv,
    4623              :                                const saved_diagnostic *sd) const
    4624              :   {
    4625            4 :     pretty_printer *pp = gv->get_pp ();
    4626            4 :     gv->begin_trtd ();
    4627            4 :     pp_printf (pp, "<TABLE BORDER=\"0\">");
    4628            4 :     gv->begin_tr ();
    4629            4 :     pp_string (pp, "<TD BGCOLOR=\"green\">");
    4630            4 :     pp_printf (pp, "DIAGNOSTIC: %s", sd->m_d->get_kind ());
    4631            4 :     gv->end_tdtr ();
    4632            4 :     gv->begin_trtd ();
    4633            4 :     if (sd->get_best_epath ())
    4634            4 :       pp_printf (pp, "epath length: %i", sd->get_epath_length ());
    4635              :     else
    4636            0 :       pp_printf (pp, "no best epath");
    4637            4 :     gv->end_tdtr ();
    4638            4 :     if (const feasibility_problem *p = sd->get_feasibility_problem ())
    4639              :       {
    4640            0 :         gv->begin_trtd ();
    4641            0 :         pp_printf (pp, "INFEASIBLE at eedge %i: EN:%i -> EN:%i",
    4642            0 :                    p->m_eedge_idx,
    4643            0 :                    p->m_eedge.m_src->m_index,
    4644            0 :                    p->m_eedge.m_dest->m_index);
    4645            0 :         pp_write_text_as_html_like_dot_to_stream (pp);
    4646            0 :         gv->end_tdtr ();
    4647            0 :         gv->begin_trtd ();
    4648            0 :         p->m_eedge.m_sedge->dump (pp);
    4649            0 :         pp_write_text_as_html_like_dot_to_stream (pp);
    4650            0 :         gv->end_tdtr ();
    4651              :         /* Ideally we'd print p->m_model here; see the notes above about
    4652              :            tooltips.  */
    4653              :       }
    4654            4 :     pp_printf (pp, "</TABLE>");
    4655            4 :     gv->end_tdtr ();
    4656            4 :   }
    4657              : 
    4658              :   const exploded_graph &m_eg;
    4659              :   std::vector<std::vector <exploded_node *> > m_enodes_per_snode_id;
    4660              : };
    4661              : 
    4662              : /* Implement -fdump-analyzer-json.  */
    4663              : 
    4664              : static void
    4665            0 : dump_analyzer_json (const supergraph &sg,
    4666              :                     const exploded_graph &eg)
    4667              : {
    4668            0 :   auto_timevar tv (TV_ANALYZER_DUMP);
    4669            0 :   char *filename = concat (dump_base_name, ".analyzer.json.gz", nullptr);
    4670            0 :   gzFile output = gzopen (filename, "w");
    4671            0 :   if (!output)
    4672              :     {
    4673            0 :       error_at (UNKNOWN_LOCATION, "unable to open %qs for writing", filename);
    4674            0 :       free (filename);
    4675            0 :       return;
    4676              :     }
    4677              : 
    4678            0 :   auto toplev_obj = std::make_unique<json::object> ();
    4679            0 :   toplev_obj->set ("sgraph", sg.to_json ());
    4680            0 :   toplev_obj->set ("egraph", eg.to_json ());
    4681              : 
    4682            0 :   pretty_printer pp;
    4683            0 :   toplev_obj->print (&pp, flag_diagnostics_json_formatting);
    4684            0 :   pp_formatted_text (&pp);
    4685              : 
    4686            0 :   if (gzputs (output, pp_formatted_text (&pp)) == EOF
    4687            0 :       || gzclose (output))
    4688            0 :     error_at (UNKNOWN_LOCATION, "error writing %qs", filename);
    4689              : 
    4690            0 :   free (filename);
    4691            0 : }
    4692              : 
    4693              : /* Concrete subclass of on_ana_init, allowing plugins to register
    4694              :    new state machines.  */
    4695              : 
    4696              : class impl_on_ana_init : public gcc::topics::analyzer_events::on_ana_init
    4697              : {
    4698              : public:
    4699           38 :   impl_on_ana_init (std::vector<std::unique_ptr<state_machine>> &checkers,
    4700              :                     known_function_manager &known_fn_mgr,
    4701              :                     logger *logger)
    4702           38 :   : m_checkers (checkers),
    4703           38 :     m_known_fn_mgr (known_fn_mgr),
    4704           38 :     m_logger (logger)
    4705              :   {}
    4706              : 
    4707              :   void
    4708            1 :   register_state_machine (std::unique_ptr<state_machine> sm)
    4709              :     const final override
    4710              :   {
    4711            1 :     LOG_SCOPE (m_logger);
    4712            1 :     m_checkers.push_back (std::move (sm));
    4713            1 :   }
    4714              : 
    4715              :   void
    4716          107 :   register_known_function (const char *name,
    4717              :                            std::unique_ptr<known_function> kf)
    4718              :     const final override
    4719              :   {
    4720          107 :     LOG_SCOPE (m_logger);
    4721          107 :     m_known_fn_mgr.add (name, std::move (kf));
    4722          107 :   }
    4723              : 
    4724              :   logger *
    4725           39 :   get_logger () const final override
    4726              :   {
    4727           39 :     return m_logger;
    4728              :   }
    4729              : 
    4730              : private:
    4731              :   std::vector<std::unique_ptr<state_machine>> &m_checkers;
    4732              :   known_function_manager &m_known_fn_mgr;
    4733              :   logger *m_logger;
    4734              : };
    4735              : 
    4736              : static void
    4737        13900 : maybe_dump_supergraph (const supergraph &sg, const char *name,
    4738              :                        const dot_annotator *annotator = nullptr,
    4739              :                        const exploded_graph *eg = nullptr)
    4740              : {
    4741        13900 :   static int dump_idx = 0;
    4742        13900 :   if (!flag_dump_analyzer_supergraph)
    4743        13880 :     return;
    4744              : 
    4745           20 :   auto_timevar tv (TV_ANALYZER_DUMP);
    4746           20 :   std::string filename (dump_base_name);
    4747           20 :   filename += ".supergraph.";
    4748           40 :   filename += std::to_string (dump_idx++);
    4749           20 :   filename += ".";
    4750           20 :   filename += name;
    4751           20 :   filename += ".dot";
    4752           20 :   supergraph::dump_args_t args
    4753              :     ((enum supergraph_dot_flags)SUPERGRAPH_DOT_SHOW_BBS,
    4754              :      annotator,
    4755           20 :      eg);
    4756           20 :   sg.dump_dot (filename.c_str (), args);
    4757           20 : }
    4758              : 
    4759              : /* Run the analysis "engine".  */
    4760              : 
    4761              : void
    4762         3474 : impl_run_checkers (logger *logger)
    4763              : {
    4764         3474 :   LOG_SCOPE (logger);
    4765              : 
    4766         3474 :   if (logger)
    4767              :     {
    4768            5 :       logger->log ("BITS_BIG_ENDIAN: %i", BITS_BIG_ENDIAN ? 1 : 0);
    4769            5 :       logger->log ("BYTES_BIG_ENDIAN: %i", BYTES_BIG_ENDIAN ? 1 : 0);
    4770            5 :       logger->log ("WORDS_BIG_ENDIAN: %i", WORDS_BIG_ENDIAN ? 1 : 0);
    4771            5 :       log_stashed_constants (logger);
    4772              :     }
    4773              : 
    4774              :   /* If using LTO, ensure that the cgraph nodes have function bodies.  */
    4775         3474 :   cgraph_node *node;
    4776        14266 :   FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
    4777        10792 :     node->get_untransformed_body ();
    4778              : 
    4779         3474 :   region_model_manager mgr;
    4780              : 
    4781              :   /* Create the supergraph.  */
    4782         3474 :   supergraph sg (mgr, logger);
    4783              : 
    4784         3474 :   maybe_dump_supergraph (sg, "original");
    4785              : 
    4786         3474 :   sg.fixup_locations (logger);
    4787              : 
    4788         3474 :   maybe_dump_supergraph (sg, "fixup-locations");
    4789              : 
    4790         3474 :   engine eng (mgr, &sg);
    4791              : 
    4792         3474 :   state_purge_map *purge_map = nullptr;
    4793         3474 :   if (flag_analyzer_state_purge)
    4794         3463 :     purge_map = new state_purge_map (sg, eng.get_model_manager (), logger);
    4795              : 
    4796         3474 :   if (flag_analyzer_simplify_supergraph)
    4797              :     {
    4798         3474 :       sg.simplify (logger);
    4799         3474 :       maybe_dump_supergraph (sg, "simplified");
    4800              :     }
    4801              : 
    4802         3474 :   sg.sort_nodes (logger);
    4803         3474 :   maybe_dump_supergraph (sg, "sorted");
    4804              : 
    4805         3474 :   if (flag_dump_analyzer_state_purge)
    4806              :     {
    4807            4 :       auto_timevar tv (TV_ANALYZER_DUMP);
    4808            4 :       state_purge_annotator a (purge_map);
    4809            4 :       char *filename = concat (dump_base_name, ".state-purge.dot", nullptr);
    4810            4 :       supergraph::dump_args_t args ((enum supergraph_dot_flags)0, &a, nullptr);
    4811            4 :       sg.dump_dot (filename, args);
    4812            4 :       free (filename);
    4813            4 :     }
    4814              : 
    4815         3474 :   auto checkers = make_checkers (logger);
    4816              : 
    4817         3474 :   register_known_functions (*eng.get_known_function_manager (),
    4818         3474 :                             *eng.get_model_manager ());
    4819              : 
    4820         3474 :   if (auto channel
    4821         3474 :         = g->get_channels ().analyzer_events_channel.get_if_active ())
    4822           38 :     channel->publish (impl_on_ana_init (checkers,
    4823           38 :                                         *eng.get_known_function_manager (),
    4824           38 :                                         logger));
    4825              : 
    4826         3474 :   if (logger)
    4827              :     {
    4828            5 :       int i = 0;
    4829           40 :       for (auto &sm : checkers)
    4830           35 :         logger->log ("checkers[%i]: %s", ++i, sm->get_name ());
    4831              :     }
    4832              : 
    4833              :   /* Extrinsic state shared by nodes in the graph.  */
    4834         3474 :   const extrinsic_state ext_state (std::move (checkers), &eng, logger);
    4835              : 
    4836         3474 :   const analysis_plan plan (sg, logger);
    4837              : 
    4838              :   /* The exploded graph.  */
    4839         3474 :   exploded_graph eg (sg, logger, ext_state, purge_map, plan,
    4840         3474 :                      analyzer_verbosity);
    4841              : 
    4842              :   /* Add entrypoints to the graph for externally-callable functions.  */
    4843         3474 :   eg.build_initial_worklist ();
    4844              : 
    4845              :   /* Now process the worklist, exploring the <point, state> graph.  */
    4846         3474 :   eg.process_worklist ();
    4847              : 
    4848         3474 :   if (warn_analyzer_infinite_loop)
    4849         3474 :     eg.detect_infinite_loops ();
    4850              : 
    4851         3474 :   if (flag_dump_analyzer_exploded_graph)
    4852              :     {
    4853            4 :       auto_timevar tv (TV_ANALYZER_DUMP);
    4854            4 :       char *filename
    4855            4 :         = concat (dump_base_name, ".eg.dot", nullptr);
    4856            4 :       exploded_graph::dump_args_t args (eg);
    4857            4 :       root_cluster c;
    4858            4 :       eg.dump_dot (filename, &c, args);
    4859            4 :       free (filename);
    4860            4 :     }
    4861              : 
    4862              :   /* Now emit any saved diagnostics.  */
    4863         3474 :   eg.get_diagnostic_manager ().emit_saved_diagnostics (eg);
    4864              : 
    4865         3474 :   eg.dump_exploded_nodes ();
    4866              : 
    4867         3474 :   eg.log_stats ();
    4868              : 
    4869         3474 :   if (flag_dump_analyzer_supergraph)
    4870              :     {
    4871              :       /* Dump post-analysis form of supergraph.  */
    4872            4 :       exploded_graph_annotator a (eg);
    4873            4 :       maybe_dump_supergraph (sg, "eg", &a, &eg);
    4874            4 :     }
    4875              : 
    4876         3474 :   if (flag_dump_analyzer_json)
    4877            0 :     dump_analyzer_json (sg, eg);
    4878              : 
    4879         3474 :   if (flag_dump_analyzer_untracked)
    4880           23 :     eng.get_model_manager ()->dump_untracked_regions ();
    4881              : 
    4882         3474 :   delete purge_map;
    4883              : 
    4884              :   /* Free up any dominance info that we may have created.  */
    4885        14266 :   FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
    4886              :     {
    4887        10792 :       function *fun = node->get_fun ();
    4888        10792 :       free_dominance_info (fun, CDI_DOMINATORS);
    4889              :     }
    4890         3474 : }
    4891              : 
    4892              : /* Handle -fdump-analyzer and -fdump-analyzer-stderr.  */
    4893              : static FILE *dump_fout = nullptr;
    4894              : 
    4895              : /* Track if we're responsible for closing dump_fout.  */
    4896              : static bool owns_dump_fout = false;
    4897              : 
    4898              : /* If dumping is enabled, attempt to create dump_fout if it hasn't already
    4899              :    been opened.  Return it.  */
    4900              : 
    4901              : FILE *
    4902         6991 : get_or_create_any_logfile ()
    4903              : {
    4904         6991 :   if (!dump_fout)
    4905              :     {
    4906         6986 :       if (flag_dump_analyzer_stderr)
    4907            0 :         dump_fout = stderr;
    4908         6986 :       else if (flag_dump_analyzer)
    4909              :         {
    4910            5 :           char *dump_filename = concat (dump_base_name, ".analyzer.txt", nullptr);
    4911            5 :           dump_fout = fopen (dump_filename, "w");
    4912            5 :           free (dump_filename);
    4913            5 :           if (dump_fout)
    4914            5 :             owns_dump_fout = true;
    4915              :         }
    4916              :      }
    4917         6991 :   return dump_fout;
    4918              : }
    4919              : 
    4920              : /* External entrypoint to the analysis "engine".
    4921              :    Set up any dumps, then call impl_run_checkers.  */
    4922              : 
    4923              : void
    4924         3474 : run_checkers ()
    4925              : {
    4926              :   /* Save input_location.  */
    4927         3474 :   location_t saved_input_location = input_location;
    4928              : 
    4929         3474 :   {
    4930         3474 :     log_user the_logger (nullptr);
    4931         3474 :     get_or_create_any_logfile ();
    4932         3474 :     if (dump_fout)
    4933            5 :       the_logger.set_logger (new logger (dump_fout, 0, 0,
    4934            5 :                                          *global_dc->get_reference_printer ()));
    4935         3474 :     LOG_SCOPE (the_logger.get_logger ());
    4936              : 
    4937         3474 :     impl_run_checkers (the_logger.get_logger ());
    4938              : 
    4939              :     /* end of lifetime of the_logger (so that dump file is closed after the
    4940              :        various dtors run).  */
    4941         3474 :   }
    4942              : 
    4943         3474 :   if (owns_dump_fout)
    4944              :     {
    4945            5 :       fclose (dump_fout);
    4946            5 :       owns_dump_fout = false;
    4947            5 :       dump_fout = nullptr;
    4948              :     }
    4949              : 
    4950              :   /* Restore input_location.  Subsequent passes may assume that input_location
    4951              :      is some arbitrary value *not* in the block tree, which might be violated
    4952              :      if we didn't restore it.  */
    4953         3474 :   input_location = saved_input_location;
    4954         3474 : }
    4955              : 
    4956              : } // namespace ana
    4957              : 
    4958              : #endif /* #if ENABLE_ANALYZER */
        

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.