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.