+\f
+/* Returns TRUE if oneh and twoh are exception handlers (gimple_try_cleanup of
+ GIMPLE_TRY) that are similar enough to be considered the same. Currently
+ this only handles handlers consisting of a single call, as that's the
+ important case for C++: a destructor call for a particular object showing
+ up in multiple handlers. */
+
+static bool
+same_handler_p (gimple_seq oneh, gimple_seq twoh)
+{
+ gimple_stmt_iterator gsi;
+ gimple ones, twos;
+ unsigned int ai;
+
+ gsi = gsi_start (oneh);
+ if (!gsi_one_before_end_p (gsi))
+ return false;
+ ones = gsi_stmt (gsi);
+
+ gsi = gsi_start (twoh);
+ if (!gsi_one_before_end_p (gsi))
+ return false;
+ twos = gsi_stmt (gsi);
+
+ if (!is_gimple_call (ones)
+ || !is_gimple_call (twos)
+ || gimple_call_lhs (ones)
+ || gimple_call_lhs (twos)
+ || gimple_call_chain (ones)
+ || gimple_call_chain (twos)
+ || !operand_equal_p (gimple_call_fn (ones), gimple_call_fn (twos), 0)
+ || gimple_call_num_args (ones) != gimple_call_num_args (twos))
+ return false;
+
+ for (ai = 0; ai < gimple_call_num_args (ones); ++ai)
+ if (!operand_equal_p (gimple_call_arg (ones, ai),
+ gimple_call_arg (twos, ai), 0))
+ return false;
+
+ return true;
+}
+
+/* Optimize
+ try { A() } finally { try { ~B() } catch { ~A() } }
+ try { ... } finally { ~A() }
+ into
+ try { A() } catch { ~B() }
+ try { ~B() ... } finally { ~A() }
+
+ This occurs frequently in C++, where A is a local variable and B is a
+ temporary used in the initializer for A. */
+
+static void
+optimize_double_finally (gimple one, gimple two)
+{
+ gimple oneh;
+ gimple_stmt_iterator gsi;
+
+ gsi = gsi_start (gimple_try_cleanup (one));
+ if (!gsi_one_before_end_p (gsi))
+ return;
+
+ oneh = gsi_stmt (gsi);
+ if (gimple_code (oneh) != GIMPLE_TRY
+ || gimple_try_kind (oneh) != GIMPLE_TRY_CATCH)
+ return;
+
+ if (same_handler_p (gimple_try_cleanup (oneh), gimple_try_cleanup (two)))
+ {
+ gimple_seq seq = gimple_try_eval (oneh);
+
+ gimple_try_set_cleanup (one, seq);
+ gimple_try_set_kind (one, GIMPLE_TRY_CATCH);
+ seq = copy_gimple_seq_and_replace_locals (seq);
+ gimple_seq_add_seq (&seq, gimple_try_eval (two));
+ gimple_try_set_eval (two, seq);
+ }
+}
+
+/* Perform EH refactoring optimizations that are simpler to do when code
+ flow has been lowered but EH structures haven't. */
+
+static void
+refactor_eh_r (gimple_seq seq)
+{
+ gimple_stmt_iterator gsi;
+ gimple one, two;
+
+ one = NULL;
+ two = NULL;
+ gsi = gsi_start (seq);
+ while (1)
+ {
+ one = two;
+ if (gsi_end_p (gsi))
+ two = NULL;
+ else
+ two = gsi_stmt (gsi);
+ if (one
+ && two
+ && gimple_code (one) == GIMPLE_TRY
+ && gimple_code (two) == GIMPLE_TRY
+ && gimple_try_kind (one) == GIMPLE_TRY_FINALLY
+ && gimple_try_kind (two) == GIMPLE_TRY_FINALLY)
+ optimize_double_finally (one, two);
+ if (one)
+ switch (gimple_code (one))
+ {
+ case GIMPLE_TRY:
+ refactor_eh_r (gimple_try_eval (one));
+ refactor_eh_r (gimple_try_cleanup (one));
+ break;
+ case GIMPLE_CATCH:
+ refactor_eh_r (gimple_catch_handler (one));
+ break;
+ case GIMPLE_EH_FILTER:
+ refactor_eh_r (gimple_eh_filter_failure (one));
+ break;
+ default:
+ break;
+ }
+ if (two)
+ gsi_next (&gsi);
+ else
+ break;
+ }
+}
+
+static unsigned
+refactor_eh (void)
+{
+ refactor_eh_r (gimple_body (current_function_decl));
+ return 0;
+}
+
+struct gimple_opt_pass pass_refactor_eh =
+{
+ {
+ GIMPLE_PASS,
+ "ehopt", /* name */
+ NULL, /* gate */
+ refactor_eh, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ TV_TREE_EH, /* tv_id */
+ PROP_gimple_lcf, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_dump_func /* todo_flags_finish */
+ }
+};