[RISCV] redundant return-only BB not eliminated
Jeff Law
jeffrey.law@oss.qualcomm.com
Mon Sep 7 03:45:41 GMT 2026
On 9/6/26 1:06 PM, Robert Guthrie wrote:
> On Fri, Sep 04, 2026 at 10:20:37AM -0600, Jeff Law wrote:
>>
>>
>> On 9/3/26 7:13 PM, Robert Guthrie wrote:
>>>> It's likely harder than a typical "simple hack of the week" as
>>>> those often end up being target patterns or match.pd patterns -- so
>>>> limited in how many APIs you need to understand, much less RTL or
>>>> gimple IL parsing, etc. But there's folks here that can help.
>>>>
>>>
>>> That is true: it has been a little harder than I expected!
>>> Since I am modifying cleanup_cfg, and it is called so many times,
>>> I am getting a lot of weird interactions leading to infinite loops or
>>> other errors. Or getting different behavior when it is called in
>>> CFGRTL vs. CFGLAYOUT
>>> mode (I spent last evening trying to grok the difference).
>>> Maybe this is just something to tag onto the end of bbro rather than
>>> adding it
>>> to a core function instead?
>> But it fits fairly naturally in cfg cleanup and thinking more about
>> it, it should work irrespective of whether we're in cfg layout mode
>> or not if we do the cfg manipulations correctly.
>>
>> The whole point of splitting out the two modes is to allow earlier
>> passes to ignore the low level mechanics of dealing with fallthru
>> edges and the like.
>>
>> Conceptually a block ending in a return can have its return removed
>> and the cfg altered so that it has a fallthru edge to a block that
>> has a return and nothing else. This is true before and after cfg
>> layout mode.
>>
>
> Ok, I have had a couple more evenings to dig at this.
> I feel like something like the diff below is quite simple and works?
> Let me explain how I got here for some context:
>
> * First, I started unaware there was a difference between RTL and
> Layout mode.
> Whatever I was doing was not handling Fallthru correctly.
> * Next, I tried to change how I handled Fallthrus, and it was breaking
> Layout mode
> (mainly because the EDGE_FALLTHRU flag means different things)
> * I ignored Layout mode for a bit and tried to just get RTL mode working.
> I was doing what you originally suggested: delete the first block
> and redirect its successors
> to the second one. I was having a lot of trouble with Fallthrus,
> such as:
> - the infinite loop I had was: I was accidentally creating fwding
> blocks, which were just an
> unconditional jump-to-ret. Another transformation in
> `try_optimize_cfg` turned that back into a
> ret, which then hit my transform again...
> - in some cases, the fallthru predecessor of the first ret-only
> block was also jumping
> to the second ret-only, so whatever code I had ended up with
> duplicate edges
> (e.g, if I had BB ret-only blocks 4 and 5, and 3->4 and 3->5, then
> deleting 4 and redirecting 3->4 to 3->5
> created a dupe)
Sometimes its better to ask for help as you work through these things.
Ideally with before/after dumps and the patch you're working with. GCC
is fairly complex and often even very experienced developers need to
throw things under the debugger to really understand what's happening.
>
> This bootstrapped correctly (on x86, I ordered a RISC-V dev board but
> haven't gotten it yet, though it solves my original problem too when I
> build cross).
Note that RISC-V development boards are still quite slow. To give you a
sense, my 2020-ish servers can do a bootstrap & regression test in about
90 minutes. On my K3 board, it's 9 hours.
> I need to learn how to add good test cases before submitting as a
> formal patch, I am just dropping the diff
> below:
Lots of examples for testcases ;-)
>
> diff --git a/gcc/cfgcleanup.cc b/gcc/cfgcleanup.cc
> index 1d9ec908dab..d74bfcf10e2 100644
> --- a/gcc/cfgcleanup.cc
> +++ b/gcc/cfgcleanup.cc
> @@ -2831,14 +2831,39 @@ try_optimize_cfg (int mode)
> redirect_edge_succ (single_succ_edge (b),
> EXIT_BLOCK_PTR_FOR_FN (cfun));
> single_succ_edge (b)->flags &= ~EDGE_CROSSING;
> changed_here = true;
> }
> }
>
> + /* If we have two consecutive return-only basic blocks,
> + delete the second one, redirecting all of its predecessors
> + to the first one. */
> + if (!(mode & CLEANUP_NO_INSN_DEL)
> + && bb_is_just_return (b, &ret, &use)
> + && bb_is_just_return (b->prev_bb, &ret, &use))
So in general you don't want to be looking at prev_bb/next_bb. Yes,
blocks are kept in a chain, but there's really no meaning to the order
of the chain until after block layout. What really matters are the
pred/succ lists.
So roughly
/* Find predecessor of exit block that is just a return and nothing
else. */
FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)
{
if (bb_is_just_return (e->pred))
{
target_bb = e->pred;
break;
}
if (target_bb)
{
FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)
{
if (e->pred == target_bb)
continue;
/* If the block unconditionally reaches EXIT_BLOCK, then
redirect it to TARGET_BB. */
if (single_succ_p (e->pred))
{
/* CFG Manipulations */
}
}
}
As I'm scribbling this code out, I do recall that we had this kind of
transformation. I see the remnants of one in reorg.cc, but that's not
something I'd really suggest copying. That doesn't really work the
CFG. I seem to recall it was in jump.cc, but that's been largely gutted
and subsumed by cfgcleanup, but I don't see anything useful in cfgcleanup.
Jeff
More information about the Gcc
mailing list