]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
ICE with -Winfinite-recursion due to recursive rather than work queue/list [PR124651]
authorHeiko Eißfeldt <heiko.Eissfeldt@hexco.de>
Tue, 7 Apr 2026 18:28:16 +0000 (20:28 +0200)
committerAndrew Pinski <andrew.pinski@oss.qualcomm.com>
Tue, 12 May 2026 22:33:06 +0000 (15:33 -0700)
As suggested the control flow in
pass_warn_recursion::find_function_exit() was changed
from a recursive to an iterative form. The logic for detecting infinite
recursion is left unchanged.

This avoids stack overflows while handling very large functions
as could be seen with the generated code attached to the PR.

Reg tested OK.

2026-04-07 Heiko Eißfeldt <heiko@hexco.de>

PR middle-end/124651
* gimple-warn-recursion.cc (find_function_exit):
replace recursive calls with iteration for lower stack usage

gcc/gimple-warn-recursion.cc

index 67ce192142d494e11b29e589ab798a4415911ed0..590da4f5e4520402cb34f662eb73c8d832c53d09 100644 (file)
@@ -82,81 +82,99 @@ pass_warn_recursion::pass_warn_recursion (gcc::context *ctxt)
    there is no (recursive) call to M_FUNC.  */
 
 bool
-pass_warn_recursion::find_function_exit (basic_block bb)
+pass_warn_recursion::find_function_exit (basic_block bb_start)
 {
-  if (!bitmap_set_bit (m_visited, bb->index))
-    return false;
+  /* work item list of BB's, presized with average size.  */
+  auto_vec<basic_block, 3> worklist;
+  worklist.safe_push (bb_start);
 
-  if (bb == EXIT_BLOCK_PTR_FOR_FN (m_func))
-    return true;
-
-  /* Iterate over statements in BB, looking for a call to FNDECL.  */
-  for (auto si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next_nondebug (&si))
+  while (!worklist.is_empty ())
     {
-      gimple *stmt = gsi_stmt (si);
-      if (!is_gimple_call (stmt))
+      const auto &bb = worklist.pop ();
+      bool nextBB = false;
+      if (!bitmap_set_bit (m_visited, bb->index))
        continue;
 
-      if (gimple_call_builtin_p (stmt, BUILT_IN_LONGJMP))
-       /* A longjmp breaks infinite recursion.  */
+      if (bb == EXIT_BLOCK_PTR_FOR_FN (m_func))
        return true;
 
-      if (tree fndecl = gimple_call_fndecl (stmt))
+      /* Iterate over statements in BB, looking for a call to FNDECL.  */
+      for (auto si = gsi_start_bb (bb); !gsi_end_p (si);
+          gsi_next_nondebug (&si))
        {
-         /* A throw statement breaks infinite recursion.  */
-         tree id = DECL_NAME (fndecl);
-         const char *name = IDENTIFIER_POINTER (id);
-         if (startswith (name, "__cxa_throw"))
-           return true;
-         /* As does a call to POSIX siglongjmp.  */
-         if (!strcmp (name, "siglongjmp"))
+         gimple *stmt = gsi_stmt (si);
+         if (!is_gimple_call (stmt))
+           continue;
+
+         if (gimple_call_builtin_p (stmt, BUILT_IN_LONGJMP))
+           /* A longjmp breaks infinite recursion.  */
            return true;
 
-         if (m_built_in
-             && gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)
-             && m_built_in == DECL_FUNCTION_CODE (fndecl))
+         if (tree fndecl = gimple_call_fndecl (stmt))
            {
-             const char *cname
-               = IDENTIFIER_POINTER (DECL_NAME (current_function_decl));
-             /* Don't warn about gnu_inline extern inline function
-                like strcpy calling __builtin_strcpy, that is fine,
-                if some call is made (the builtin isn't expanded inline),
-                a call is made to the external definition.  */
-             if (!(DECL_DECLARED_INLINE_P (current_function_decl)
-                   && DECL_EXTERNAL (current_function_decl))
-                 || strcmp (name, cname) == 0)
+             /* A throw statement breaks infinite recursion.  */
+             tree id = DECL_NAME (fndecl);
+             const char *name = IDENTIFIER_POINTER (id);
+             if (startswith (name, "__cxa_throw"))
+               return true;
+             /* As does a call to POSIX siglongjmp.  */
+             if (!strcmp (name, "siglongjmp"))
+               return true;
+
+             if (m_built_in
+                 && gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)
+                 && m_built_in == DECL_FUNCTION_CODE (fndecl))
                {
-                 /* The call is being made from the definition of a built-in
-                    (e.g., in a replacement of one) to itself.  */
-                 m_calls->safe_push (stmt);
-                 return false;
+                 const char *cname
+                   = IDENTIFIER_POINTER (DECL_NAME (current_function_decl));
+                 /* Don't warn about gnu_inline extern inline function
+                      like strcpy calling __builtin_strcpy, that is fine,
+                      if some call is made (the builtin isn't expanded inline),
+                      a call is made to the external definition.  */
+                 if (!(DECL_DECLARED_INLINE_P (current_function_decl)
+                     && DECL_EXTERNAL (current_function_decl))
+                     || strcmp (name, cname) == 0)
+                   {
+                     /* The call is being made from the definition of a
+                          built-in (e.g., in a replacement of one) to itself.
+                      */
+                     m_calls->safe_push (stmt);
+                     nextBB = true;
+                     break;
+                   }
                }
            }
-       }
 
-      if (noreturn_p)
-       {
-         /* A noreturn call breaks infinite recursion.  */
-         int flags = gimple_call_flags (stmt);
-         if (flags & ECF_NORETURN)
-           return true;
+         if (noreturn_p)
+           {
+             /* A noreturn call breaks infinite recursion.  */
+             int flags = gimple_call_flags (stmt);
+             if (flags & ECF_NORETURN)
+               return true;
+           }
+
+         tree callee = gimple_call_fndecl (stmt);
+         if (!callee || m_func->decl != callee)
+           continue;
+
+         /* Add the recursive call to the vector and return false.  */
+         m_calls->safe_push (stmt);
+         nextBB = true;
+         break;
        }
+       if (nextBB)
+         continue;
 
-      tree callee = gimple_call_fndecl (stmt);
-      if (!callee || m_func->decl != callee)
-       continue;
+       /* If no call to FNDECL has been found search all BB's successors.  */
+       /* Add more BB's to check for on demand.  */
+       edge e;
+       edge_iterator ei;
 
-      /* Add the recursive call to the vector and return false.  */
-      m_calls->safe_push (stmt);
-      return false;
-    }
+       FOR_EACH_EDGE (e, ei, bb->succs)
+         if (!bitmap_bit_p (m_visited, e->dest->index))
+           worklist.safe_push (e->dest);
 
-  /* If no call to FNDECL has been found search all BB's successors.  */
-  edge e;
-  edge_iterator ei;
-  FOR_EACH_EDGE (e, ei, bb->succs)
-    if (find_function_exit (e->dest))
-      return true;
+    }
 
   return false;
 }