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?

Src:

     int fact(int n) {
         if (n == 0 || n == 1) return 1;
         return n * fact(n-1);
     }

Asm, Target riscv32-unknown-elf, march=rv32imac, only flag is -O3, off of 
master (9c40d803 specifically)

     fact:
             mv      a5,a0
             li      a0,1
             mv      a2,a0
             bleu    a5,a0,.L1
             sub     a4,a5,a0
             and     t0,a4,a0
             mv      a3,a0
             beq     t0,zero,.L2
             mv      a0,a5
             addi    a5,a5,-1
             beq     a5,a2,.L12
     .L2:
             mul     a0,a0,a5
             addi    t1,a5,-1
             addi    a5,a5,-2
             mul     a0,a0,t1
             bne     a5,a3,.L2
     .L1:
             ret
     .L12:
             ret

Thanks
Robert

Reply via email to