On 6/24/25 4:30 AM, Richard Sandiford wrote:
lra-eliminations.cc:move_plus_up tries to:

    Transform (subreg (plus reg const)) to (plus (subreg reg) const)
    when it is possible.

Most of it is heavily conditional:

   if (!paradoxical_subreg_p (x)
       && GET_CODE (subreg_reg) == PLUS
       && CONSTANT_P (XEXP (subreg_reg, 1))
       && GET_MODE_CLASS (x_mode) == MODE_INT
       && GET_MODE_CLASS (subreg_reg_mode) == MODE_INT)
     {
       rtx cst = simplify_subreg (x_mode, XEXP (subreg_reg, 1), subreg_reg_mode,
                                 subreg_lowpart_offset (x_mode,
                                                        subreg_reg_mode));
       if (cst && CONSTANT_P (cst))

but the final:

        return gen_rtx_PLUS (x_mode, lowpart_subreg (x_mode,
                                                     XEXP (subreg_reg, 0),
                                                     subreg_reg_mode), cst);

assumed without checking that lowpart_subreg succeeded.  In the PR,
this led to creating a PLUS with a null operand.

In more detail, the testcase had:

     (var_location a (plus:SI (subreg:SI (reg/f:DI 64 sfp) 0)
         (const_int -4 [0xfffffffffffffffc])))

with sfp being eliminated to (plus:DI (reg:DI sp) (const_int 16)).
Initially, during the !subst_p phase, lra_eliminate_regs_1 sees
the PLUS and recurses into each operand.  The recursive call sees
the SUBREG and recurses into the SUBREG_REG.  Since !subst_p,
this final recursive call replaces (reg:DI sfp) with:

     (plus:DI (reg:DI sfp) (const_int 16))

(i.e. keeping the base register the same).  So the SUBREG is
eliminated to:

     (subreg:SI (plus:DI (reg:DI sfp) (const_int 16)) 0)

The PLUS handling in lra_eliminate_regs_1 then passes this to
move_plus_up, which tries to push the SUBREG into the PLUS.
This means trying to create:

     (plus:SI (simplify_gen_subreg:SI (reg:DI sfp) 0) (const_int 16))

The simplify_gen_subreg then returns null, because simplify_subreg_regno
fails both with allow_stack_regs==false (when trying to simplify the
SUBREG to a REG) and with allow_stack_regs=true (when validating
whether the SUBREG can be generated).  And that in turn happens
because aarch64 refuses to allow SImode to be stored in sfp:

   if (regno == SP_REGNUM)
     /* The purpose of comparing with ptr_mode is to support the
        global register variable associated with the stack pointer
        register via the syntax of asm ("wsp") in ILP32.  */
     return mode == Pmode || mode == ptr_mode;

   if (regno == FRAME_POINTER_REGNUM || regno == ARG_POINTER_REGNUM)
     return mode == Pmode;

This seems dubious.  If the frame pointer can hold a DImode value then it
can also hold an SImode value.  There might be limited cases when the low
32 bits of the frame pointer are useful, but aarch64_hard_regno_mode_ok
doesn't have the context to second-guess things like that.  It seemed
from a quick scan of other targets that they behave more as I'd expect.

So there might be a target bug here too.  But it seemed worth fixing the
unchecked use of lowpart_subreg independently of that.

The patch fixes an existing ICE in gcc.c-torture/compile/pass.c.

gcc/
        PR rtl-optimization/120733
        * lra-eliminations.cc (move_plus_up): Check whether lowpart_subreg
        returns null.
Yea, even if there's a target issue at play, this seems like the safer way to handle this. So OK for the trunk if you haven't installed it already.

jeff

Reply via email to