Hi! In gimple_fold_call (called from fold_stmt from replace_uses_by from cfg cleanup) we weren't calling maybe_clean_or_replace_eh_stmt, so when we've replaced a printf call (which can throw) with puts (which can throw too), nothing would update EH stmts. It would be problematic calling gimple_purge_dead_eh_edges, because callers might be surprised by that, especially when this happens during cfg cleanup, so instead I just assert it is not needed and don't try to fold if a throwing stmt would be replaced by non-throwing. FAB pass can handle that instead. No folding has been actually disabled because of that check during bootstrap/regtest, so it is there just in case.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2011-12-12 Jakub Jelinek <ja...@redhat.com> PR tree-optimization/51481 * gimple-fold.c (gimple_fold_call): Call maybe_clean_or_replace_eh_stmt. Avoid optimization if stmt has EH edges, but gimple_fold_builtin result can't throw. * gcc.dg/pr51481.c: New test. --- gcc/gimple-fold.c.jj 2011-12-11 22:02:37.000000000 +0100 +++ gcc/gimple-fold.c 2011-12-12 11:42:34.740168390 +0100 @@ -1117,10 +1117,21 @@ gimple_fold_call (gimple_stmt_iterator * if (callee && DECL_BUILT_IN (callee)) { tree result = gimple_fold_builtin (stmt); - if (result) + if (result + /* Disallow EH edge removal here. We can't call + gimple_purge_dead_eh_edges here. */ + && (lookup_stmt_eh_lp (stmt) == 0 + || tree_could_throw_p (result))) { if (!update_call_from_tree (gsi, result)) gimplify_and_update_call_from_tree (gsi, result); + if (!gsi_end_p (*gsi)) + { + gimple new_stmt = gsi_stmt (*gsi); + bool update_eh ATTRIBUTE_UNUSED + = maybe_clean_or_replace_eh_stmt (stmt, new_stmt); + gcc_assert (!update_eh); + } changed = true; } } --- gcc/testsuite/gcc.dg/pr51481.c.jj 2011-12-12 11:18:27.304678207 +0100 +++ gcc/testsuite/gcc.dg/pr51481.c 2011-12-12 11:18:02.000000000 +0100 @@ -0,0 +1,33 @@ +/* PR tree-optimization/51481 */ +/* { dg-do compile } */ +/* { dg-options "-O -fexceptions -fipa-cp -fipa-cp-clone" } */ + +extern const unsigned short int **foo (void) + __attribute__ ((__nothrow__, __const__)); +struct S { unsigned short s1; int s2; }; +extern struct S *s[26]; + +void +bar (int x, struct S *y, ...) +{ + static struct S *t; + __builtin_va_list ap; + __builtin_va_start (ap, y); + if (t != s[7]) + { + const char *p = "aAbBc"; + t = s[7]; + while ((*foo ())[(unsigned char) *p]) + p++; + } + __builtin_printf (x == 0 ? "abc\n" : "def\n"); + if (y != 0) + __builtin_printf ("ghi %d %d", y->s2, y->s1); + __builtin_va_end (ap); +} + +void +baz (char *x) +{ + bar (1, 0, x); +} Jakub