We have two registers being assigned to each other: (set (reg 213) (reg 209)) (set (reg 209) (reg 213))
These being the only definitions, we are happy to forward propagate reg 209 for reg 213 into a third insn, making a new use for reg 209. We are then happy to forward propagate reg 213 for it in the same insn... ending up in an infinite loop.
I don't really see an elegant way to prevent this, so the following just tries to detect the situation (and more general ones) by brute force. Bootstrapped and tested on x86_64-linux, verified that the test passes with a ppc cross, ok?
Bernd
PR rtl-optimization/79405 * fwprop.c (forward_propagate_into): Detect potentially cyclic replacements and bail out for them. PR rtl-optimization/79405 * gcc.dg/torture/pr79405.c: New test. Index: gcc/fwprop.c =================================================================== --- gcc/fwprop.c (revision 244815) +++ gcc/fwprop.c (working copy) @@ -1374,13 +1374,42 @@ forward_propagate_into (df_ref use) /* Only consider uses that have a single definition. */ def = get_def_for_use (use); - if (!def) + if (!def || DF_REF_INSN_INFO (def) == NULL) return false; if (DF_REF_FLAGS (def) & DF_REF_READ_WRITE) return false; if (DF_REF_IS_ARTIFICIAL (def)) return false; + df_ref tmp_def = def; + /* There is a problematic case where a chain of assignments + rA = rB; rB = rC; .... ; rM = rN; rN = rA. + can cause us to replace these registers in an infinite cycle. + Walk backwards until we can guarantee that this situation is + not present. */ + for (;;) + { + rtx_insn *insn = DF_REF_INSN (tmp_def); + rtx set = single_set (insn); + if (set == NULL_RTX) + break; + rtx src = SET_SRC (set); + rtx dst = SET_DEST (set); + if (GET_CODE (src) != REG || GET_CODE (dst) != REG) + break; + if (rtx_equal_p (src, DF_REF_REG (use))) + return false; + df_ref tmp_use = df_single_use (DF_REF_INSN_INFO (tmp_def)); + if (!tmp_use) + break; + tmp_def = get_def_for_use (tmp_use); + if (!tmp_def || DF_REF_INSN_INFO (tmp_def) == NULL) + break; + if (DF_REF_FLAGS (tmp_def) & DF_REF_READ_WRITE) + break; + if (DF_REF_IS_ARTIFICIAL (tmp_def)) + break; + } /* Do not propagate loop invariant definitions inside the loop. */ if (DF_REF_BB (def)->loop_father != DF_REF_BB (use)->loop_father) return false; Index: gcc/testsuite/gcc.dg/torture/pr79405.c =================================================================== --- gcc/testsuite/gcc.dg/torture/pr79405.c (nonexistent) +++ gcc/testsuite/gcc.dg/torture/pr79405.c (working copy) @@ -0,0 +1,42 @@ +/* { dg-do compile } */ +char cz; +long long int xx, u2; + +void +qv (int js, int wl) +{ + if (js != 0) + { + short int sc; + int *at = (int *)≻ + long long int gx = 0; + + for (;;) + { + *at = 0; + js /= sc; + + for (wl = 0; wl < 2; ++wl) + { + xx = gx; + u2 %= xx > 0; + cz /= u2; + + fa: + if (cz != u2) + { + gx |= js; + cz = gx / js; + } + } + } + + yq: + wl /= 0x80000000; + u2 = wl; + u2 |= (wl != 0) | (wl != 0 && gx != 0); + js = u2; + goto fa; + } + goto yq; +}