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;
}