What seems to have happened is that the instructions to push argument 3 have inherited the output reload of --%sp into %r1 from the instructions pushing argument 5, but GCC hasn't noted that the pushl $0 for argument 4 has also changed %sp so the output reload can't be reused./* function call (5 arguments)
cp_finish_decl(l, 0, am->ad.e.ab.d, __null, au);
*/
movl *8(%r6),%r0
subl2 $4,%sp /* space for argument 5 */
movl %sp,%r1
movb 4(%r0),%r0
rotl $21,%r0,(%r1)
bicl2 $-8388609,(%r1) /* argument 5 */
pushl $0 /* argument 4 */
movl am,%r0
movzbl 4(%r0),%r0
movl %sp,%r1
rotl $30,%r0,(%r1)
bicl2 $-2,(%r1)
/* argument 3
but it overwrites argument 4
oops */
pushl $0 /* argument 2 */
pushl %r6 /* argument 1 */
calls $5,_Z14cp_finish_declP9tree_nodeS0_bS0_i
I'm not yet sure what the cause is, but I have noticed that the output from g++'s 'reload' pass isn't correct.
There's a block of code in gcc/reload1.c which does invalidate old output reloads; the attached patch adds to this block to invalidate a reload of an output register if the output register is a reference to memory using an autoincrement or autodecrement addressing mode.
With the patch applied, I've been able to compile the test case
using the resulting native compiler, and a native build of NetBSD
hasn't failed yet, which is promising. I have not applied any
workarounds to sancov.c or to tree-switch-conversion.c, and I'm
also compiling GCC with the default optimisation level (-O2; I
haven't applied any of Matthew's changes mentioned earlier).
I've also applied this change to a copy of GCC 10.5.0 from
upstream and on amd64 at least there was no change to the
regression test results so I don't think this introduces any
regressions.
I'm curious to know if this will work for anyone else.
cheers
kalvis
diff --git a/external/gpl3/gcc.old/dist/gcc/reload1.c b/external/gpl3/gcc.old/dist/gcc/reload1.c index 88f4727d5453..8d481071a12b 100644 --- a/external/gpl3/gcc.old/dist/gcc/reload1.c +++ b/external/gpl3/gcc.old/dist/gcc/reload1.c @@ -8377,6 +8377,43 @@ emit_reload_insns (class insn_chain *chain) reg_last_reload_reg[out_regno + k] = 0; } } + +#if AUTO_INC_DEC /* XXX KD */ + /* Where an output register might be reloaded, and it is a + memory reference, and the address is auto-incremented, any + previously reloaded copy of the address must be + invalidated. */ + if (i < 0 + && rld[r].out != 0 + && MEM_P (rld[r].out)) + { + rtx out = XEXP (rld[r].out, 0); /* address expression */ + enum rtx_code code = GET_CODE (out); + + if (code != POST_INC && code != POST_DEC + && code != PRE_INC && code != PRE_DEC) + { + /* do nothing */ + } + else + { + int out_regno = REGNO (XEXP (out, 0)); + machine_mode mode = GET_MODE (XEXP (out, 0)); + + /* for the moment, handle only the case where out_regno + is a hardware register */ + + if (HARD_REGISTER_NUM_P (out_regno)) + { + int k, out_nregs = hard_regno_nregs (out_regno, mode); + + for (k = 0; k < out_nregs; k++) + reg_last_reload_reg[out_regno + k] = 0; + } + } + } +#endif /* AUTO_INC_DEC */ /* XXX KD */ + } reg_reloaded_dead |= reg_reloaded_died; }