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

--- Comment #8 from GCC Commits <cvs-commit at gcc dot gnu.org> ---
The trunk branch has been updated by Richard Sandiford <rsand...@gcc.gnu.org>:

https://gcc.gnu.org/g:da3f2a561649c7c4899449c6b3ab2b6d67792a71

commit r16-1774-gda3f2a561649c7c4899449c6b3ab2b6d67792a71
Author: Richard Sandiford <richard.sandif...@arm.com>
Date:   Mon Jun 30 08:52:26 2025 +0100

    lra: Check for null lowpart_subregs [PR120733]

    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.

Reply via email to