LCOV - code coverage report
Current view: top level - gcc/analyzer - supergraph-simplify.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 98.7 % 149 147
Test Date: 2026-02-28 14:20:25 Functions: 100.0 % 10 10
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* Simplifying the supergraph.
       2              :    Copyright (C) 2025-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              : #define INCLUDE_DEQUE
      22              : #define INCLUDE_SET
      23              : #include "analyzer/common.h"
      24              : 
      25              : #include "cgraph.h"
      26              : #include "timevar.h"
      27              : 
      28              : #include "analyzer/supergraph.h"
      29              : #include "analyzer/analyzer-logging.h"
      30              : #include "analyzer/supergraph-manipulation.h"
      31              : #include "analyzer/bar-chart.h"
      32              : 
      33              : #if ENABLE_ANALYZER
      34              : 
      35              : namespace ana {
      36              : 
      37              : namespace {
      38              : 
      39              : /* A class for tracking a set of simplifications to a supergraph.
      40              :    supergraph.m_nodes and supergraph.m_edges may contain deleted object
      41              :    during the lifetime of this class; they are removed at the end.  */
      42              : 
      43              : class simplifier
      44              : {
      45              : public:
      46         3377 :   simplifier (supergraph &sg,
      47              :               ana::logger *logger)
      48         3377 :   : m_sg (sg),
      49         3377 :     m_nodes_to_remove (),
      50         3377 :     m_edges_to_remove (),
      51         3377 :     m_logger (logger),
      52         3377 :     m_stats ()
      53              :   {
      54       197777 :     for (auto node : sg.m_nodes)
      55       187654 :       m_worklist.ensure_node_queued (node, logger);
      56         3377 :   }
      57              : 
      58         3377 :   ~simplifier ()
      59              :   {
      60              :     // Remove nodes from m_sg.m_nodes and delete them
      61         3377 :     m_sg.delete_nodes (m_nodes_to_remove);
      62              : 
      63              :     // Remove edges from m_sg.m_edges and delete them
      64         3377 :     {
      65         3377 :       unsigned read_index, write_index;
      66         3377 :       superedge **elem_ptr;
      67       190620 :       VEC_ORDERED_REMOVE_IF (m_sg.m_edges, read_index, write_index, elem_ptr,
      68         3377 :                              edge_to_be_removed_p (*elem_ptr));
      69         8691 :       for (auto iter : m_edges_to_remove)
      70         5314 :         delete iter;
      71              :     }
      72              : 
      73         3377 :     if (m_logger)
      74              :       {
      75            5 :         log_nesting_level sentinel (m_logger, "stats");
      76            5 :         m_stats.log (*m_logger);
      77            5 :       }
      78         3377 :   }
      79              : 
      80              :   /* Manipulations: logged, and refreshing the worklist.  */
      81              : 
      82              :   void
      83         5812 :   set_edge_dest (superedge *edge, supernode *new_dest)
      84              :   {
      85         5812 :     if (edge->m_dest == new_dest)
      86            0 :       return;
      87         5812 :     log_nesting_level sentinel
      88              :       (m_logger, "updating edge dest: SN: %i -> SN: {%i, %i}",
      89         5812 :        edge->m_src->m_id,
      90              :        edge->m_dest->m_id,
      91         5812 :        new_dest->m_id);
      92         5812 :     m_worklist.ensure_node_queued (edge->m_src, m_logger);
      93         5812 :     m_worklist.ensure_node_queued (edge->m_dest, m_logger);
      94         5812 :     m_worklist.ensure_node_queued (new_dest, m_logger);
      95              : 
      96         5812 :     edge->set_dest (new_dest);
      97              : 
      98         5812 :     m_stats.m_num_set_edge_dest++;
      99         5812 :   }
     100              : 
     101              :   void
     102        10879 :   remove_node (supernode *node)
     103              :   {
     104        10879 :     log_nesting_level sentinel
     105        10879 :       (m_logger, "removing node: SN: %i", node->m_id);
     106        32061 :     for (auto in_edge : node->m_preds)
     107            0 :       remove_edge (in_edge);
     108        37449 :     for (auto out_edge : node->m_succs)
     109         5314 :       remove_edge (out_edge);
     110        10879 :     m_nodes_to_remove.insert (node);
     111        10879 :     m_stats.m_num_remove_node++;
     112        10879 :   }
     113              : 
     114              :   void
     115         5314 :   remove_edge (superedge *edge)
     116              :   {
     117         5314 :     log_nesting_level sentinel
     118              :       (m_logger, "removing edge dest: SN: %i -> SN: %i",
     119         5314 :        edge->m_src->m_id,
     120         5314 :        edge->m_dest->m_id);
     121         5314 :     gcc_assert (!edge->preserve_p ());
     122              : 
     123         5314 :     m_worklist.ensure_node_queued (edge->m_src, m_logger);
     124         5314 :     m_worklist.ensure_node_queued (edge->m_dest, m_logger);
     125              : 
     126         5314 :     edge->m_src->remove_out_edge (edge);
     127         5314 :     edge->m_dest->remove_in_edge (edge);
     128              : 
     129         5314 :     m_edges_to_remove.insert (edge);
     130              : 
     131         5314 :     m_stats.m_num_remove_edge++;
     132         5314 :   }
     133              : 
     134              :   /* High level ops.  */
     135              : 
     136              :   supernode *
     137       209881 :   pop_next_node_in_queue ()
     138              :   {
     139       209881 :     return m_worklist.pop ();
     140              :   }
     141              : 
     142              :   void
     143       206504 :   consider_node (supernode *node)
     144              :   {
     145       206504 :     m_stats.m_num_iterations++;
     146              : 
     147       206504 :     log_nesting_level sentinel (m_logger, "considering SN: %i", node->m_id);
     148              : 
     149              :     /* Eliminate nodes with no in-edges that aren't function entry nodes.  */
     150       217095 :     if (node->m_preds.length () == 0
     151       206504 :         && !node->entry_p ())
     152              :       {
     153        10879 :         log_nesting_level s2 (m_logger, "no in-edges");
     154        10879 :         remove_node (node);
     155        10879 :         return;
     156        10879 :       }
     157              : 
     158              :     /* Handle nodes with a single out-edge.  */
     159       364561 :     if (node->m_succs.length () == 1)
     160       174346 :       if (consider_single_out_edge (node, node->m_succs[0]))
     161              :         return;
     162       206504 :   }
     163              : 
     164              :   bool
     165       174346 :   consider_single_out_edge (supernode *node,
     166              :                             superedge *single_out_edge)
     167              :   {
     168       174346 :     if (node->preserve_p ())
     169              :       {
     170        17746 :         log_nesting_level s3
     171              :           (m_logger,
     172        17746 :            "node has preserve_p flag; preserving");
     173        17746 :         return false;
     174        17746 :       }
     175       156600 :     if (single_out_edge->preserve_p ())
     176              :       {
     177         1702 :         log_nesting_level s3
     178              :           (m_logger,
     179         1702 :            "edge has preserve_p flag; preserving");
     180         1702 :         return false;
     181              :       }
     182              : 
     183              :     /* Is the single out-edge a no-op?  */
     184       154898 :     if (!single_out_edge->get_op ())
     185              :       {
     186              :         /* If the node doesn't add useful location information, we can
     187              :            redirect all in-edges to the node to point at the outedge's dst.  */
     188        33244 :         log_nesting_level s2 (m_logger,
     189              :                               "single outedge is no-op (to SN: %i)",
     190        33244 :                               node->m_succs[0]->m_dest->m_id);
     191        33244 :         bool redirect = false;
     192        33244 :         if (node->m_loc == UNKNOWN_LOCATION)
     193              :           {
     194         1317 :             log_nesting_level s3
     195              :               (m_logger,
     196         1317 :                "node is at UNKNOWN_LOCATION; redirecting in-edges");
     197         1317 :             redirect = true;
     198         1317 :           }
     199        31927 :         else if (node->m_loc == single_out_edge->m_dest->m_loc)
     200              :           {
     201         4093 :             log_nesting_level s3
     202              :               (m_logger,
     203         4093 :                "node has same location as successor; redirecting in-edges");
     204         4093 :             redirect = true;
     205         4093 :           }
     206              : 
     207         5410 :         if (redirect)
     208              :           {
     209        21774 :             for (auto in_edge : node->m_preds)
     210         5812 :               set_edge_dest (in_edge, single_out_edge->m_dest);
     211              :             return true;
     212              :           }
     213              : 
     214              :         return false;
     215        33244 :       }
     216              : 
     217              :     gcc_assert (single_out_edge->get_op ());
     218              : 
     219              :     return false;
     220              :   }
     221              : 
     222              : private:
     223       187243 :   bool edge_to_be_removed_p (superedge *edge)
     224              :   {
     225       187243 :     return m_edges_to_remove.find (edge) != m_edges_to_remove.end ();
     226              :   }
     227              : 
     228              :   supergraph &m_sg;
     229              :   supergraph_manipulation::worklist m_worklist;
     230              :   std::set<supernode *> m_nodes_to_remove;
     231              :   std::set<superedge *> m_edges_to_remove;
     232              :   ana::logger *m_logger;
     233              :   struct stats
     234              :   {
     235              :     stats () = default;
     236              : 
     237            5 :     void log (ana::logger &logger)
     238              :     {
     239            5 :       logger.log ("# iterations taken: " HOST_SIZE_T_PRINT_UNSIGNED,
     240            5 :                   (fmt_size_t)m_num_iterations);
     241            5 :       logger.log ("# nodes removed: " HOST_SIZE_T_PRINT_UNSIGNED,
     242            5 :                   (fmt_size_t)m_num_remove_node);
     243            5 :       logger.log ("# edges removed: " HOST_SIZE_T_PRINT_UNSIGNED,
     244            5 :                   (fmt_size_t)m_num_remove_edge);
     245            5 :       logger.log ("# set_edge_dest: " HOST_SIZE_T_PRINT_UNSIGNED,
     246            5 :                   (fmt_size_t)m_num_set_edge_dest);
     247            5 :     }
     248              : 
     249              :     size_t m_num_iterations;
     250              :     size_t m_num_remove_node;
     251              :     size_t m_num_remove_edge;
     252              :     size_t m_num_set_edge_dest;
     253              : 
     254              :   } m_stats;
     255              : };
     256              : 
     257              : } // anonymous namespace
     258              : 
     259              : void
     260         6754 : supergraph::log_stats (logger *logger) const
     261              : {
     262         6754 :   if (!logger)
     263              :     return;
     264              : 
     265           20 :   logger->log ("# nodes: %u", m_nodes.length ());
     266           20 :   logger->log ("# edges: %u", m_edges.length ());
     267              : 
     268              :   /* Show per-function bar charts of supernodes per function.  */
     269           10 :   {
     270           10 :     bar_chart snodes_per_function;
     271           10 :     logger->log ("snodes per function:");
     272              : 
     273           10 :     cgraph_node *cgnode;
     274           22 :     FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (cgnode)
     275              :       {
     276           12 :         function *fn = cgnode->get_fun ();
     277              : 
     278           12 :         int snodes_for_this_function = 0;
     279          251 :         for (auto node : m_nodes)
     280          215 :           if (node->get_function () == fn)
     281          162 :             ++snodes_for_this_function;
     282              : 
     283           12 :         pretty_printer pp;
     284           12 :         pp_format_decoder (&pp) = default_tree_printer;
     285           12 :         pp_printf (&pp, "%qD", fn->decl);
     286           12 :         snodes_per_function.add_item (pp_formatted_text (&pp),
     287              :                                       snodes_for_this_function);
     288           12 :       }
     289              : 
     290           10 :     snodes_per_function.print (logger->get_printer ());
     291           10 :   }
     292              : }
     293              : 
     294              : void
     295         3377 : supergraph::simplify (logger *logger)
     296              : {
     297         3377 :   auto_timevar tv (TV_ANALYZER_SUPERGRAPH_SIMPLIFY);
     298         3377 :   LOG_SCOPE (logger);
     299              : 
     300         3377 :   {
     301         3377 :     log_nesting_level sentinel (logger, "before simplification:");
     302         3377 :     log_stats (logger);
     303         3377 :   }
     304              : 
     305         3377 :   {
     306         3377 :     simplifier opt (*this, logger);
     307       209881 :     while (supernode *node = opt.pop_next_node_in_queue ())
     308       206504 :       opt.consider_node (node);
     309         3377 :   }
     310              : 
     311         3377 :   {
     312         3377 :     log_nesting_level sentinel (logger, "after simplification");
     313         3377 :     log_stats (logger);
     314         3377 :   }
     315         3377 : }
     316              : 
     317              : } // namespace ana
     318              : 
     319              : #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.