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