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

Reply via email to