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

Generated by: LCOV version 2.5.0-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.