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

--- Comment #9 from GCC Commits <cvs-commit at gcc dot gnu.org> ---
The master branch has been updated by Kyrylo Tkachov <[email protected]>:

https://gcc.gnu.org/g:21d783552e49650b6dab5e062842f491dbcfb2d6

commit r17-4505-g21d783552e49650b6dab5e062842f491dbcfb2d6
Author: Kyrylo Tkachov <[email protected]>
Date:   Wed Sep 16 23:55:02 2026 -0700

    middle-end: rescan calls after inserting call-used register zeroing
[PR127445]

    gen_call_used_regs_seq adds the registers it zeroes to
    crtl->must_be_zero_on_return.  df_epilogue_uses_p reports those registers
as
    used by the epilogue, so they join the exit block use set.  That set in
turn
    decides which registers a sibling call is recorded as clobbering:
    df_get_call_refs skips the clobber def of any register that the exit block
    uses.  Growing the set therefore invalidates the recorded defs of every
    sibling call in the function, but pass_zero_call_used_regs only refreshed
the
    artificial refs, through df_update_exit_block_uses.

    To hit this a function needs one return path that is a sibling call and
    another that is a plain return.  The zeroing sequence goes on the plain
    return path while the sibling call keeps its stale defs.  The next pass to
    run df_analyze then ICEs in a df checking compiler.  On aarch64 that pass
is
    narrow_gp_writes:

      during RTL pass: narrow_gp_writes
      internal compiler error: in df_refs_verify, at df-scan.cc:4012
      0x87315f df_refs_verify
      0xed6b66 df_insn_refs_verify
      0xed8b63 df_bb_verify
      0xed8ee7 df_scan_verify()
      0xec2161 df_verify()

    For the sibling call insn the recorded chain holds 65 defs while a fresh
scan
    collects 57, the difference being x0 to x7:

      (call_insn/j:TI 82 147 83 5 (parallel [
                  (call (mem:DI (symbol_ref:DI ("bar")))
                      (const_int 0 [0]))
                  (unspec:DI [(const_int 2 [0x2])] UNSPEC_CALLEE_ABI)
                  (return)
              ]) 110 {*sibcall_insn}

      recorded defs { 0 1 2 3 4 5 6 7 8 ... 18 32 ... 86 16 17 }
      fresh defs    {               8 ... 18 32 ... 86 16 17 }

    Call df_update_entry_exit_and_calls once after all return blocks have been
    handled.  This is the same fix that PR rtl-optimization/78400 applied to
    shrink-wrapping, and thread_prologue_and_epilogue_insns already does it for
    the same reason.  The df_update_exit_block_uses call being replaced came
from
    PR middle-end/100775, which covered only the artificial refs of the exit
    block.

    df_compute_regs_ever_live also calls df_update_entry_exit_and_calls
whenever
    regs_ever_live changed, which is why the bug usually hides: when zeroing a
    register makes it ever-live for the first time, the next df_analyze repairs
    the refs by accident.  The testcase uses -fzero-call-used-regs=used-arg so
    that every zeroed register is already live.

    The original report used -finstrument-functions-once, which supplies the
two
    return paths through the tail call to __cyg_profile_func_exit.

    Bootstrapped and tested on aarch64-none-linux-gnu.

    gcc/ChangeLog:

            PR rtl-optimization/127445
            * function.cc (gen_call_used_regs_seq): Return whether a zeroing
            sequence was emitted.  Do not update the exit block uses here.
            (pass_zero_call_used_regs::execute): Call
            df_update_entry_exit_and_calls once when a sequence was emitted.

    gcc/testsuite/ChangeLog:

            PR rtl-optimization/127445
            * gcc.target/aarch64/pr127445.c: New test.

    Signed-off-by: Kyrylo Tkachov <[email protected]>

Reply via email to