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.

Diff:
---
 gcc/lra-eliminations.cc | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/gcc/lra-eliminations.cc b/gcc/lra-eliminations.cc
index 045f2dcf23ef..9103ef10cb5c 100644
--- a/gcc/lra-eliminations.cc
+++ b/gcc/lra-eliminations.cc
@@ -302,9 +302,12 @@ move_plus_up (rtx x)
                                 subreg_lowpart_offset (x_mode,
                                                        subreg_reg_mode));
       if (cst && CONSTANT_P (cst))
-       return gen_rtx_PLUS (x_mode, lowpart_subreg (x_mode,
-                                                    XEXP (subreg_reg, 0),
-                                                    subreg_reg_mode), cst);
+       {
+         rtx lowpart = lowpart_subreg (x_mode, XEXP (subreg_reg, 0),
+                                       subreg_reg_mode);
+         if (lowpart)
+           return gen_rtx_PLUS (x_mode, lowpart, cst);
+       }
     }
   return x;
 }

Reply via email to