https://gcc.gnu.org/bugzilla/show_bug.cgi?id=119164

--- Comment #1 from Vineet Gupta <vineetg at gcc dot gnu.org> ---
The RISC-V mode switching state machine tracks transitions into call insn and
out of call insn, going from MODE_DYN to MODE_CALL and back to MODE_DYN.

However when the call happens to be last insn of BB (not the tail call case),
it ends up with wrong transitions, resulting in a FRM write, which can't be
optimized away.

The gist of problem is around following codegen as it hits mode_sw (following
is from subreg3)

  (jump_insn 7 6 8 2 (set (pc)
          (if_then_else (eq (reg/f:DI 141 [ a ])
                  (const_int 0 [0]))
              (label_ref:DI 10)
              (pc))) "node_texture_util.c":6:6 377 {*branchdi}
       (expr_list:REG_DEAD (reg/f:DI 141 [ a ])
          (int_list:REG_BR_PROB 499612076 (nil)))
   -> 10)

  (note 8 7 9 3 [bb 3] NOTE_INSN_BASIC_BLOCK)
  (call_insn 9 8 10 3 (parallel [
              (call (mem:SI (symbol_ref:DI ("d") [flags 0x41]  <function_decl
0x7e5994026a00 d>) [0 d S4 A32])
                  (const_int 0 [0]))
              (use (unspec:SI [
                          (const_int 0 [0])
                      ] UNSPEC_CALLEE_CC))
              (clobber (reg:SI 1 ra))
          ]) "node_texture_util.c":7:5 471 {call_internal}
       (expr_list:REG_CALL_DECL (symbol_ref:DI ("d") [flags 0x41] 
<function_decl 0x7e5994026a00 d>)
          (nil))
      (nil))
  (code_label 10 9 11 4 2 (nil) [1 uses])
  (note 11 10 12 4 [bb 4] NOTE_INSN_BASIC_BLOCK)
  (insn 12 11 13 4 (set (reg/f:DI 142)
          (high:DI (symbol_ref:DI ("c") [flags 0x86]  <var_decl 0x7e599402fd10
c>))) "node_texture_util.c":8:7 283 {*movdi_64bit}
       (nil))

In BB 3, there's call_insn 9 followed by code_label 10 which technically (I
think) belongs to BB 4, but hoisted before the BB 4 start note.

Anyhow in TARGET_MODE_NEEDED / riscv_frm_mode_needed (), handling call_insn 9
uses next_nonnote_nondebug_insn_bb () to check if it is last insn in BB.
Somewhat non-intuitively (to me) this API *includes* code_label 10 and deduces
it call is not last insn, failing to insert a fresh FRM read at the end of BB.

The next insn passed to TARGET_MODE_NEEDED is not code_label 10 but the one
after as generic mode_sw skips over labels due to NONDEBUG_INSN_P filter. That
insn 12 fails to backreference to call insn 9 (which it anyways would have
given this is a different BB anyways). Eventually TARGET_MODE_EMIT ends up with
following transitions

  frm mode set: 7-dyn to 9-call
  frm mode set: 10-none to 7-dyn
  scanning new insn with uid = 56.  <--- FRM write
  frm mode set: 10-none to 8-dyn-exit  

as opposed to (unconditional call test)

  frm mode set: 7-dyn to 9-call
  frm mode set: 9-call to 7-dyn
  scanning new insn with uid = 44.  <-- FRM read which gets optimized away
  frm mode set: 7-dyn to 8-dyn-exit

Reply via email to