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. --- 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 bb708b007a4..c651e70d274 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; } -- 2.43.0