If combine is given a parallel I2, it splits it into separate I1 and I2 insns in some cases (one case since the beginning of time; more cases since my r218248). It gives the new I1 the same UID as the I2 already has; there is a comment that this works fine because the I1 never is added to the insn stream.
When combine eventually replaces the insns with something new, it calls SET_INSN_DELETED on those it wants gone. Since r125624 (when DF was added, back in 2007) SET_INSN_DELETED uses the UID of the insn it is called for to do the deletion for dataflow. So since then, when such a split I1 is deleted, DF thinks I2 is deleted as well. This of course explodes if I2 is in fact needed (but only if that I2 still exists at the end of combine, i.e. the insn was not combined to further down; that might explain why it wasn't noticed before). This should be fixed properly, but it is somewhat involved. This patch simply disallows all combinations coming from a split parallel I2 if it needs a new I2. This fixes PR target/64268. Bootstrapped on powerpc64-linux and powerpc-linux; the fails are gone. Regtested on powerpc64-linux -m32,-m32/-mpowerpc64,-m64,-m64/-mlra. Is this okay for mainline? Segher 2014-12-15 Segher Boessenkool <seg...@kernel.crashing.org> gcc/ * combine.c (try_combine): Don't try to split newpat if we started with I2 a PARALLEL that we split. --- gcc/combine.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/gcc/combine.c b/gcc/combine.c index 8995c1d3..de2e49f 100644 --- a/gcc/combine.c +++ b/gcc/combine.c @@ -3471,6 +3471,17 @@ try_combine (rtx_insn *i3, rtx_insn *i2, rtx_insn *i1, rtx_insn *i0, if (insn_code_number < 0) insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); + /* If we got I1 and I2 from splitting the original (parallel) I2 into two, + I1 and I2 have the same UID, which means that DF ends up deleting I2 + when it is asked to delete I1. So only allow this if we want I2 deleted, + that is, if we get only one insn as combine result; don't try to split + off a new I2 if it doesn't match yet. */ + if (i1 && insn_code_number < 0 && INSN_UID (i1) == INSN_UID (i2)) + { + undo_all (); + return 0; + } + /* If we were combining three insns and the result is a simple SET with no ASM_OPERANDS that wasn't recognized, try to split it into two insns. There are two ways to do this. It can be split using a -- 1.8.1.4