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