On 8/30/26 1:34 PM, Robert Guthrie via Gcc wrote:
Hello, I have been exploring RISCV, compiling simple programs and
looking at the output.
The below basic recursive factorial function generates two `ret` insns
when only one is necessary:
the BB labeled with .L12 can be eliminated (by just re-targeting to .L1).
I tried to see if I could understand what was going on myself.
I have no experience working on GCC or any other big compiler, so I
took this as an opportunity to dig around more than anything else.
Thus, please forgive if my analysis is way off.
I determined:
* When the loop was unrolled, the early exit for the special case n ==
2 added
the extra `ret` insn as a fallthru
* The `bbro` pass subsequently moved both `ret`s to the end of the
linear sequence,
which is the point at which the second one becomes truly redundant.
Next, I tried to determine why it wasn't subsequently eliminated.
* I looked at `cleanup_cfg`. I thought maybe it was supposed to be
eliminated with CROSSJUMP,
but after looking more, I think my understanding of what CROSSJUMP
is was just incorrect.
And anyways, for some reason two basic blocks with only `ret` and
`use` do not count as having
any matching insns in the suffix (I tried to change this, and
`try_optimize_cfg` began infinite looping
though I didn't determine why: this condition early in
`flow_find_cross_jump` is what I was looking at.)
if (!simplejump_p (i2) && !returnjump_p (i2) && last1 && dir_p)
ninsns++;
* In `try_optimize_cfg` there are various pattern-matches to simplify
stuff like this.
I added my own: "if we have a BB that is only a ret+use with a
single pred that is not a fallthru,
and there is another BB that is only a ret+use, retarget the
predecessor to the other BB and delete this one",
which does seem to eliminate this particular case correctly, though
I find it hard to believe that
is the right way to solve this problem (the pattern is quite overly
specific).
I am wondering: which part of the compiler should in principle
eliminate the duplicate? Am I even looking in the right place?
And if I am, is there some specific part of the CFG simplification
that ought to handle this?
My memory has gotten quite fuzzy on this stuff. But the fact that the
redundancy isn't exposed until after bbro could well be a meaningful
part of the problem.
Conceptually what we want to realize is that if we have a block that
ends with a return that is immediately followed by a block that is only
a return, then the first return can be eliminated and the block changed
to fall through.
Given the hard dependency on final layout, optimizing this case has to
happen late, in particular after bbro has run. You might even argue it
should defer past things like branch shortening.
I'd be looking at cfgcleanup.cc, cfgrtl.cc and bb_is_just_return for
inspiration.
Note this change will save code size, but not likely directly improve
performance. It's still worth doing, but just want to make sure
expectations are sane.
I would recommend you get an account on gcc.gnu.org/bugzilla so that you
can file this as a bug report ensuring it doesn't get lost.
jeff