LCOV - code coverage report
Current view: top level - gcc - tree-diagnostic-cfg.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 84.8 % 158 134
Test Date: 2026-02-28 14:20:25 Functions: 92.3 % 13 12
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* Generating diagnostics graphs from GCC CFGs.
       2              :    Copyright (C) 2025 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 under
       8              : the terms of the GNU General Public License as published by the Free
       9              : Software Foundation; either version 3, or (at your option) any later
      10              : version.
      11              : 
      12              : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      13              : WARRANTY; without even the implied warranty of MERCHANTABILITY or
      14              : FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      15              : 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_LIST
      22              : #define INCLUDE_MAP
      23              : #define INCLUDE_STRING
      24              : #define INCLUDE_VECTOR
      25              : #include "config.h"
      26              : #include "system.h"
      27              : #include "coretypes.h"
      28              : #include "version.h"
      29              : #include "tree.h"
      30              : #include "lazily-created.h"
      31              : #include "diagnostics/digraphs.h"
      32              : #include "diagnostics/dumping.h"
      33              : #include "diagnostic.h"
      34              : #include "tree-pass.h"
      35              : #include "custom-sarif-properties/cfg.h"
      36              : #include "tree-diagnostic-sink-extensions.h"
      37              : #include "tree-logical-location.h"
      38              : #include "tree-pass.h"
      39              : #include "function.h"
      40              : #include "topics/pass-events.h"
      41              : #include "diagnostics/digraphs.h"
      42              : #include "diagnostics/sink.h"
      43              : #include "context.h"
      44              : #include "channels.h"
      45              : #include "bitmap.h"
      46              : #include "sbitmap.h"
      47              : #include "cfghooks.h"
      48              : #include "cfganal.h"
      49              : #include "cfgloop.h"
      50              : #include "graph.h"
      51              : #include "basic-block.h"
      52              : #include "cfg.h"
      53              : 
      54              : 
      55              : namespace {
      56              :   namespace graph_properties = custom_sarif_properties::cfg::graph;
      57              :   namespace node_properties = custom_sarif_properties::cfg::node;
      58              :   namespace edge_properties = custom_sarif_properties::cfg::edge;
      59              : }
      60              : 
      61              : /* Disable warnings about quoting issues in the pp_xxx calls below
      62              :    that (intentionally) don't follow GCC diagnostic conventions.  */
      63              : #if __GNUC__ >= 10
      64              : #  pragma GCC diagnostic push
      65              : #  pragma GCC diagnostic ignored "-Wformat-diag"
      66              : #endif
      67              : 
      68          180 : class cfg_diagnostic_digraph
      69              :   : public lazily_created<diagnostics::digraphs::digraph>
      70              : {
      71              : public:
      72           90 :   cfg_diagnostic_digraph (function *fun,
      73              :                           opt_pass *pass)
      74           90 :   : m_fun (fun), m_pass (pass)
      75              :   {}
      76              : 
      77              :   std::unique_ptr<diagnostics::digraphs::digraph>
      78           90 :   create_object () const final override
      79              :   {
      80           90 :     auto g = std::make_unique<diagnostics::digraphs::digraph> ();
      81           90 :     g->set_graph_kind ("cfg");
      82              : 
      83           90 :     pretty_printer pp;
      84           90 :     pp_printf (&pp, "%s: %s", function_name (m_fun), m_pass->name);
      85           90 :     g->set_description (pp_formatted_text (&pp));
      86              : 
      87           90 :     g->set_property (graph_properties::pass_name, m_pass->name);
      88           90 :     g->set_property (graph_properties::pass_number, m_pass->static_pass_number);
      89              : 
      90           90 :     add_cluster_for_function (*g, m_fun);
      91          180 :     return g;
      92           90 :   }
      93              : 
      94              :   void
      95           90 :   add_cluster_for_function (diagnostics::digraphs::digraph &g,
      96              :                             function *fun) const
      97              :   {
      98           90 :     const char *funcname = function_name (fun);
      99           90 :     auto cluster = std::make_unique<diagnostics::digraphs::node> (g, funcname);
     100           90 :     cluster->set_property (node_properties::kind, "function");
     101              : 
     102           90 :     bb_to_node_map node_map;
     103           90 :     add_cfg_nodes (g, node_map, *cluster, fun);
     104           90 :     add_cfg_edges (g, node_map, fun);
     105           90 :     g.add_node (std::move (cluster));
     106           90 :   }
     107              : 
     108              : private:
     109              :   typedef std::map<basic_block, diagnostics::digraphs::node *> bb_to_node_map;
     110              : 
     111              :   /* Add a basic block BB belonging to the function with FUNCDEF_NO
     112              :      as its unique number.  */
     113              :   void
     114          540 :   add_cfg_node (diagnostics::digraphs::digraph &g,
     115              :                 bb_to_node_map &node_map,
     116              :                 diagnostics::digraphs::node &parent_node,
     117              :                 int funcdef_no,
     118              :                 basic_block bb) const
     119              :   {
     120          540 :     pretty_printer pp;
     121          540 :     pp_printf (&pp, "fn_%d_basic_block_%d",
     122              :                funcdef_no, bb->index);
     123          540 :     std::string name (pp_formatted_text (&pp));
     124          540 :     auto bb_node = std::make_unique<diagnostics::digraphs::node> (g, name);
     125          540 :     node_map.insert ({bb, bb_node.get ()});
     126              : 
     127          540 :     bb_node->set_property (node_properties::kind, "basic_block");
     128              : 
     129          540 :     dump_bb_as_sarif_properties (nullptr,
     130          540 :                                  bb_node->ensure_property_bag (),
     131              :                                  bb);
     132              : 
     133          540 :     parent_node.add_child (std::move (bb_node));
     134          540 :   }
     135              : 
     136              :   /* Add all successor edges of a basic block BB belonging to the function
     137              :      with FUNCDEF_NO as its unique number.  */
     138              :   void
     139          540 :   add_cfg_node_succ_edges (diagnostics::digraphs::digraph &g,
     140              :                            bb_to_node_map &node_map,
     141              :                            int /*funcdef_no*/,
     142              :                            basic_block bb) const
     143              :   {
     144          540 :     edge e;
     145          540 :     edge_iterator ei;
     146         1080 :     FOR_EACH_EDGE (e, ei, bb->succs)
     147              :       {
     148          540 :         auto src_node = node_map.find (e->src);
     149          540 :         gcc_assert (src_node != node_map.end ());
     150          540 :         auto dst_node = node_map.find (e->dest);
     151          540 :         gcc_assert (dst_node != node_map.end ());
     152              : 
     153          540 :         auto diag_edge
     154         1080 :           = std::make_unique<diagnostics::digraphs::edge> (g, nullptr,
     155          540 :                                                            *(src_node->second),
     156          540 :                                                            *(dst_node->second));
     157          540 :         auto flag_arr = std::make_unique<json::array> ();
     158              : #define DEF_EDGE_FLAG(NAME,IDX) \
     159              :         { handle_edge_flag (*flag_arr, #NAME, (e->flags & EDGE_##NAME)); }
     160              : #include "cfg-flags.def"
     161              : #undef DEF_EDGE_FLAG
     162              : 
     163          540 :         auto &bag = diag_edge->ensure_property_bag ();
     164          540 :         bag.set<json::array> (edge_properties::flags.m_key.get (),
     165              :                               std::move (flag_arr));
     166              : 
     167          540 :         if (e->probability.initialized_p ())
     168           96 :           diag_edge->set_property (edge_properties::probability_pc,
     169           96 :                                    (e->probability.to_reg_br_prob_base ()
     170              :                                     * 100 / REG_BR_PROB_BASE));
     171              : 
     172          540 :         g.add_edge (std::move (diag_edge));
     173          540 :       }
     174          540 :   }
     175              : 
     176              :   void
     177         9720 :   handle_edge_flag (json::array &flag_arr,
     178              :                     const char *flag_name,
     179              :                     bool value) const
     180              :   {
     181          920 :     if (value)
     182          380 :       flag_arr.append_string (flag_name);
     183              :   }
     184              : 
     185              :   /* Add all the basic blocks in the CFG in case loops are not available.
     186              :      First compute a topological order of the blocks to get a good ranking of
     187              :      the nodes.  Then, if any nodes are not reachable from ENTRY, add them at
     188              :      the end.  */
     189              :   void
     190           40 :   add_cfg_nodes_no_loops (diagnostics::digraphs::digraph &g,
     191              :                           bb_to_node_map &node_map,
     192              :                           diagnostics::digraphs::node &parent_node,
     193              :                           function *fun) const
     194              :   {
     195           40 :     int *rpo = XNEWVEC (int, n_basic_blocks_for_fn (fun));
     196           40 :     int i, n;
     197              : 
     198           40 :     auto_sbitmap visited (last_basic_block_for_fn (fun));
     199           40 :     bitmap_clear (visited);
     200              : 
     201           40 :     n = pre_and_rev_post_order_compute_fn (fun, NULL, rpo, true);
     202          280 :     for (i = n_basic_blocks_for_fn (fun) - n;
     203          280 :          i < n_basic_blocks_for_fn (fun); i++)
     204              :       {
     205          240 :         basic_block bb = BASIC_BLOCK_FOR_FN (fun, rpo[i]);
     206          240 :         add_cfg_node (g, node_map, parent_node, fun->funcdef_no, bb);
     207          240 :         bitmap_set_bit (visited, bb->index);
     208              :       }
     209           40 :     free (rpo);
     210              : 
     211           40 :     if (n != n_basic_blocks_for_fn (fun))
     212              :       {
     213              :         /* Some blocks are unreachable.  We still want to dump them.  */
     214            0 :         basic_block bb;
     215            0 :         FOR_ALL_BB_FN (bb, fun)
     216            0 :           if (! bitmap_bit_p (visited, bb->index))
     217            0 :             add_cfg_node (g, node_map, parent_node, fun->funcdef_no, bb);
     218              :       }
     219           40 :   }
     220              : 
     221              : /* Add all the basic blocks in LOOP.  Add the blocks in breath-first
     222              :    order to get a good ranking of the nodes.  This function is recursive:
     223              :    It first adds inner loops, then the body of LOOP itself.  */
     224              : 
     225              :   void
     226           50 :   add_cfg_nodes_for_loop (diagnostics::digraphs::digraph &g,
     227              :                           bb_to_node_map &node_map,
     228              :                           diagnostics::digraphs::node *parent_node,
     229              :                           int funcdef_no,
     230              :                           class loop *loop) const
     231              :   {
     232           50 :     namespace loop_properties = custom_sarif_properties::cfg::loop;
     233              : 
     234           50 :     gcc_assert (parent_node);
     235           50 :     diagnostics::digraphs::node &orig_parent_node = *parent_node;
     236              : 
     237           50 :     unsigned int i;
     238           50 :     std::unique_ptr<diagnostics::digraphs::node> loop_node;
     239              : 
     240           50 :     if (loop->header != NULL
     241           50 :         && loop->latch != EXIT_BLOCK_PTR_FOR_FN (cfun))
     242              :       {
     243            0 :         pretty_printer pp;
     244            0 :         pp_printf (&pp, "fun_%d_loop_%d", funcdef_no, loop->num);
     245            0 :         std::string name (pp_formatted_text (&pp));
     246            0 :         loop_node
     247            0 :           = std::make_unique<diagnostics::digraphs::node> (g, name);
     248            0 :         parent_node = loop_node.get ();
     249            0 :         loop_node->set_property (node_properties::kind, "loop");
     250            0 :         loop_node->set_property (loop_properties::num, loop->num);
     251            0 :         loop_node->set_property (loop_properties::depth, loop_depth (loop));
     252            0 :       }
     253              : 
     254           50 :     for (class loop *inner = loop->inner; inner; inner = inner->next)
     255            0 :       add_cfg_nodes_for_loop (g, node_map, parent_node, funcdef_no, inner);
     256              : 
     257           50 :     if (loop->header == NULL)
     258            0 :       return;
     259              : 
     260           50 :     basic_block *body;
     261           50 :     if (loop->latch == EXIT_BLOCK_PTR_FOR_FN (cfun))
     262           50 :       body = get_loop_body (loop);
     263              :     else
     264            0 :       body = get_loop_body_in_bfs_order (loop);
     265              : 
     266          350 :     for (i = 0; i < loop->num_nodes; i++)
     267              :       {
     268          300 :         basic_block bb = body[i];
     269          300 :         if (bb->loop_father == loop)
     270          300 :           add_cfg_node (g, node_map, *parent_node, funcdef_no, bb);
     271              :       }
     272              : 
     273           50 :     free (body);
     274              : 
     275           50 :     if (loop->latch != EXIT_BLOCK_PTR_FOR_FN (cfun))
     276              :       {
     277            0 :         gcc_assert (loop_node);
     278            0 :         orig_parent_node.add_child (std::move (loop_node));
     279              :       }
     280           50 :   }
     281              : 
     282              :   void
     283           90 :   add_cfg_nodes (diagnostics::digraphs::digraph &g,
     284              :                  bb_to_node_map &node_map,
     285              :                  diagnostics::digraphs::node &parent_node,
     286              :                  function *fun) const
     287              :   {
     288              :     /* ???  The loop and dominance APIs are dependent on fun == cfun.  */
     289           90 :     if (fun == cfun && loops_for_fn (fun))
     290           50 :       add_cfg_nodes_for_loop (g, node_map, &parent_node, fun->funcdef_no,
     291              :                               get_loop (fun, 0));
     292              :     else
     293           40 :       add_cfg_nodes_no_loops (g, node_map, parent_node, fun);
     294           90 :   }
     295              : 
     296              :   void
     297           90 :   add_cfg_edges (diagnostics::digraphs::digraph &g,
     298              :                  bb_to_node_map &node_map,
     299              :                  function *fun) const
     300              :   {
     301           90 :     basic_block bb;
     302              : 
     303              :     /* Save EDGE_DFS_BACK flag to dfs_back.  */
     304           90 :     auto_bitmap dfs_back;
     305           90 :     edge e;
     306           90 :     edge_iterator ei;
     307           90 :     unsigned int idx = 0;
     308          450 :     FOR_EACH_BB_FN (bb, fun)
     309          810 :       FOR_EACH_EDGE (e, ei, bb->succs)
     310              :       {
     311          450 :         if (e->flags & EDGE_DFS_BACK)
     312            0 :           bitmap_set_bit (dfs_back, idx);
     313          450 :         idx++;
     314              :       }
     315              : 
     316           90 :     mark_dfs_back_edges (fun);
     317          630 :     FOR_ALL_BB_FN (bb, fun)
     318          540 :       add_cfg_node_succ_edges (g, node_map, fun->funcdef_no, bb);
     319              : 
     320              :     /* Restore EDGE_DFS_BACK flag from dfs_back.  */
     321           90 :     idx = 0;
     322          450 :     FOR_EACH_BB_FN (bb, fun)
     323          810 :       FOR_EACH_EDGE (e, ei, bb->succs)
     324              :       {
     325          450 :         if (bitmap_bit_p (dfs_back, idx))
     326            0 :           e->flags |= EDGE_DFS_BACK;
     327              :         else
     328          450 :           e->flags &= ~EDGE_DFS_BACK;
     329          450 :         idx++;
     330              :       }
     331           90 :   }
     332              : 
     333              :   function *m_fun;
     334              :   opt_pass *m_pass;
     335              : };
     336              : 
     337              : #if __GNUC__ >= 10
     338              : #  pragma GCC diagnostic pop
     339              : #endif
     340              : 
     341              : namespace pass_events = gcc::topics::pass_events;
     342              : 
     343              : /* A diagnostics::sink::extension which subscribes to pass_events
     344              :    and responds to "after_pass" events by adding a diagnostics digraph
     345              :    for the CFG for the relevant function.  */
     346              : 
     347              : class compiler_capture_cfgs : public diagnostics::sink::extension
     348              : {
     349              : public:
     350            2 :   compiler_capture_cfgs (diagnostics::sink &sink)
     351            2 :   : extension (sink),
     352            2 :     m_event_subscriber (sink)
     353              :   {
     354            2 :     g->get_channels ().pass_events_channel.add_subscriber (m_event_subscriber);
     355            2 :   }
     356              : 
     357              :   void
     358            0 :   dump (FILE *out, int indent) const
     359              :   {
     360            0 :     diagnostics::dumping::emit_heading (out, indent, "compiler_capture_cfgs");
     361            0 :   }
     362              : 
     363              : private:
     364              :   class event_subscriber : public pass_events::subscriber
     365              :   {
     366              :   public:
     367            2 :     event_subscriber (diagnostics::sink &sink) : m_sink (sink) {}
     368          416 :     void on_message (const pass_events::before_pass &) final override
     369              :     {
     370          416 :     }
     371          156 :     void on_message (const pass_events::after_pass &m) final override
     372              :     {
     373          156 :       if (m.fun
     374          124 :           && m.fun->cfg
     375          114 :           && m.pass->static_pass_number > 0)
     376           90 :         m_sink.report_digraph_for_logical_location
     377           90 :           (cfg_diagnostic_digraph (m.fun, m.pass),
     378              :            tree_logical_location_manager::key_from_tree (m.fun->decl));
     379          156 :     }
     380              : 
     381              :   private:
     382              :     diagnostics::sink &m_sink;
     383              :   } m_event_subscriber;
     384              : };
     385              : 
     386              : std::unique_ptr<diagnostics::sink::extension>
     387            2 : compiler_extension_factory::make_cfg_extension (diagnostics::sink &sink) const
     388              : {
     389            2 :   return std::make_unique<compiler_capture_cfgs> (sink);
     390              : }
        

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.