From bbba21e93a0705f1f85cf6dea1fb7892448f7010 Mon Sep 17 00:00:00 2001 From: hellboy28D Date: Fri, 4 Sep 2026 05:37:11 +0530 Subject: [PATCH] transpile: rewrite relooper to be iterative to avoid stack overflow on large CFGs --- c2rust-transpile/src/cfg/relooper.rs | 402 ++++++++++++++------------- 1 file changed, 211 insertions(+), 191 deletions(-) diff --git a/c2rust-transpile/src/cfg/relooper.rs b/c2rust-transpile/src/cfg/relooper.rs index 6659d9fe7a..66d5304d0d 100644 --- a/c2rust-transpile/src/cfg/relooper.rs +++ b/c2rust-transpile/src/cfg/relooper.rs @@ -32,7 +32,7 @@ //! If we have a single entry point, and there are no back edges to that entry, //! then we can generate a simple structure. `Simple` structures contain a //! single basic block, and represent a straight-line sequence of instructions. -//! All remaining blocks are then recursively relooped, with the immediate +//! All remaining blocks are then iteratively relooped, with the immediate //! successors of the entry becoming the new entries for the rest of that //! portion of the CFG. //! @@ -47,7 +47,7 @@ //! If we have entries with back edges to them, then we can generate a `Loop` //! structure. Any nodes that can reach the entry become part of the loop body, //! with any remaining nodes becoming the follow blocks for the loop. The loop's -//! contents are then relooped into the loops's body, and the follow blocks get +//! contents are then relooped into the loop's body, and the follow blocks get //! relooped to be the logic that follows the loop. //! //! # Simplification and Nesting @@ -73,6 +73,8 @@ pub fn reloop( use_c_multiple_info: bool, // use the multiple information in the CFG (slower, but better) live_in: IndexSet, // declarations we assume are live going into this graph ) -> (Vec, Vec>) { + // `cfg.entries` is the CFG's single entry `Label`; `relooper` wants a set + // of entries so it can be reused uniformly for `Multiple` branches too. let entries = indexset![cfg.entries.clone()]; let blocks: BasicBlocks = cfg .nodes @@ -210,208 +212,224 @@ type BasicBlocks = IndexMap, StmtOr type AdjacencyList = IndexMap>; impl RelooperState { - /// Recursive helper for `reloop`. + /// Helper for `reloop`. /// - /// TODO: perhaps manually perform TCO? + /// Sequential chains — runs of `Simple` blocks, and the follow-blocks + /// after a `Multiple` — are processed with an explicit loop rather than + /// tail recursion, so large functions with long straight-line CFGs + /// cannot exhaust the stack (c2rust#1908). Recursion is still used for + /// the individual branches of a `Multiple` and inside `make_loop`, since + /// that recursion depth is bounded by nesting depth rather than block + /// count. fn relooper( &mut self, - entries: IndexSet