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 : }
|