LCOV - code coverage report
Current view: top level - gcc - path-coverage.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 91.7 % 301 276
Test Date: 2025-06-21 16:26:05 Functions: 82.4 % 17 14
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /* Calculate prime path coverage.
       2                 :             :    Copyright (C) 2024-2025 Free Software Foundation, Inc.
       3                 :             : 
       4                 :             : This file is part of GCC.
       5                 :             : 
       6                 :             : GCC is free software; you can redistribute it and/or modify it under
       7                 :             : the terms of the GNU General Public License as published by the Free
       8                 :             : Software Foundation; either version 3, or (at your option) any later
       9                 :             : version.
      10                 :             : 
      11                 :             : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      12                 :             : WARRANTY; without even the implied warranty of MERCHANTABILITY or
      13                 :             : FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      14                 :             : for more details.
      15                 :             : 
      16                 :             : You should have received a copy of the GNU General Public License
      17                 :             : along with GCC; see the file COPYING3.  If not see
      18                 :             : <http://www.gnu.org/licenses/>.  */
      19                 :             : 
      20                 :             : #include "config.h"
      21                 :             : #include "system.h"
      22                 :             : #include "coretypes.h"
      23                 :             : #include "diagnostic-core.h"
      24                 :             : #include "memmodel.h"
      25                 :             : #include "target.h"
      26                 :             : #include "function.h"
      27                 :             : #include "basic-block.h"
      28                 :             : #include "tree.h"
      29                 :             : #include "tree-cfg.h"
      30                 :             : #include "tree-nested.h"
      31                 :             : #include "cfg.h"
      32                 :             : #include "gimple.h"
      33                 :             : #include "gimple-iterator.h"
      34                 :             : #include "gimplify.h"
      35                 :             : #include "coverage.h"
      36                 :             : #include "ssa.h"
      37                 :             : #include "vec.h"
      38                 :             : #include "sbitmap.h"
      39                 :             : #include "graphds.h"
      40                 :             : #include "hash-map.h"
      41                 :             : #include "bitmap.h"
      42                 :             : #include "cfganal.h"
      43                 :             : 
      44                 :             : bool mark_dfs_back_edges (struct function *);
      45                 :             : vec<vec<int>> prime_paths (struct graph*, size_t);
      46                 :             : 
      47                 :             : namespace
      48                 :             : {
      49                 :             : 
      50                 :             : /* Check if all the successors of BB are abnormal, e.g. setjmp.  */
      51                 :             : bool
      52                 :         599 : all_abnormal_succs_p (basic_block bb)
      53                 :             : {
      54                 :        1259 :   for (edge e : bb->succs)
      55                 :         326 :     if (!(e->flags & EDGE_ABNORMAL))
      56                 :             :       return false;
      57                 :             :   return true;
      58                 :             : }
      59                 :             : 
      60                 :             : /* Build a struct graph equivalent to the CFG for the function FN.  The caller
      61                 :             :    must free the returned graph with free_graph.  The data field of every
      62                 :             :    vertex and edge point to the basic blocks and edges in the CFG.
      63                 :             : 
      64                 :             :    The CFG recording and gcov is not aware of abnormal edges, so they are
      65                 :             :    ignored here, too.  This means some paths are lost, e.g. those that involve
      66                 :             :    setjmp/longjmp.  They are still paths but would need more support from
      67                 :             :    profile.cc and gcov.cc to work.  */
      68                 :             : struct graph*
      69                 :         291 : cfg_as_graph (struct function* fn)
      70                 :             : {
      71                 :         291 :   struct graph *g = new_graph (n_basic_blocks_for_fn (fn));
      72                 :         291 :   basic_block entry = ENTRY_BLOCK_PTR_FOR_FN (fn);
      73                 :         291 :   basic_block exit = EXIT_BLOCK_PTR_FOR_FN (fn);
      74                 :             : 
      75                 :         291 :   g->vertices[entry->index].data = entry;
      76                 :         291 :   g->vertices[exit->index].data = exit;
      77                 :             : 
      78                 :         291 :   const unsigned ignore = EDGE_FAKE | EDGE_ABNORMAL | EDGE_ABNORMAL_CALL;
      79                 :         291 :   basic_block bb;
      80                 :        2590 :   FOR_EACH_BB_FN (bb, fn)
      81                 :             :     {
      82                 :        2299 :       g->vertices[bb->index].data = bb;
      83                 :       10282 :       for (edge e : bb->succs)
      84                 :        3385 :         if (!(e->flags & ignore) && e->dest != exit)
      85                 :        3017 :           add_edge (g, e->src->index, e->dest->index)->data = e;
      86                 :             :     }
      87                 :         291 :   return g;
      88                 :             : }
      89                 :             : 
      90                 :             : /* Check if BB's predecessor is the ENTRY_BLOCK, i.e. BB is the first real
      91                 :             :    block in the function.  */
      92                 :             : bool
      93                 :         633 : pred_entry_p (const_basic_block bb)
      94                 :             : {
      95                 :         948 :   return single_pred_p (bb) && single_pred (bb)->index == ENTRY_BLOCK;
      96                 :             : }
      97                 :             : 
      98                 :             : /* Find the prime paths for the function FN with the ENTRY and EXIT blocks
      99                 :             :    removed.  This can lead to empty paths when there is a fake edge to the exit
     100                 :             :    block, for example for abort functions or infinite loops.  Empty paths are
     101                 :             :    removed because the length of the returned vec is important.  */
     102                 :             : vec<vec<int>>
     103                 :         291 : find_prime_paths (struct function *fn)
     104                 :             : {
     105                 :         291 :   struct graph *cfg = cfg_as_graph (fn);
     106                 :         291 :   vec<vec<int>> paths = prime_paths (cfg, path_coverage_limit);
     107                 :             : 
     108                 :         291 :   bool any_empty = false;
     109                 :        3364 :   for (vec<int> &path : paths)
     110                 :             :     {
     111                 :             :       /* setjmp calls will be isolated basic blocks when ABNORMAL_EDGEs are
     112                 :             :          removed.  If a path is made up of just such a vertex it is pointless
     113                 :             :          and can be removed.  If a function is only __builtin_exit() (see
     114                 :             :          gcov-22.C) the CFG will only have one block and look like such an
     115                 :             :          island, and we want to preserve it.  */
     116                 :        2497 :       if (path.length () == 1
     117                 :         633 :           && !pred_entry_p (BASIC_BLOCK_FOR_FN (fn, path[0]))
     118                 :        3096 :           && all_abnormal_succs_p (BASIC_BLOCK_FOR_FN (fn, path[0])))
     119                 :         311 :         path.pop ();
     120                 :        4683 :       if (!path.is_empty () && path[0] == ENTRY_BLOCK)
     121                 :         288 :         path.ordered_remove (0);
     122                 :        4395 :       if (!path.is_empty () && path.last () == EXIT_BLOCK)
     123                 :           0 :         path.pop ();
     124                 :             : 
     125                 :        4994 :       if (path.is_empty ())
     126                 :             :         {
     127                 :         599 :           any_empty = true;
     128                 :         599 :           path.release ();
     129                 :             :         }
     130                 :             :     }
     131                 :             : 
     132                 :         291 :   unsigned ix1, ix2;
     133                 :         291 :   vec<int> *ptr;
     134                 :         291 :   if (any_empty)
     135                 :        2785 :     VEC_ORDERED_REMOVE_IF (paths, ix1, ix2, ptr, ptr->is_empty ());
     136                 :             : 
     137                 :         291 :   return paths;
     138                 :             : }
     139                 :             : 
     140                 :             : /* Return the edge between SRC and DST.  */
     141                 :             : edge
     142                 :        8451 : edge_between (struct function *fn, int src, int dst)
     143                 :             : {
     144                 :        8451 :   basic_block bbsrc = BASIC_BLOCK_FOR_FN (fn, src);
     145                 :        8451 :   basic_block bbdst = BASIC_BLOCK_FOR_FN (fn, dst);
     146                 :       49279 :   for (edge e : bbsrc->succs)
     147                 :       32377 :     if (e->dest == bbdst)
     148                 :        8451 :       return e;
     149                 :           0 :   gcc_unreachable ();
     150                 :             : }
     151                 :             : 
     152                 :             : /* Get the basic blocks of FN in topological order so that all predecessors
     153                 :             :    come before the vertex, while ignoring back edges.  */
     154                 :             : vec<basic_block>
     155                 :         288 : topsort (struct function *fn)
     156                 :             : {
     157                 :         288 :   vec<basic_block> blocks {};
     158                 :         288 :   auto_vec<int> order {};
     159                 :         288 :   blocks.reserve (n_basic_blocks_for_fn (fn));
     160                 :         288 :   order.safe_grow (n_basic_blocks_for_fn (fn));
     161                 :             : 
     162                 :         288 :   const int n = post_order_compute (order.address (), false, false);
     163                 :        2365 :   for (int i = 0; i < n; ++i)
     164                 :        2077 :     blocks.quick_push (BASIC_BLOCK_FOR_FN (fn, order[i]));
     165                 :         288 :   blocks.reverse ();
     166                 :         288 :   return blocks;
     167                 :         288 : }
     168                 :             : 
     169                 :             : /* Hasher for maps where the key is a pair of edge/basic_block and bucket id
     170                 :             :    (size_t).  */
     171                 :             : template <typename T>
     172                 :             : using bucket_hash = pair_hash <nofree_ptr_hash <T>,
     173                 :             :                                int_hash <size_t, size_t(-1)>>;
     174                 :             : 
     175                 :             : /* Hasher for {edge, bucket-id} keys.  */
     176                 :             : using edge_hash = bucket_hash <edge_def>;
     177                 :             : /* Hasher for {basic_block, bucket-id} keys.  */
     178                 :             : using block_hash = bucket_hash <basic_block_def>;
     179                 :             : 
     180                 :             : /* Find the union of the bitsets sets on the incoming edges of BB at BUCKET.
     181                 :             :    If no paths go through BB the returned bitset would be empty.  Bitsets are
     182                 :             :    looked up in ANDS, and if the nth bit is set then the nth path contains that
     183                 :             :    edge.  */
     184                 :             : uint64_t
     185                 :        2805 : union_incoming_bit_and (hash_map <edge_hash, uint64_t> &ands,
     186                 :             :                         const basic_block bb, size_t bucket)
     187                 :             : {
     188                 :        2805 :   uint64_t inc = 0;
     189                 :       12910 :   for (edge e : bb->preds)
     190                 :             :     {
     191                 :        4495 :       const uint64_t *val = ands.get ({e, bucket});
     192                 :        4495 :       inc |= (val ? *val : 0);
     193                 :             :     }
     194                 :        2805 :   return inc;
     195                 :             : }
     196                 :             : 
     197                 :             : 
     198                 :             : /* Check if the incoming edges have the power to modify the paths in flight for
     199                 :             :    BUCKET.  If all the incoming edges would apply the same bitmask the
     200                 :             :    BB+BUCKET will not actually update the path set, and we don't need to emit
     201                 :             :    an AND_EXPR and have a later fork distinguish the paths.  */
     202                 :             : bool
     203                 :         586 : can_change_path_p (hash_map <edge_hash, uint64_t> &ands,
     204                 :             :                    const basic_block bb, size_t bucket, uint64_t all_and)
     205                 :             : {
     206                 :        2012 :   for (edge e : bb->preds)
     207                 :             :     {
     208                 :         620 :       const uint64_t *e_and = ands.get ({e, bucket});
     209                 :         620 :       if (!e_and || *e_and != all_and)
     210                 :         586 :         return true;
     211                 :             :     }
     212                 :             :   return false;
     213                 :             : }
     214                 :             : 
     215                 :             : /* Check if all bits in BITSET are 1 for the target size TARGET_SIZE.  For a
     216                 :             :    32-bit target only the bits in the lower half should be set, and this should
     217                 :             :    return true when all bits in the lower half are set, even if the bitset type
     218                 :             :    have room for more bits.  */
     219                 :             : bool
     220                 :        1850 : all_bits_set_p (uint64_t bitset, size_t target_size)
     221                 :             : {
     222                 :        3023 :   return (size_t)popcount_hwi (bitset) == target_size;
     223                 :             : }
     224                 :             : 
     225                 :             : /* Create a constant or SSA name of GCOV_TYPE_NODE type and zero-assign to it
     226                 :             :    safely on the edge E.  If the edge is abnormal it is assumed the phi is
     227                 :             :    abnormal and we need an SSA name, otherwise fall back to a constant.  The
     228                 :             :    returned value is safe to use with add_phi_arg ().
     229                 :             : 
     230                 :             :    If the edge is abnormal we cannot insert on it directly, and instead
     231                 :             :    carefully add the assignment on the source block.  If that source block ends
     232                 :             :    with control flow (like those produced by _setjmp) we must insert before to
     233                 :             :    not break that invariant, otherwise insert after so that things like the
     234                 :             :    setjmp receiver is the first element of the basic block.  Doing the assign
     235                 :             :    is necessary as phis cannot resolve to constants.  */
     236                 :             : tree
     237                 :         725 : safe_insert_zero_phi_arg (edge e, tree gcov_type_node)
     238                 :             : {
     239                 :         725 :   tree cst = build_zero_cst (gcov_type_node);
     240                 :         725 :   if (!(e->flags & (EDGE_ABNORMAL | EDGE_ABNORMAL_CALL)))
     241                 :             :     return cst;
     242                 :             : 
     243                 :          43 :   tree zero = make_ssa_name (gcov_type_node);
     244                 :          43 :   gassign *put = gimple_build_assign (zero, cst);
     245                 :          43 :   gimple_stmt_iterator gsi = gsi_start_nondebug_after_labels_bb (e->src);
     246                 :          43 :   if (gimple_call_internal_p (*gsi, IFN_ABNORMAL_DISPATCHER)
     247                 :          43 :       || stmt_ends_bb_p (*gsi))
     248                 :          21 :     gsi_insert_before (&gsi, put, GSI_NEW_STMT);
     249                 :             :   else
     250                 :          22 :     gsi_insert_after (&gsi, put, GSI_NEW_STMT);
     251                 :             : 
     252                 :             :   return zero;
     253                 :             : }
     254                 :             : 
     255                 :             : /* Check if SSA is a constant value (created by build_int_cst) and can be
     256                 :             :    folded.  */
     257                 :             : bool
     258                 :        1111 : constant_p (tree ssa)
     259                 :             : {
     260                 :        1111 :   return tree_fits_uhwi_p (ssa);
     261                 :             : }
     262                 :             : 
     263                 :             : /* A fixup task.  When resolving the exit SSA for a back edge arg to a phi
     264                 :             :    node, the exit SSA has not been created yet.  Record what needs to be done
     265                 :             :    when it is created, and tie the phi to the right SSA name once it is
     266                 :             :    guaranteed it is created.  If MASK is nullptr the predecessor's SSA should
     267                 :             :    be used as-is, and does not need an AND.  This should only be created with
     268                 :             :    the helpers fixup_noop and fixup_and.  */
     269                 :             : struct fixup
     270                 :             : {
     271                 :             :   gphi *phi;
     272                 :             :   edge e;
     273                 :             :   tree lhs;
     274                 :             :   tree mask;
     275                 :             :   size_t bucket;
     276                 :             : };
     277                 :             : 
     278                 :             : /* Create a fixup with a no-op for the PHI in BUCKET.  Use this when the edge E
     279                 :             :    does not need an AND applied and should rather propagate the predecessor's
     280                 :             :    SSA name.  */
     281                 :             : fixup
     282                 :           0 : fixup_noop (gphi *phi, edge e, size_t bucket)
     283                 :             : {
     284                 :           0 :   fixup todo;
     285                 :           0 :   todo.phi = phi;
     286                 :           0 :   todo.e = e;
     287                 :           0 :   todo.bucket = bucket;
     288                 :           0 :   todo.lhs = nullptr;
     289                 :           0 :   todo.mask = nullptr;
     290                 :           0 :   return todo;
     291                 :             : }
     292                 :             : 
     293                 :             : /* Create a fixup for PHI through BUCKET with the exit SSA name E->src ANDed
     294                 :             :    with MASK (E->src & MASK).  GCOV_TYPE_NODE should be a tree of the gcov type
     295                 :             :    node for creating SSA names.  */
     296                 :             : fixup
     297                 :          62 : fixup_and (gphi *phi, edge e, size_t bucket, uint64_t mask,
     298                 :             :            tree gcov_type_node)
     299                 :             : {
     300                 :          62 :   fixup todo;
     301                 :          62 :   todo.phi = phi;
     302                 :          62 :   todo.e = e;
     303                 :          62 :   todo.bucket = bucket;
     304                 :          62 :   todo.lhs = make_ssa_name (gcov_type_node);
     305                 :          62 :   todo.mask = build_int_cst (gcov_type_node, mask);
     306                 :          62 :   return todo;
     307                 :             : }
     308                 :             : 
     309                 :             : /* Insert LOCAL |= MASK on BB safely, even when BB is a returns_twice block.
     310                 :             : 
     311                 :             :    This is a helper to safely emit code for updating the path bitmask in the
     312                 :             :    presence of abnormal edges and returns_twice blocks, since they have special
     313                 :             :    restrictions on edge splits and first/last instructions on the block.  */
     314                 :             : tree
     315                 :         170 : safe_insert_ior (basic_block bb, tree local, tree mask, gphi *phi,
     316                 :             :                  tree gcov_type_node)
     317                 :             : {
     318                 :         170 :   gimple_stmt_iterator gsi = gsi_start_nondebug_after_labels_bb (bb);
     319                 :         170 :   gimple *stmt = gsi_stmt (gsi);
     320                 :             : 
     321                 :             :   /* This is a returns_twice block (e.g. setjmp) which does not really expect
     322                 :             :      anything before or after, so we cannot insert the IOR on the block itself.
     323                 :             :      We move the IORs to the predecessors instead and update the phi.  The
     324                 :             :      abnormal edge cannot be split, so in that case we carefully put the IOR
     325                 :             :      after the ABNORMAL_DISPATCHER.  */
     326                 :         155 :   if (stmt && is_gimple_call (stmt)
     327                 :         196 :       && (gimple_call_flags (stmt) & ECF_RETURNS_TWICE))
     328                 :             :     {
     329                 :          88 :       for (edge e : bb->preds)
     330                 :             :         {
     331                 :          37 :           gcc_checking_assert (phi);
     332                 :          37 :           tree next = make_ssa_name (gcov_type_node);
     333                 :          37 :           tree prev = gimple_phi_arg_def_from_edge (phi, e);
     334                 :          37 :           gassign *put = prev
     335                 :          37 :             ? gimple_build_assign (next, BIT_IOR_EXPR, prev, mask)
     336                 :           1 :             : gimple_build_assign (next, mask);
     337                 :             : 
     338                 :          37 :           gimple_stmt_iterator gsi = gsi_last_bb (e->src);
     339                 :          37 :           add_phi_arg (phi, next, e, UNKNOWN_LOCATION);
     340                 :             : 
     341                 :          37 :           if (e->flags & (EDGE_ABNORMAL | EDGE_ABNORMAL_CALL))
     342                 :          17 :             gsi_insert_before (&gsi, put, GSI_SAME_STMT);
     343                 :             :           else
     344                 :          20 :             gsi_insert_on_edge (e, put);
     345                 :             :         }
     346                 :             :     }
     347                 :             :   else
     348                 :             :     {
     349                 :         153 :       tree next = make_ssa_name (gcov_type_node);
     350                 :         153 :       gassign *put = gimple_build_assign (next, BIT_IOR_EXPR, local, mask);
     351                 :         153 :       gsi_insert_before (&gsi, put, GSI_SAME_STMT);
     352                 :         153 :       local = next;
     353                 :             :     }
     354                 :         170 :   return local;
     355                 :             : }
     356                 :             : 
     357                 :             : /* Insert instructions updating the global counter at BUCKET with the contents
     358                 :             :    of (LOCAL & MASK).  LOCAL is the SSA counter for this bucket, MASK the bits
     359                 :             :    that should be updated (that is, the paths that end in this basic block).
     360                 :             :    If ATOMIC_IOR is non-null the it uses atomics.  GCOV_TYPE_NODE should be a
     361                 :             :    tree of the gcov type node for creating SSA names.
     362                 :             : 
     363                 :             :    global[BUCKET] |= (LOCAL & MASK)
     364                 :             : 
     365                 :             :    If MASK is null, no mask is applied and it becomes:
     366                 :             : 
     367                 :             :    global[BUCKET] |= LOCAL
     368                 :             : 
     369                 :             :    This function should only be necessary for the successor of an
     370                 :             :    ABNORMAL_DISPATCHER e.g. the destination of a longjmp.  Paths starting at a
     371                 :             :    longjmp do not have anything to flush, so those edges are simply ignored.
     372                 :             :    Since this is a returns_twice block we cannot put anything before (or really
     373                 :             :    after), so we instrument on the edge itself, rather than messing with
     374                 :             :    splitting and changing the graph now.
     375                 :             : 
     376                 :             :    This is similar to gsi_safe_insert_before, but this function does not
     377                 :             :    immediately commit edge inserts and does not split blocks.  */
     378                 :             : void
     379                 :          17 : flush_on_edges (basic_block bb, size_t bucket, tree local, tree mask,
     380                 :             :                 tree atomic_ior, tree gcov_type_node)
     381                 :             : {
     382                 :          17 :   gimple *def = SSA_NAME_DEF_STMT (local);
     383                 :          17 :   gphi *phi = dyn_cast <gphi *> (def);
     384                 :             : 
     385                 :          17 :   tree relaxed = nullptr;
     386                 :          17 :   if (atomic_ior)
     387                 :           0 :     relaxed = build_int_cst (integer_type_node, MEMMODEL_RELAXED);
     388                 :             : 
     389                 :          88 :   for (edge e : bb->preds)
     390                 :             :     {
     391                 :          37 :       if (e->flags & (EDGE_ABNORMAL | EDGE_ABNORMAL_CALL))
     392                 :          17 :         continue;
     393                 :             : 
     394                 :          20 :       tree global = tree_coverage_counter_ref (GCOV_COUNTER_PATHS, bucket);
     395                 :          20 :       if (phi)
     396                 :          20 :         local = gimple_phi_arg_def_from_edge (phi, e);
     397                 :             : 
     398                 :          20 :       tree global_copy = make_ssa_name (gcov_type_node);
     399                 :          20 :       gassign *ga1 = gimple_build_assign (global_copy, global);
     400                 :          20 :       gsi_insert_on_edge (e, ga1);
     401                 :             : 
     402                 :          20 :       tree masked;
     403                 :          20 :       if (mask)
     404                 :             :         {
     405                 :          20 :           masked = make_ssa_name (gcov_type_node);
     406                 :          20 :           gassign *ga2 = gimple_build_assign (masked, BIT_AND_EXPR, local,
     407                 :             :                                               mask);
     408                 :          20 :           gsi_insert_on_edge (e, ga2);
     409                 :             :         }
     410                 :             :       else
     411                 :             :         masked = local;
     412                 :             : 
     413                 :          20 :       if (atomic_ior)
     414                 :             :         {
     415                 :           0 :           global = unshare_expr (global);
     416                 :           0 :           gcall *call = gimple_build_call (atomic_ior, 3, build_addr (global),
     417                 :             :                                            masked, relaxed);
     418                 :           0 :           gsi_insert_on_edge (e, call);
     419                 :             :         }
     420                 :             :       else
     421                 :             :         {
     422                 :          20 :           tree tmp = make_ssa_name (gcov_type_node);
     423                 :          20 :           gassign *ga3 = gimple_build_assign (tmp, BIT_IOR_EXPR, global_copy,
     424                 :             :                                               masked);
     425                 :          20 :           gassign *ga4 = gimple_build_assign (unshare_expr (global), tmp);
     426                 :          20 :           gsi_insert_on_edge (e, ga3);
     427                 :          20 :           gsi_insert_on_edge (e, ga4);
     428                 :             :         }
     429                 :             :     }
     430                 :          17 : }
     431                 :             : 
     432                 :             : /* Insert instructions update the global counter at BUCKET with the contents of
     433                 :             :    (LOCAL & MASK).  LOCAL is the SSA counter for this bucket, MASK the bits
     434                 :             :    that should be updated (that is, the paths that end in this basic block).
     435                 :             :    If ATOMIC_IOR is non-null the it uses atomics.  GCOV_TYPE_NODE should be a
     436                 :             :    tree of the gcov type node for creating SSA names.
     437                 :             : 
     438                 :             :    global[BUCKET] |= (LOCAL & MASK)
     439                 :             : 
     440                 :             :    If MASK is null, no mask is applied and it becomes:
     441                 :             : 
     442                 :             :    global[BUCKET] |= LOCAL
     443                 :             : 
     444                 :             :    This function should be used on every block except returns_twice blocks (see
     445                 :             :    flush_on_edge) as all incoming edges can use the same flushing code.  */
     446                 :             : void
     447                 :         490 : flush_on_gsi (gimple_stmt_iterator *gsi, size_t bucket, tree local, tree mask,
     448                 :             :               tree atomic_ior, tree gcov_type_node)
     449                 :             : {
     450                 :         490 :   tree relaxed = nullptr;
     451                 :         490 :   if (atomic_ior)
     452                 :          85 :     relaxed = build_int_cst (integer_type_node, MEMMODEL_RELAXED);
     453                 :         490 :   tree global = tree_coverage_counter_ref (GCOV_COUNTER_PATHS, bucket);
     454                 :             : 
     455                 :         490 :   tree global_copy = make_ssa_name (gcov_type_node);
     456                 :         490 :   gassign *ga1 = gimple_build_assign (global_copy, global);
     457                 :         490 :   gsi_insert_before (gsi, ga1, GSI_SAME_STMT);
     458                 :             : 
     459                 :         490 :   tree masked;
     460                 :         490 :   if (mask)
     461                 :             :     {
     462                 :         480 :       masked = make_ssa_name (gcov_type_node);
     463                 :         480 :       gassign *ga2 = gimple_build_assign (masked, BIT_AND_EXPR, local, mask);
     464                 :         480 :       gsi_insert_before (gsi, ga2, GSI_SAME_STMT);
     465                 :             :     }
     466                 :             :   else
     467                 :             :     masked = local;
     468                 :             : 
     469                 :         490 :   if (atomic_ior)
     470                 :             :     {
     471                 :          85 :       global = unshare_expr (global);
     472                 :          85 :       gcall *call = gimple_build_call (atomic_ior, 3, build_addr (global),
     473                 :             :                                        masked, relaxed);
     474                 :          85 :       gsi_insert_before (gsi, call, GSI_SAME_STMT);
     475                 :             :     }
     476                 :             :   else
     477                 :             :     {
     478                 :         405 :       tree tmp = make_ssa_name (gcov_type_node);
     479                 :         405 :       gassign *ga3 = gimple_build_assign (tmp, BIT_IOR_EXPR, global_copy,
     480                 :             :                                           masked);
     481                 :         405 :       gassign *ga4 = gimple_build_assign (unshare_expr (global), tmp);
     482                 :         405 :       gsi_insert_before (gsi, ga3, GSI_SAME_STMT);
     483                 :         405 :       gsi_insert_before (gsi, ga4, GSI_SAME_STMT);
     484                 :             :     }
     485                 :         490 : }
     486                 :             : 
     487                 :             : } // namespace
     488                 :             : 
     489                 :             : 
     490                 :             : /* Instrument FN for prime path coverage, enabled by -fpath-coverage.  The
     491                 :             :    number of paths grows very fast with the complexity (control flow) which
     492                 :             :    explodes compile times and object file size.  Giving up is controlled by the
     493                 :             :    -fpath-coverage-limit flag.  The paths are sorted lexicographically by basic
     494                 :             :    block id, and each path is identified by its index in the sorted set of
     495                 :             :    paths, which in turn corresponds to a bit in a large bitset associated with
     496                 :             :    FN.  The monitoring code is a few bitwise operations added to edges and
     497                 :             :    basic blocks to maintain a set of live paths (note that many paths may
     498                 :             :    overlap and that many paths may be covered by the same input).  When
     499                 :             :    reaching the final vertex of a path the global counters are updated.
     500                 :             : 
     501                 :             :    This is made more complicated by the gcov type having very limited size.  To
     502                 :             :    support functions with more than 32/64 paths the bitmap is implemented on
     503                 :             :    top of a sequence of gcov integers, "buckets", where path N is recorded as
     504                 :             :    bit N%64 in bucket N/64.  For large functions, an individual basic block
     505                 :             :    will only be part of a small subset of paths, and by extension buckets and
     506                 :             :    local counters.  Only the necessary counters are read and written.  */
     507                 :             : unsigned
     508                 :         291 : instrument_prime_paths (struct function *fn)
     509                 :             : {
     510                 :         291 :   mark_dfs_back_edges (fn);
     511                 :         291 :   vec<vec<int>> paths = find_prime_paths (fn);
     512                 :             : 
     513                 :         291 :   if (paths.is_empty ())
     514                 :             :     {
     515                 :           3 :       warning_at (fn->function_start_locus, OPT_Wcoverage_too_many_paths,
     516                 :             :                   "paths exceeding limit, giving up path coverage");
     517                 :           3 :       release_vec_vec (paths);
     518                 :           3 :       return 0;
     519                 :             :     }
     520                 :             : 
     521                 :         288 :   tree gcov_type_node = get_gcov_type ();
     522                 :         288 :   const size_t bucketsize = TYPE_PRECISION (gcov_type_node);
     523                 :         288 :   const size_t nbuckets = (paths.length () + (bucketsize - 1)) / bucketsize;
     524                 :         288 :   gcc_checking_assert (sizeof (uint64_t) * BITS_PER_UNIT >= bucketsize);
     525                 :             : 
     526                 :         288 :   if (!coverage_counter_alloc (GCOV_COUNTER_PATHS, nbuckets))
     527                 :             :     {
     528                 :           0 :       release_vec_vec (paths);
     529                 :           0 :       return 0;
     530                 :             :     }
     531                 :             : 
     532                 :         288 :   hash_map <edge_hash, uint64_t> ands;
     533                 :         288 :   hash_map <block_hash, uint64_t> iors;
     534                 :         288 :   hash_map <block_hash, uint64_t> flushes;
     535                 :             : 
     536                 :             :   /* Now that we have all paths we must figure out what bitmasks must be
     537                 :             :      applied on the edges.  We also record in which basic block the path starts
     538                 :             :      (e.g. accumulator resets) and ends (accumulator flushes).  */
     539                 :        2186 :   for (size_t pathno = 0; pathno != paths.length (); ++pathno)
     540                 :             :     {
     541                 :        1898 :       const vec<int> &path = paths[pathno];
     542                 :        1898 :       const size_t bucket = pathno / bucketsize;
     543                 :        1898 :       const uint64_t bit = uint64_t (1) << (pathno % bucketsize);
     544                 :             : 
     545                 :        1898 :       basic_block first = BASIC_BLOCK_FOR_FN (fn, path[0]);
     546                 :        1898 :       basic_block last = BASIC_BLOCK_FOR_FN (fn, path[path.length () - 1]);
     547                 :             : 
     548                 :       20698 :       for (unsigned i = 1; i != path.length (); ++i)
     549                 :             :         {
     550                 :        8451 :           edge e = edge_between (fn, path[i-1], path[i]);
     551                 :        8451 :           ands.get_or_insert ({e, bucket}) |= bit;
     552                 :             :         }
     553                 :             : 
     554                 :        1898 :       iors.get_or_insert ({first, bucket}) |= bit;
     555                 :        1898 :       flushes.get_or_insert ({last, bucket}) |= bit;
     556                 :             :     }
     557                 :             : 
     558                 :             :   /* The basic blocks (except entry, exit) for this function, in topological
     559                 :             :      order.  Processing in this order means that the predecessor(s) exit SSAs
     560                 :             :      will have been created by the time a block is processed, unless it is a
     561                 :             :      loop/back edge.  This simplifies processing a bit.  */
     562                 :         288 :   vec<basic_block> blocks = topsort (fn);
     563                 :             : 
     564                 :             :   /* The exit SSA names for the BLOCK, the SSA name the BLOCK successors should
     565                 :             :      use as inputs.  */
     566                 :         288 :   hash_map<block_hash, tree> SSAex;
     567                 :             :   /* The entry SSA name for the BLOCK.  This name forms the basis for the
     568                 :             :      flushing to the global accumulators.  In the presence of phi nodes this is
     569                 :             :      the resolved phi, otherwise it is some predecessor's exit SSA name.  */
     570                 :         288 :   hash_map<block_hash, tree> SSAen;
     571                 :             : 
     572                 :         288 :   auto_vec<fixup, 4> todos;
     573                 :             : 
     574                 :             :   /* Generate the operations for each basic block.  */
     575                 :        2941 :   for (basic_block bb : blocks)
     576                 :             :     {
     577                 :        4882 :       for (size_t bucket = 0; bucket != nbuckets; ++bucket)
     578                 :             :         {
     579                 :        2805 :           tree ssa = nullptr;
     580                 :        2805 :           gphi *phi = nullptr;
     581                 :        2805 :           uint64_t all_and = union_incoming_bit_and (ands, bb, bucket);
     582                 :             : 
     583                 :        2805 :           if (all_and && single_pred_p (bb))
     584                 :             :             {
     585                 :             :               /* There is a path on this edge through the bucket, but since
     586                 :             :                  there is only one predecessor there it has no decisive power.
     587                 :             :                  Just push the predecessor's exit and have later ANDs sort it
     588                 :             :                  out.  */
     589                 :        1571 :               tree *prev = SSAex.get ({single_pred (bb), bucket});
     590                 :        1571 :               gcc_checking_assert (prev);
     591                 :        1571 :               ssa = *prev;
     592                 :             :             }
     593                 :        1234 :           else if (all_and)
     594                 :             :             {
     595                 :             :               /* There are multiple predecessors, so we need a phi.  */
     596                 :         250 :               ssa = make_ssa_name (gcov_type_node);
     597                 :         250 :               phi = create_phi_node (ssa, bb);
     598                 :             :             }
     599                 :             : 
     600                 :        2805 :           if (ssa)
     601                 :        1821 :             SSAen.put ({bb, bucket}, ssa);
     602                 :             : 
     603                 :        2805 :           if (single_pred_p (bb) && single_succ_p (bb))
     604                 :             :             {
     605                 :             :               /* Straight line -- the AND mask will already have been applied
     606                 :             :                  to the first ancestor of this chain, so we don't need to apply
     607                 :             :                  it here.  */
     608                 :             :             }
     609                 :         586 :           else if (!can_change_path_p (ands, bb, bucket, all_and))
     610                 :             :             {
     611                 :             :               /* All incoming edges would apply the same mask, so applying the
     612                 :             :                  AND here would not actually distinguish paths.  Such an AND
     613                 :             :                  will be applied later anyway so we don't need to apply it
     614                 :             :                  here.  This is a huge improvement for large programs.  */
     615                 :             :             }
     616                 :        3154 :           else for (edge e : bb->preds)
     617                 :             :             {
     618                 :        2056 :               const uint64_t *bitmask = ands.get ({e, bucket});
     619                 :             :               /* There is no phi, and there are no paths through this bucket.
     620                 :             :                  Set the SSA name to nullptr so we don't contaminate it by
     621                 :             :                  pushing unrelated predecessors.  */
     622                 :        2056 :               if (!bitmask && !phi)
     623                 :         158 :                 ssa = nullptr;
     624                 :        1898 :               else if (!bitmask && phi)
     625                 :             :                 {
     626                 :         725 :                   tree zero = safe_insert_zero_phi_arg (e, gcov_type_node);
     627                 :         725 :                   add_phi_arg (phi, zero, e, UNKNOWN_LOCATION);
     628                 :             :                 }
     629                 :        1173 :               else if (all_bits_set_p (*bitmask, bucketsize) && !phi)
     630                 :             :                 {
     631                 :             :                   /* This reduces to a no-op (x & ~0) and there is no phi
     632                 :             :                      selection, so just push the SSA.  */
     633                 :           0 :                   gcc_checking_assert (ssa);
     634                 :             :                 }
     635                 :        1173 :               else if (all_bits_set_p (*bitmask, bucketsize) && phi)
     636                 :             :                 {
     637                 :             :                   /* This reduces to a no-op (x & ~0).  Reusing the SSA and not
     638                 :             :                      emitting an unecessary AND is a big improvement for large
     639                 :             :                      programs.  */
     640                 :           0 :                   tree *prev = SSAex.get ({e->src, bucket});
     641                 :           0 :                   if (prev)
     642                 :           0 :                     add_phi_arg (phi, *prev, e, UNKNOWN_LOCATION);
     643                 :             :                   else
     644                 :           0 :                     todos.safe_push (fixup_noop (phi, e, bucket));
     645                 :             :                 }
     646                 :        1173 :               else if (SSAex.get ({e->src, bucket}))
     647                 :             :                 {
     648                 :             :                   /* We need to apply a mask, folding if possible.  If there is
     649                 :             :                      a phi it is already the latest-touched ssa.  */
     650                 :        1111 :                   tree prev = *SSAex.get ({e->src, bucket});
     651                 :        1111 :                   gcc_assert (prev);
     652                 :        1111 :                   if (constant_p (prev))
     653                 :             :                     {
     654                 :         283 :                       const uint64_t x = tree_to_uhwi (prev);
     655                 :         283 :                       tree cst = build_int_cst (gcov_type_node, x & *bitmask);
     656                 :         283 :                       if (phi)
     657                 :         283 :                         add_phi_arg (phi, cst, e, UNKNOWN_LOCATION);
     658                 :             :                       else
     659                 :           0 :                         ssa = cst;
     660                 :             :                     }
     661                 :             :                   else
     662                 :             :                     {
     663                 :         828 :                       tree lhs = make_ssa_name (gcov_type_node);
     664                 :         828 :                       tree mask = build_int_cst (gcov_type_node, *bitmask);
     665                 :         828 :                       gassign *put = gimple_build_assign (lhs, BIT_AND_EXPR,
     666                 :             :                                                           prev, mask);
     667                 :         828 :                       gsi_insert_on_edge (e, put);
     668                 :         828 :                       if (phi)
     669                 :         828 :                         add_phi_arg (phi, lhs, e, UNKNOWN_LOCATION);
     670                 :             :                       else
     671                 :           0 :                         ssa = lhs;
     672                 :             :                     }
     673                 :             :                 }
     674                 :             :               else
     675                 :             :                 {
     676                 :             :                   /* There is a phi and this edge is a back edge,
     677                 :             :                      which means the predecessor (and descendant) exit
     678                 :             :                      SSA has not been created yet.  */
     679                 :          62 :                   gcc_assert (phi);
     680                 :          62 :                   gcc_assert (e->flags & EDGE_DFS_BACK);
     681                 :          62 :                   fixup todo = fixup_and (phi, e, bucket, *bitmask,
     682                 :             :                                           gcov_type_node);
     683                 :          62 :                   todos.safe_push (todo);
     684                 :             :                 }
     685                 :             :             }
     686                 :             : 
     687                 :             :           /* Bitwise IOR.  Unlike the AND this assignment can always be created
     688                 :             :              right away as this should be applied to the result of the phi,
     689                 :             :              AND, or single predecessor's exit SSA, and all of those have
     690                 :             :              already been created.  */
     691                 :        2805 :           const uint64_t *ior = iors.get ({bb, bucket});
     692                 :        2805 :           if (ior && !ssa)
     693                 :             :             {
     694                 :             :               /* In case there was no predecessor, the IOR/initial state can
     695                 :             :                  just be a constant.  In this case, the IOR also becomes the
     696                 :             :                  block's entry node which means it will be considered for
     697                 :             :                  flushing in single-vertex paths.  */
     698                 :         292 :               ssa = build_int_cst (gcov_type_node, *ior);
     699                 :         292 :               SSAen.put ({bb, bucket}, ssa);
     700                 :             :             }
     701                 :         170 :           else if (ior && all_bits_set_p (*ior, bucketsize))
     702                 :           0 :             ssa = build_all_ones_cst (gcov_type_node);
     703                 :        2513 :           else if (ior)
     704                 :             :             {
     705                 :         170 :               gcc_assert (ssa);
     706                 :         170 :               tree mask = build_int_cst (gcov_type_node, *ior);
     707                 :         170 :               ssa = safe_insert_ior (bb, ssa, mask, phi, gcov_type_node);
     708                 :             :             }
     709                 :             : 
     710                 :        2805 :           if (ssa)
     711                 :        2113 :             SSAex.put ({bb, bucket}, ssa);
     712                 :             :         }
     713                 :             :     }
     714                 :             : 
     715                 :             :   /* Apply fixups -- now that all exit SSA names are created we can properly
     716                 :             :      set the phi argument if there is a phi node, and emit the (x & mask)
     717                 :             :      instruction if necessary.  */
     718                 :         926 :   for (fixup &todo : todos)
     719                 :             :     {
     720                 :          62 :       tree *exit = SSAex.get ({todo.e->src, todo.bucket});
     721                 :          62 :       gcc_assert (exit && *exit);
     722                 :          62 :       gcc_checking_assert (todo.phi);
     723                 :          62 :       if (todo.mask)
     724                 :             :         {
     725                 :          62 :           gassign *put = gimple_build_assign (todo.lhs, BIT_AND_EXPR, *exit,
     726                 :             :                                               todo.mask);
     727                 :          62 :           gsi_insert_on_edge (todo.e, put);
     728                 :             :         }
     729                 :             : 
     730                 :          62 :       add_phi_arg (todo.phi, todo.lhs ? todo.lhs : *exit, todo.e,
     731                 :             :                    UNKNOWN_LOCATION);
     732                 :             :     }
     733                 :             : 
     734                 :         288 :   tree atomic_ior = nullptr;
     735                 :         288 :   if (flag_profile_update == PROFILE_UPDATE_ATOMIC)
     736                 :          34 :     atomic_ior = builtin_decl_explicit
     737                 :          34 :       (TYPE_PRECISION (gcov_type_node) > 32
     738                 :             :        ? BUILT_IN_ATOMIC_FETCH_OR_8
     739                 :             :        : BUILT_IN_ATOMIC_FETCH_OR_4);
     740                 :             : 
     741                 :             :   /* Finally, add instructions to update the global counters.  */
     742                 :        2653 :   for (basic_block bb : blocks)
     743                 :             :     {
     744                 :        2077 :       gimple_stmt_iterator gsi = gsi_after_labels (bb);
     745                 :        4882 :       for (size_t bucket = 0; bucket != nbuckets; ++bucket)
     746                 :             :         {
     747                 :        2805 :           const uint64_t *bitmask = flushes.get ({bb, bucket});
     748                 :        2805 :           if (!bitmask || !*bitmask)
     749                 :        2298 :             continue;
     750                 :             : 
     751                 :         507 :           tree *counter = SSAen.get ({bb, bucket});
     752                 :         507 :           gcc_checking_assert (counter);
     753                 :         507 :           if (!*counter)
     754                 :           0 :             continue;
     755                 :             : 
     756                 :         507 :           tree mask = nullptr;
     757                 :         507 :           if (!all_bits_set_p (*bitmask, bucketsize))
     758                 :         497 :             mask = build_int_cst (gcov_type_node, *bitmask);
     759                 :             : 
     760                 :         507 :           if (bb_has_abnormal_pred (bb))
     761                 :          17 :             flush_on_edges (bb, bucket, *counter, mask, atomic_ior,
     762                 :             :                             gcov_type_node);
     763                 :             :           else
     764                 :         490 :             flush_on_gsi (&gsi, bucket, *counter, mask, atomic_ior,
     765                 :             :                           gcov_type_node);
     766                 :             :         }
     767                 :             :     }
     768                 :             : 
     769                 :         288 :   const unsigned npaths = paths.length ();
     770                 :         288 :   blocks.release ();
     771                 :         288 :   release_vec_vec (paths);
     772                 :         288 :   return npaths;
     773                 :         288 : }
        

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