LCOV - code coverage report
Current view: top level - gcc - gimple-warn-recursion.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 98.7 % 78 77
Test Date: 2026-05-30 15:37:04 Functions: 100.0 % 5 5
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* -Winfinite-recursion support.
       2              :    Copyright (C) 2021-2026 Free Software Foundation, Inc.
       3              :    Contributed by Martin Sebor <msebor@redhat.com>
       4              : 
       5              :    This file is part of GCC.
       6              : 
       7              :    GCC is free software; you can redistribute it and/or modify
       8              :    it under the terms of the GNU General Public License as published by
       9              :    the Free Software Foundation; either version 3, or (at your option)
      10              :    any later version.
      11              : 
      12              :    GCC is distributed in the hope that it will be useful,
      13              :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      14              :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15              :    GNU General Public License for more details.
      16              : 
      17              :    You should have received a copy of the GNU General Public License
      18              :    along with GCC; see the file COPYING3.  If not see
      19              :    <http://www.gnu.org/licenses/>.  */
      20              : 
      21              : #include "config.h"
      22              : #include "system.h"
      23              : #include "coretypes.h"
      24              : #include "backend.h"
      25              : #include "tree.h"
      26              : #include "gimple.h"
      27              : #include "tree-pass.h"
      28              : #include "ssa.h"
      29              : #include "diagnostic-core.h"
      30              : // #include "tree-dfa.h"
      31              : #include "attribs.h"
      32              : #include "gimple-iterator.h"
      33              : 
      34              : namespace {
      35              : 
      36              : const pass_data warn_recursion_data =
      37              : {
      38              :   GIMPLE_PASS, /* type */
      39              :   "*infinite-recursion", /* name */
      40              :   OPTGROUP_NONE, /* optinfo_flags */
      41              :   TV_NONE, /* tv_id */
      42              :   PROP_ssa, /* properties_required */
      43              :   0, /* properties_provided */
      44              :   0, /* properties_destroyed */
      45              :   0, /* todo_flags_start */
      46              :   0, /* todo_flags_finish */
      47              : };
      48              : 
      49              : class pass_warn_recursion : public gimple_opt_pass
      50              : {
      51              : public:
      52              :   pass_warn_recursion (gcc::context *);
      53              : 
      54              : private:
      55      2879219 :   bool gate (function *) final override { return warn_infinite_recursion; }
      56              : 
      57              :   unsigned int execute (function *) final override;
      58              : 
      59              :   bool find_function_exit (basic_block);
      60              : 
      61              :   /* Recursive calls found in M_FUNC.  */
      62              :   vec<gimple *> *m_calls;
      63              :   /* Basic blocks already visited in the current function.  */
      64              :   bitmap m_visited;
      65              :   /* The current function.  */
      66              :   function *m_func;
      67              :   /* The current function code if it's (also) a built-in.  */
      68              :   built_in_function m_built_in;
      69              :   /* True if M_FUNC is a noreturn function.  */
      70              :   bool noreturn_p;
      71              : };
      72              : 
      73              : /* Initialize the pass and its members.  */
      74              : 
      75       288767 : pass_warn_recursion::pass_warn_recursion (gcc::context *ctxt)
      76              :   : gimple_opt_pass (warn_recursion_data, ctxt),
      77       288767 :     m_calls (), m_visited (), m_func (), m_built_in (), noreturn_p ()
      78              : {
      79       288767 : }
      80              : 
      81              : /* Return true if there is path from BB to M_FUNC exit point along which
      82              :    there is no (recursive) call to M_FUNC.  */
      83              : 
      84              : bool
      85       252611 : pass_warn_recursion::find_function_exit (basic_block bb_start)
      86              : {
      87              :   /* work item list of BB's, presized with average size.  */
      88       252611 :   auto_vec<basic_block, 3> worklist;
      89       252611 :   worklist.safe_push (bb_start);
      90              : 
      91      1663335 :   while (!worklist.is_empty ())
      92              :     {
      93      1156334 :       const auto &bb = worklist.pop ();
      94      1156334 :       bool nextBB = false;
      95      1156334 :       if (!bitmap_set_bit (m_visited, bb->index))
      96          451 :         continue;
      97              : 
      98      1156144 :       if (bb == EXIT_BLOCK_PTR_FOR_FN (m_func))
      99       250832 :         return true;
     100              : 
     101              :       /* Iterate over statements in BB, looking for a call to FNDECL.  */
     102      6260497 :       for (auto si = gsi_start_bb (bb); !gsi_end_p (si);
     103      4448805 :            gsi_next_nondebug (&si))
     104              :         {
     105      4449600 :           gimple *stmt = gsi_stmt (si);
     106      4449600 :           if (!is_gimple_call (stmt))
     107      4448805 :             continue;
     108              : 
     109       558703 :           if (gimple_call_builtin_p (stmt, BUILT_IN_LONGJMP))
     110              :             /* A longjmp breaks infinite recursion.  */
     111          534 :             return true;
     112              : 
     113       558700 :           if (tree fndecl = gimple_call_fndecl (stmt))
     114              :             {
     115              :               /* A throw statement breaks infinite recursion.  */
     116       492070 :               tree id = DECL_NAME (fndecl);
     117       492070 :               const char *name = IDENTIFIER_POINTER (id);
     118       492070 :               if (startswith (name, "__cxa_throw"))
     119              :                 return true;
     120              :               /* As does a call to POSIX siglongjmp.  */
     121       491875 :               if (!strcmp (name, "siglongjmp"))
     122              :                 return true;
     123              : 
     124       491872 :               if (m_built_in
     125          143 :                   && gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)
     126       491998 :                   && m_built_in == DECL_FUNCTION_CODE (fndecl))
     127              :                 {
     128            4 :                   const char *cname
     129            4 :                     = IDENTIFIER_POINTER (DECL_NAME (current_function_decl));
     130              :                   /* Don't warn about gnu_inline extern inline function
     131              :                        like strcpy calling __builtin_strcpy, that is fine,
     132              :                        if some call is made (the builtin isn't expanded inline),
     133              :                        a call is made to the external definition.  */
     134            4 :                   if (!(DECL_DECLARED_INLINE_P (current_function_decl)
     135            2 :                       && DECL_EXTERNAL (current_function_decl))
     136            6 :                       || strcmp (name, cname) == 0)
     137              :                     {
     138              :                       /* The call is being made from the definition of a
     139              :                            built-in (e.g., in a replacement of one) to itself.
     140              :                        */
     141            3 :                       m_calls->safe_push (stmt);
     142            3 :                       nextBB = true;
     143            3 :                       break;
     144              :                     }
     145              :                 }
     146              :             }
     147              : 
     148       558499 :           if (noreturn_p)
     149              :             {
     150              :               /* A noreturn call breaks infinite recursion.  */
     151         1212 :               int flags = gimple_call_flags (stmt);
     152         1212 :               if (flags & ECF_NORETURN)
     153              :                 return true;
     154              :             }
     155              : 
     156       558166 :           tree callee = gimple_call_fndecl (stmt);
     157       558166 :           if (!callee || m_func->decl != callee)
     158       557908 :             continue;
     159              : 
     160              :           /* Add the recursive call to the vector and return false.  */
     161          258 :           m_calls->safe_push (stmt);
     162          258 :           nextBB = true;
     163          258 :           break;
     164              :         }
     165       905312 :         if (nextBB)
     166          261 :           continue;
     167              : 
     168              :         /* If no call to FNDECL has been found search all BB's successors.  */
     169              :         /* Add more BB's to check for on demand.  */
     170       905051 :         edge e;
     171       905051 :         edge_iterator ei;
     172              : 
     173      2067110 :         FOR_EACH_EDGE (e, ei, bb->succs)
     174      1162059 :           if (!bitmap_bit_p (m_visited, e->dest->index))
     175      1154975 :             worklist.safe_push (e->dest);
     176              : 
     177              :     }
     178              : 
     179              :   return false;
     180       252611 : }
     181              : 
     182              : 
     183              : /* Search FUNC for unconditionally infinitely recursive calls to self
     184              :    and issue a warning if it is such a function.  */
     185              : 
     186              : unsigned int
     187       252611 : pass_warn_recursion::execute (function *func)
     188              : {
     189       252611 :   auto_bitmap visited;
     190       252611 :   auto_vec<gimple *> calls;
     191              : 
     192       252611 :   m_visited = visited;
     193       252611 :   m_calls = &calls;
     194       252611 :   m_func = func;
     195              : 
     196              :   /* Avoid diagnosing an apparently infinitely recursive function that
     197              :      doesn't return where the infinite recursion might be avoided by
     198              :      a call to another noreturn function.  */
     199       252611 :   noreturn_p = lookup_attribute ("noreturn", DECL_ATTRIBUTES (m_func->decl));
     200              : 
     201       252611 :   if (fndecl_built_in_p (m_func->decl, BUILT_IN_NORMAL))
     202          144 :     m_built_in = DECL_FUNCTION_CODE (m_func->decl);
     203              :   else
     204       252467 :     m_built_in = BUILT_IN_NONE;
     205              : 
     206       252611 :   basic_block entry_bb = ENTRY_BLOCK_PTR_FOR_FN (func);
     207              : 
     208       252668 :   if (find_function_exit (entry_bb) || m_calls->length () == 0)
     209              :     return 0;
     210              : 
     211           57 :   auto_diagnostic_group d;
     212           57 :   if (warning_at (DECL_SOURCE_LOCATION (func->decl),
     213           57 :                   OPT_Winfinite_recursion,
     214              :                   "infinite recursion detected"))
     215          240 :     for (auto stmt: *m_calls)
     216              :       {
     217           69 :         location_t loc = gimple_location (stmt);
     218           69 :         if (loc == UNKNOWN_LOCATION)
     219            0 :           continue;
     220              : 
     221           69 :         inform (loc, "recursive call");
     222              :       }
     223              : 
     224           57 :   return 0;
     225       252668 : }
     226              : 
     227              : } // namespace
     228              : 
     229              : gimple_opt_pass *
     230       288767 : make_pass_warn_recursion (gcc::context *ctxt)
     231              : {
     232       288767 :   return new pass_warn_recursion (ctxt);
     233              : }
        

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.