On Fri, Mar 13, 2026 at 4:44 AM Andrew Pinski
<[email protected]> wrote:
>
> cprop_hardreg changes:
> ```
> (insn 69 62 183 6 (set (reg:SI 1 x1 [orig:107 _12 ] [107])
>         (zero_extend:SI (mem:QI (reg/f:DI 1 x1 [150]) [0 MEM[(char *)_4]+0 S1 
> A64]))) "/app/example.cpp":8:7 discrim 1 149 {*zero_extendqisi2_aarch64}
>      (expr_list:REG_EH_REGION (const_int 2 [0x2])
>         (nil)))
> ```
>
> into:
> ```
> (insn 69 62 183 6 (set (reg:SI 1 x1 [orig:107 _12 ] [107])
>         (zero_extend:SI (mem:QI (plus:DI (reg/f:DI 31 sp)
>                     (const_int 56 [0x38])) [0 MEM[(char *)_4]+0 S1 A64]))) 
> "/app/example.cpp":8:7 discrim 1 149 {*zero_extendqisi2_aarch64}
>      (expr_list:REG_EH_REGION (const_int 2 [0x2])
>         (nil)))
> ```
>
> But that new instruction no longer traps as it is load from the stack. So 
> later on
> purge_dead_edges removes the REG_EH_REGION note and the edge to the eh 
> landing pad
> but this is during find_many_sub_basic_blocks in split3 and nothing then 
> removes the
> unreachable basic blocks.
>
> To fix this, instead of depending on a later pass to clean this up, 
> cprop_hardreg
> should call purge_all_dead_edges if non-call exceptions were enabled and then
> also call cleanup_cfg (to remove the unreachable blocks).
>
> Bootstrapped and tested on x86_64-linux-gnu and lightly tested for 
> aarch64-linux-gnu.
>
>         PR rtl-optimization/124454
>
> gcc/ChangeLog:
>
>         * regcprop.cc (pass_cprop_hardreg::execute): If something
>         changed and non-call exceptions is on, call purge_all_dead_edges
>         and cleanup_cfg.
>
> gcc/testsuite/ChangeLog:
>
>         * gcc.dg/pr124454-1.c: New test.
>
> Signed-off-by: Andrew Pinski <[email protected]>
> ---
>  gcc/regcprop.cc                   | 21 +++++++++++++++++++--
>  gcc/testsuite/gcc.dg/pr124454-1.c | 15 +++++++++++++++
>  2 files changed, 34 insertions(+), 2 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.dg/pr124454-1.c
>
> diff --git a/gcc/regcprop.cc b/gcc/regcprop.cc
> index e884cb5a966..b1f00ca0435 100644
> --- a/gcc/regcprop.cc
> +++ b/gcc/regcprop.cc
> @@ -36,6 +36,7 @@
>  #include "cfgrtl.h"
>  #include "target.h"
>  #include "function-abi.h"
> +#include "cfgcleanup.h"
>
>  /* The following code does forward propagation of hard register copies.
>     The object is to eliminate as many dependencies as possible, so that
> @@ -1443,6 +1444,7 @@ pass_cprop_hardreg::execute (function *fun)
>    auto_vec<int> *curr = &worklist1;
>    auto_vec<int> *next = &worklist2;
>    bool any_debug_changes = false;
> +  bool any_changes = false;
>
>    /* We need accurate notes.  Earlier passes such as if-conversion may
>       leave notes in an inconsistent state.  */
> @@ -1462,7 +1464,10 @@ pass_cprop_hardreg::execute (function *fun)
>    FOR_EACH_BB_FN (bb, fun)
>      {
>        if (cprop_hardreg_bb (bb, all_vd, visited))
> -       curr->safe_push (bb->index);
> +       {
> +         curr->safe_push (bb->index);
> +         any_changes = true;
> +       }
>        if (all_vd[bb->index].n_debug_insn_changes)
>         any_debug_changes = true;
>      }
> @@ -1489,7 +1494,10 @@ pass_cprop_hardreg::execute (function *fun)
>         {
>           bb = BASIC_BLOCK_FOR_FN (fun, index);
>            if (cprop_hardreg_bb (bb, all_vd, visited))
> -           next->safe_push (bb->index);
> +           {
> +             next->safe_push (bb->index);
> +             any_changes = true;
> +           }
>           if (all_vd[bb->index].n_debug_insn_changes)
>             any_debug_changes = true;
>         }
> @@ -1501,6 +1509,15 @@ pass_cprop_hardreg::execute (function *fun)
>      }
>
>    free (all_vd);
> +
> +  /* Copy propagation of the sp register into an mem's address
> +     can cause what was a trapping instruction into
> +     a non-trapping one so cleanup after that in the case of non-call
> +     exceptions.  */
> +  if (any_changes
> +      && cfun->can_throw_non_call_exceptions
> +      && purge_all_dead_edges ())
> +    cleanup_cfg (0);

OK.

Thanks,
Richard.

Btw, I see dse.cc does

  /* DSE can eliminate potentially-trapping MEMs.
     Remove any EH edges associated with them.  */
  if ((locally_deleted || globally_deleted)
      && cfun->can_throw_non_call_exceptions
      && purge_all_dead_edges ())
    {
      free_dominance_info (CDI_DOMINATORS);
      cleanup_cfg (0);

and earlier

  if ((locally_deleted || globally_deleted)
      && cfun->can_throw_non_call_exceptions
      && purge_all_dead_edges ())
    {
      free_dominance_info (CDI_DOMINATORS);
      delete_unreachable_blocks ();
    }

where the former freeing of dominance info looks overzealous.  I also wonder
about cfg_cleanup vs. delete_unreachable_blocks; the latter might be good
enough?  Or what are we expecting from cfg_cleanup here?


>    return 0;
>  }
>
> diff --git a/gcc/testsuite/gcc.dg/pr124454-1.c 
> b/gcc/testsuite/gcc.dg/pr124454-1.c
> new file mode 100644
> index 00000000000..11c4d803d1b
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/pr124454-1.c
> @@ -0,0 +1,15 @@
> +/* { dg-do compile } */
> +/* { dg-additional-options "-O3 -fno-forward-propagate -fnon-call-exceptions 
> -fno-dse -fprofile-generate -fopenmp -finstrument-functions" } */
> +/* { dg-additional-options "-mno-outline-atomics" { target aarch64*-*-* } } 
> */
> +
> +/* PR rtl-optimization/124454 */
> +
> +int a, b;
> +
> +void
> +foo()
> +{
> +  long c;
> +  a *= b;
> +  b = *(char *)__builtin_memset(&c, 1, 4);
> +}
> --
> 2.43.0
>

Reply via email to