On Mon, Feb 16, 2026 at 09:07:36AM +0100, Robin Dapp wrote:
> Hi,
> 
> We can perform equivalence substitution in subreg context:
> 
> (insn 34 32 36 3 (set (reg:SI 103 [ _7 ])
>         (subreg:SI (reg/f:DI 119) 0)) "bla.c":7:41 104 {*movsi_aarch64}
> 
> becomes
> 
> (insn 34 32 36 3 (set (reg:SI 103 [ _7 ])
>         (subreg:SI (reg/f:DI 64 sfp) 0)) "bla.c":7:41 104 {*movsi_aarch64}
>      (nil))
> 
> but aarch64_hard_regno_mode_ok doesn't like that:
> 
>   if (regno == FRAME_POINTER_REGNUM || regno == ARG_POINTER_REGNUM)
>     return mode == Pmode;
> 
> and ICEs further on.
> 
> Therefore, this patch checks hard_regno_mode_ok if we substitute a hard
> reg in subreg context.

Would it make sense to pull the check into get_equiv_with_elimination(),
i.e., let the function look through the subreg and deal with invalid
substitutions?  Then the check would also apply at another call side in
loc_equivalence_change_p() where we also deal with subregs and potential
invalid substitutions.

Either way, this looks reasonable to me.

Cheers,
Stefan

> 
> Bootstrapped and regtested on x86, power10, and aarch64.
> Regtested on riscv64.
> 
> Regards
>  Robin
> 
> 
>       PR rtl-optimization/124041
> 
> gcc/ChangeLog:
> 
>       * lra-constraints.cc (curr_insn_transform): Check if hardreg is
>       valid in subreg context.
> 
> gcc/testsuite/ChangeLog:
> 
>       * gcc.dg/torture/pr124041.c: New test.
> ---
>  gcc/lra-constraints.cc                  | 14 +++++++++++++-
>  gcc/testsuite/gcc.dg/torture/pr124041.c | 17 +++++++++++++++++
>  2 files changed, 30 insertions(+), 1 deletion(-)
>  create mode 100644 gcc/testsuite/gcc.dg/torture/pr124041.c
> 
> diff --git a/gcc/lra-constraints.cc b/gcc/lra-constraints.cc
> index 8ea05bff0d7..bc8fd658500 100644
> --- a/gcc/lra-constraints.cc
> +++ b/gcc/lra-constraints.cc
> @@ -4377,11 +4377,23 @@ curr_insn_transform (bool check_only_p)
>         continue;
>  
>       old = op = *curr_id->operand_loc[i];
> +     machine_mode outer_mode = GET_MODE (old);
> +     bool subreg_p = false;
>       if (GET_CODE (old) == SUBREG)
> -       old = SUBREG_REG (old);
> +       {
> +         old = SUBREG_REG (old);
> +         subreg_p = true;
> +       }
>       subst = get_equiv_with_elimination (old, curr_insn);
>       original_subreg_reg_mode[i] = VOIDmode;
>       equiv_substition_p[i] = false;
> +
> +     /* If we are about to replace a register inside a subreg, check if
> +        the target can handle that.  */
> +     if (subreg_p && REG_P (subst) && HARD_REGISTER_P (subst)
> +         && !targetm.hard_regno_mode_ok (REGNO (subst), outer_mode))
> +       continue;
> +
>       if (subst != old)
>         {
>           equiv_substition_p[i] = true;
> diff --git a/gcc/testsuite/gcc.dg/torture/pr124041.c 
> b/gcc/testsuite/gcc.dg/torture/pr124041.c
> new file mode 100644
> index 00000000000..f966757e4b5
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/torture/pr124041.c
> @@ -0,0 +1,17 @@
> +/* { dg-compile } */
> +/* { dg-additional-options "-O2  -fno-strict-aliasing -fstack-protector 
> -ftrivial-auto-var-init=zero -fsanitize=thread -Wall" } */
> +
> +char *trick_compiler(void *);
> +int do_usercopy_stack_callee_i;
> +char *do_usercopy_stack_callee(long value)
> +{
> +    char buf[28];
> +    for (; do_usercopy_stack_callee_i < sizeof(buf); 
> do_usercopy_stack_callee_i++)
> +        buf[do_usercopy_stack_callee_i] = value;
> +    return trick_compiler(buf);
> +}
> +void do_usercopy_stack(void)
> +{
> +    char *bad_stack;
> +    bad_stack = do_usercopy_stack_callee((long)&bad_stack);
> +}
> -- 
> 2.53.0

Reply via email to