re PR tree-optimization/93199 (Compile time hog in sink_clobbers)
[gcc.git] / gcc / tree-eh.c
index 75385f7b53f1462ac73cec0a912a8847093becf1..dc80f574a2cb9eb66e1d4dc9d2f14cef0f906d53 100644 (file)
@@ -1,5 +1,5 @@
 /* Exception handling semantics and decomposition for trees.
-   Copyright (C) 2003-2018 Free Software Foundation, Inc.
+   Copyright (C) 2003-2020 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -139,19 +139,19 @@ remove_stmt_from_eh_lp (gimple *t)
    statement is not recorded in the region table.  */
 
 int
-lookup_stmt_eh_lp_fn (struct function *ifun, gimple *t)
+lookup_stmt_eh_lp_fn (struct function *ifun, const gimple *t)
 {
   if (ifun->eh->throw_stmt_table == NULL)
     return 0;
 
-  int *lp_nr = ifun->eh->throw_stmt_table->get (t);
+  int *lp_nr = ifun->eh->throw_stmt_table->get (const_cast <gimple *> (t));
   return lp_nr ? *lp_nr : 0;
 }
 
 /* Likewise, but always use the current function.  */
 
 int
-lookup_stmt_eh_lp (gimple *t)
+lookup_stmt_eh_lp (const gimple *t)
 {
   /* We can get called from initialized data when -fnon-call-exceptions
      is on; prevent crash.  */
@@ -503,7 +503,11 @@ replace_goto_queue_1 (gimple *stmt, struct leh_tf_state *tf,
       seq = find_goto_replacement (tf, temp);
       if (seq)
        {
-         gsi_insert_seq_before (gsi, gimple_seq_copy (seq), GSI_SAME_STMT);
+         gimple_stmt_iterator i;
+         seq = gimple_seq_copy (seq);
+         for (i = gsi_start (seq); !gsi_end_p (i); gsi_next (&i))
+           gimple_set_location (gsi_stmt (i), gimple_location (stmt));
+         gsi_insert_seq_before (gsi, seq, GSI_SAME_STMT);
          gsi_remove (gsi, false);
          return;
        }
@@ -811,15 +815,6 @@ emit_resx (gimple_seq *seq, eh_region region)
     record_stmt_eh_region (region->outer, x);
 }
 
-/* Emit an EH_DISPATCH statement into SEQ for REGION.  */
-
-static void
-emit_eh_dispatch (gimple_seq *seq, eh_region region)
-{
-  geh_dispatch *x = gimple_build_eh_dispatch (region->index);
-  gimple_seq_add_stmt (seq, x);
-}
-
 /* Note that the current EH region may contain a throw, or a
    call to a function which itself may contain a throw.  */
 
@@ -1001,11 +996,14 @@ honor_protect_cleanup_actions (struct leh_state *outer_state,
       gimple_try_set_cleanup (tf->top_p, gimple_eh_else_n_body (eh_else));
       finally = gimple_eh_else_e_body (eh_else);
 
-      /* Let the ELSE see the exception that's being processed.  */
-      eh_region save_ehp = this_state->ehp_region;
-      this_state->ehp_region = this_state->cur_region;
-      lower_eh_constructs_1 (this_state, &finally);
-      this_state->ehp_region = save_ehp;
+      /* Let the ELSE see the exception that's being processed, but
+        since the cleanup is outside the try block, process it with
+        outer_state, otherwise it may be used as a cleanup for
+        itself, and Bad Things (TM) ensue.  */
+      eh_region save_ehp = outer_state->ehp_region;
+      outer_state->ehp_region = this_state->cur_region;
+      lower_eh_constructs_1 (outer_state, &finally);
+      outer_state->ehp_region = save_ehp;
     }
   else
     {
@@ -1762,7 +1760,9 @@ lower_catch (struct leh_state *state, gtry *tp)
   tree out_label;
   gimple_seq new_seq, cleanup;
   gimple *x;
+  geh_dispatch *eh_dispatch;
   location_t try_catch_loc = gimple_location (tp);
+  location_t catch_loc = UNKNOWN_LOCATION;
 
   if (flag_exceptions)
     {
@@ -1776,7 +1776,8 @@ lower_catch (struct leh_state *state, gtry *tp)
     return gimple_try_eval (tp);
 
   new_seq = NULL;
-  emit_eh_dispatch (&new_seq, try_region);
+  eh_dispatch = gimple_build_eh_dispatch (try_region->index);
+  gimple_seq_add_stmt (&new_seq, eh_dispatch);
   emit_resx (&new_seq, try_region);
 
   this_state.cur_region = state->cur_region;
@@ -1799,6 +1800,8 @@ lower_catch (struct leh_state *state, gtry *tp)
       gimple_seq handler;
 
       catch_stmt = as_a <gcatch *> (gsi_stmt (gsi));
+      if (catch_loc == UNKNOWN_LOCATION)
+       catch_loc = gimple_location (catch_stmt);
       c = gen_eh_region_catch (try_region, gimple_catch_types (catch_stmt));
 
       handler = gimple_catch_handler (catch_stmt);
@@ -1822,6 +1825,10 @@ lower_catch (struct leh_state *state, gtry *tp)
        break;
     }
 
+  /* Try to set a location on the dispatching construct to avoid inheriting
+     the location of the previous statement.  */
+  gimple_set_location (eh_dispatch, catch_loc);
+
   gimple_try_set_cleanup (tp, new_seq);
 
   gimple_seq new_eh_seq = eh_seq;
@@ -1857,11 +1864,13 @@ lower_eh_filter (struct leh_state *state, gtry *tp)
   if (!eh_region_may_contain_throw (this_region))
     return gimple_try_eval (tp);
 
-  new_seq = NULL;
   this_state.cur_region = state->cur_region;
   this_state.ehp_region = this_region;
 
-  emit_eh_dispatch (&new_seq, this_region);
+  new_seq = NULL;
+  x = gimple_build_eh_dispatch (this_region->index);
+  gimple_set_location (x, gimple_location (tp));
+  gimple_seq_add_stmt (&new_seq, x);
   emit_resx (&new_seq, this_region);
 
   this_region->u.allowed.label = create_artificial_label (UNKNOWN_LOCATION);
@@ -1984,7 +1993,7 @@ lower_eh_constructs_2 (struct leh_state *state, gimple_stmt_iterator *gsi)
        tree fndecl = gimple_call_fndecl (stmt);
        tree rhs, lhs;
 
-       if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
+       if (fndecl && fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
          switch (DECL_FUNCTION_CODE (fndecl))
            {
            case BUILT_IN_EH_POINTER:
@@ -2028,12 +2037,12 @@ lower_eh_constructs_2 (struct leh_state *state, gimple_stmt_iterator *gsi)
       /* FALLTHRU */
 
     case GIMPLE_ASSIGN:
-      /* If the stmt can throw use a new temporary for the assignment
+      /* If the stmt can throw, use a new temporary for the assignment
          to a LHS.  This makes sure the old value of the LHS is
         available on the EH edge.  Only do so for statements that
         potentially fall through (no noreturn calls e.g.), otherwise
         this new assignment might create fake fallthru regions.  */
-      if (stmt_could_throw_p (stmt)
+      if (stmt_could_throw_p (cfun, stmt)
          && gimple_has_lhs (stmt)
          && gimple_stmt_may_fallthru (stmt)
          && !tree_could_throw_p (gimple_get_lhs (stmt))
@@ -2051,7 +2060,7 @@ lower_eh_constructs_2 (struct leh_state *state, gimple_stmt_iterator *gsi)
          gsi_insert_after (gsi, s, GSI_SAME_STMT);
        }
       /* Look for things that can throw exceptions, and record them.  */
-      if (state->cur_region && stmt_could_throw_p (stmt))
+      if (state->cur_region && stmt_could_throw_p (cfun, stmt))
        {
          record_stmt_eh_region (state->cur_region, stmt);
          note_eh_region_may_contain_throw (state->cur_region);
@@ -2231,7 +2240,7 @@ make_eh_dispatch_edges (geh_dispatch *stmt)
     case ERT_TRY:
       for (c = r->u.eh_try.first_catch; c ; c = c->next_catch)
        {
-         dst = label_to_block (c->label);
+         dst = label_to_block (cfun, c->label);
          make_edge (src, dst, 0);
 
          /* A catch-all handler doesn't have a fallthru.  */
@@ -2241,7 +2250,7 @@ make_eh_dispatch_edges (geh_dispatch *stmt)
       break;
 
     case ERT_ALLOWED_EXCEPTIONS:
-      dst = label_to_block (r->u.allowed.label);
+      dst = label_to_block (cfun, r->u.allowed.label);
       make_edge (src, dst, 0);
       break;
 
@@ -2270,7 +2279,7 @@ make_eh_edges (gimple *stmt)
   gcc_assert (lp != NULL);
 
   src = gimple_bb (stmt);
-  dst = label_to_block (lp->post_landing_pad);
+  dst = label_to_block (cfun, lp->post_landing_pad);
   make_edge (src, dst, EDGE_EH);
 }
 
@@ -2301,7 +2310,7 @@ redirect_eh_edge_1 (edge edge_in, basic_block new_bb, bool change_region)
   old_lp = get_eh_landing_pad_from_number (old_lp_nr);
 
   throw_stmt = last_stmt (edge_in->src);
-  gcc_assert (lookup_stmt_eh_lp (throw_stmt) == old_lp_nr);
+  gcc_checking_assert (lookup_stmt_eh_lp (throw_stmt) == old_lp_nr);
 
   new_label = gimple_block_label (new_bb);
 
@@ -2389,7 +2398,7 @@ redirect_eh_dispatch_edge (geh_dispatch *stmt, edge e, basic_block new_bb)
     case ERT_TRY:
       for (c = r->u.eh_try.first_catch; c ; c = c->next_catch)
        {
-         old_bb = label_to_block (c->label);
+         old_bb = label_to_block (cfun, c->label);
          if (old_bb == e->dest)
            {
              c->label = new_lab;
@@ -2399,7 +2408,7 @@ redirect_eh_dispatch_edge (geh_dispatch *stmt, edge e, basic_block new_bb)
       break;
 
     case ERT_ALLOWED_EXCEPTIONS:
-      old_bb = label_to_block (r->u.allowed.label);
+      old_bb = label_to_block (cfun, r->u.allowed.label);
       gcc_assert (old_bb == e->dest);
       r->u.allowed.label = new_lab;
       any_changed = true;
@@ -2436,7 +2445,7 @@ operation_could_trap_helper_p (enum tree_code op,
     case ROUND_MOD_EXPR:
     case TRUNC_MOD_EXPR:
     case RDIV_EXPR:
-      if (honor_snans || honor_trapv)
+      if (honor_snans)
        return true;
       if (fp_operation)
        return flag_trapping_math;
@@ -2471,6 +2480,10 @@ operation_could_trap_helper_p (enum tree_code op,
        return true;
       return false;
 
+    case ABSU_EXPR:
+      /* ABSU_EXPR never traps.  */
+      return false;
+
     case PLUS_EXPR:
     case MINUS_EXPR:
     case MULT_EXPR:
@@ -2486,6 +2499,14 @@ operation_could_trap_helper_p (enum tree_code op,
       /* Constructing an object cannot trap.  */
       return false;
 
+    case COND_EXPR:
+    case VEC_COND_EXPR:
+      /* Whether *COND_EXPR can trap depends on whether the
+        first argument can trap, so signal it as not handled.
+        Whether lhs is floating or not doesn't matter.  */
+      *handled = false;
+      return false;
+
     default:
       /* Any floating arithmetic may trap.  */
       if (fp_operation && flag_trapping_math)
@@ -2510,10 +2531,13 @@ operation_could_trap_p (enum tree_code op, bool fp_operation, bool honor_trapv,
   bool honor_snans = fp_operation && flag_signaling_nans != 0;
   bool handled;
 
+  /* This function cannot tell whether or not COND_EXPR and VEC_COND_EXPR could
+     trap, because that depends on the respective condition op.  */
+  gcc_assert (op != COND_EXPR && op != VEC_COND_EXPR);
+
   if (TREE_CODE_CLASS (op) != tcc_comparison
       && TREE_CODE_CLASS (op) != tcc_unary
-      && TREE_CODE_CLASS (op) != tcc_binary
-      && op != FMA_EXPR)
+      && TREE_CODE_CLASS (op) != tcc_binary)
     return false;
 
   return operation_could_trap_helper_p (op, fp_operation, honor_trapv,
@@ -2598,6 +2622,13 @@ tree_could_trap_p (tree expr)
   if (!expr)
     return false;
 
+  /* In COND_EXPR and VEC_COND_EXPR only the condition may trap, but
+     they won't appear as operands in GIMPLE form, so this is just for the
+     GENERIC uses where it needs to recurse on the operands and so
+     *COND_EXPR itself doesn't trap.  */
+  if (TREE_CODE (expr) == COND_EXPR || TREE_CODE (expr) == VEC_COND_EXPR)
+    return false;
+
   code = TREE_CODE (expr);
   t = TREE_TYPE (expr);
 
@@ -2729,6 +2760,7 @@ static tree
 find_trapping_overflow (tree *tp, int *walk_subtrees, void *data)
 {
   if (EXPR_P (*tp)
+      && ANY_INTEGRAL_TYPE_P (TREE_TYPE (*tp))
       && !operation_no_trapping_overflow (TREE_TYPE (*tp), TREE_CODE (*tp)))
     return *tp;
   if (IS_TYPE_OR_DECL_P (*tp)
@@ -2755,27 +2787,9 @@ replace_trapping_overflow (tree *tp, int *walk_subtrees, void *data)
 
       if (TREE_CODE (*tp) == ABS_EXPR)
        {
-         tree op = TREE_OPERAND (*tp, 0);
-         op = save_expr (op);
-         /* save_expr skips simple arithmetics, which is undesirable
-            here, if it might trap due to flag_trapv.  We need to
-            force a SAVE_EXPR in the COND_EXPR condition, to evaluate
-            it before the comparison.  */
-         if (EXPR_P (op)
-             && TREE_CODE (op) != SAVE_EXPR
-             && walk_tree (&op, find_trapping_overflow, NULL, NULL))
-           {
-             op = build1_loc (EXPR_LOCATION (op), SAVE_EXPR, type, op);
-             TREE_SIDE_EFFECTS (op) = 1;
-           }
-         /* Change abs (op) to op < 0 ? -op : op and handle the NEGATE_EXPR
-            like other signed integer trapping operations.  */
-         tree cond = fold_build2 (LT_EXPR, boolean_type_node,
-                                  op, build_int_cst (type, 0));
-         tree neg = fold_build1 (NEGATE_EXPR, utype,
-                                 fold_convert (utype, op));
-         *tp = fold_build3 (COND_EXPR, type, cond,
-                            fold_convert (type, neg), op);
+         TREE_SET_CODE (*tp, ABSU_EXPR);
+         TREE_TYPE (*tp) = utype;
+         *tp = fold_convert (type, *tp);
        }
       else
        {
@@ -2824,8 +2838,7 @@ stmt_could_throw_1_p (gassign *stmt)
 
   if (TREE_CODE_CLASS (code) == tcc_comparison
       || TREE_CODE_CLASS (code) == tcc_unary
-      || TREE_CODE_CLASS (code) == tcc_binary
-      || code == FMA_EXPR)
+      || TREE_CODE_CLASS (code) == tcc_binary)
     {
       if (TREE_CODE_CLASS (code) == tcc_comparison)
        t = TREE_TYPE (gimple_assign_rhs1 (stmt));
@@ -2863,10 +2876,10 @@ stmt_could_throw_1_p (gassign *stmt)
 }
 
 
-/* Return true if statement STMT could throw an exception.  */
+/* Return true if statement STMT within FUN could throw an exception.  */
 
 bool
-stmt_could_throw_p (gimple *stmt)
+stmt_could_throw_p (function *fun, gimple *stmt)
 {
   if (!flag_exceptions)
     return false;
@@ -2883,7 +2896,7 @@ stmt_could_throw_p (gimple *stmt)
 
     case GIMPLE_COND:
       {
-       if (!cfun->can_throw_non_call_exceptions)
+       if (fun && !fun->can_throw_non_call_exceptions)
          return false;
        gcond *cond = as_a <gcond *> (stmt);
        tree lhs = gimple_cond_lhs (cond);
@@ -2893,13 +2906,13 @@ stmt_could_throw_p (gimple *stmt)
       }
 
     case GIMPLE_ASSIGN:
-      if (!cfun->can_throw_non_call_exceptions
+      if ((fun && !fun->can_throw_non_call_exceptions)
          || gimple_clobber_p (stmt))
         return false;
       return stmt_could_throw_1_p (as_a <gassign *> (stmt));
 
     case GIMPLE_ASM:
-      if (!cfun->can_throw_non_call_exceptions)
+      if (fun && !fun->can_throw_non_call_exceptions)
         return false;
       return gimple_asm_volatile_p (as_a <gasm *> (stmt));
 
@@ -2933,33 +2946,37 @@ tree_could_throw_p (tree t)
   return false;
 }
 
-/* Return true if STMT can throw an exception that is not caught within
-   the current function (CFUN).  */
+/* Return true if STMT can throw an exception that is not caught within its
+   function FUN.  FUN can be NULL but the function is extra conservative
+   then.  */
 
 bool
-stmt_can_throw_external (gimple *stmt)
+stmt_can_throw_external (function *fun, gimple *stmt)
 {
   int lp_nr;
 
-  if (!stmt_could_throw_p (stmt))
+  if (!stmt_could_throw_p (fun, stmt))
     return false;
+  if (!fun)
+    return true;
 
-  lp_nr = lookup_stmt_eh_lp (stmt);
+  lp_nr = lookup_stmt_eh_lp_fn (fun, stmt);
   return lp_nr == 0;
 }
 
-/* Return true if STMT can throw an exception that is caught within
-   the current function (CFUN).  */
+/* Return true if STMT can throw an exception that is caught within its
+   function FUN.  */
 
 bool
-stmt_can_throw_internal (gimple *stmt)
+stmt_can_throw_internal (function *fun, gimple *stmt)
 {
   int lp_nr;
 
-  if (!stmt_could_throw_p (stmt))
+  gcc_checking_assert (fun);
+  if (!stmt_could_throw_p (fun, stmt))
     return false;
 
-  lp_nr = lookup_stmt_eh_lp (stmt);
+  lp_nr = lookup_stmt_eh_lp_fn (fun, stmt);
   return lp_nr > 0;
 }
 
@@ -2970,7 +2987,7 @@ stmt_can_throw_internal (gimple *stmt)
 bool
 maybe_clean_eh_stmt_fn (struct function *ifun, gimple *stmt)
 {
-  if (stmt_could_throw_p (stmt))
+  if (stmt_could_throw_p (ifun, stmt))
     return false;
   return remove_stmt_from_eh_lp_fn (ifun, stmt);
 }
@@ -2995,7 +3012,7 @@ maybe_clean_or_replace_eh_stmt (gimple *old_stmt, gimple *new_stmt)
 
   if (lp_nr != 0)
     {
-      bool new_stmt_could_throw = stmt_could_throw_p (new_stmt);
+      bool new_stmt_could_throw = stmt_could_throw_p (cfun, new_stmt);
 
       if (new_stmt == old_stmt && new_stmt_could_throw)
        return false;
@@ -3025,7 +3042,7 @@ maybe_duplicate_eh_stmt_fn (struct function *new_fun, gimple *new_stmt,
 {
   int old_lp_nr, new_lp_nr;
 
-  if (!stmt_could_throw_p (new_stmt))
+  if (!stmt_could_throw_p (new_fun, new_stmt))
     return false;
 
   old_lp_nr = lookup_stmt_eh_lp_fn (old_fun, old_stmt);
@@ -3064,7 +3081,7 @@ maybe_duplicate_eh_stmt (gimple *new_stmt, gimple *old_stmt)
 {
   int lp_nr;
 
-  if (!stmt_could_throw_p (new_stmt))
+  if (!stmt_could_throw_p (cfun, new_stmt))
     return false;
 
   lp_nr = lookup_stmt_eh_lp (old_stmt);
@@ -3326,7 +3343,7 @@ lower_resx (basic_block bb, gresx *stmt,
          else
            {
              lab = *slot;
-             new_bb = label_to_block (lab);
+             new_bb = label_to_block (cfun, lab);
            }
 
          gcc_assert (EDGE_COUNT (bb->succs) == 0);
@@ -3533,10 +3550,15 @@ optimize_clobbers (basic_block bb)
 }
 
 /* Try to sink var = {v} {CLOBBER} stmts followed just by
-   internal throw to successor BB.  */
+   internal throw to successor BB.
+   SUNK, if not NULL, is an array of sequences indexed by basic-block
+   index to sink to and to pick up sinking opportunities from.
+   If FOUND_OPPORTUNITY is not NULL then do not perform the optimization
+   but set *FOUND_OPPORTUNITY to true.  */
 
 static int
-sink_clobbers (basic_block bb)
+sink_clobbers (basic_block bb,
+              gimple_seq *sunk = NULL, bool *found_opportunity = NULL)
 {
   edge e;
   edge_iterator ei;
@@ -3571,16 +3593,22 @@ sink_clobbers (basic_block bb)
        return 0;
       any_clobbers = true;
     }
-  if (!any_clobbers)
+  if (!any_clobbers && (!sunk || gimple_seq_empty_p (sunk[bb->index])))
     return 0;
 
+  /* If this was a dry run, tell it we found clobbers to sink.  */
+  if (found_opportunity)
+    {
+      *found_opportunity = true;
+      return 0;
+    }
+
   edge succe = single_succ_edge (bb);
   succbb = succe->dest;
 
   /* See if there is a virtual PHI node to take an updated virtual
      operand from.  */
   gphi *vphi = NULL;
-  tree vuse = NULL_TREE;
   for (gphi_iterator gpi = gsi_start_phis (succbb);
        !gsi_end_p (gpi); gsi_next (&gpi))
     {
@@ -3588,12 +3616,16 @@ sink_clobbers (basic_block bb)
       if (virtual_operand_p (res))
        {
          vphi = gpi.phi ();
-         vuse = res;
          break;
        }
     }
 
-  dgsi = gsi_after_labels (succbb);
+  gimple *first_sunk = NULL;
+  gimple *last_sunk = NULL;
+  if (sunk)
+    dgsi = gsi_start (sunk[succbb->index]);
+  else
+    dgsi = gsi_after_labels (succbb);
   gsi = gsi_last_bb (bb);
   for (gsi_prev (&gsi); !gsi_end_p (gsi); gsi_prev (&gsi))
     {
@@ -3624,36 +3656,46 @@ sink_clobbers (basic_block bb)
          forwarder edge we can keep virtual operands in place.  */
       gsi_remove (&gsi, false);
       gsi_insert_before (&dgsi, stmt, GSI_NEW_STMT);
-
-      /* But adjust virtual operands if we sunk across a PHI node.  */
-      if (vuse)
+      if (!first_sunk)
+       first_sunk = stmt;
+      last_sunk = stmt;
+    }
+  if (sunk && !gimple_seq_empty_p (sunk[bb->index]))
+    {
+      if (!first_sunk)
+       first_sunk = gsi_stmt (gsi_last (sunk[bb->index]));
+      last_sunk = gsi_stmt (gsi_start (sunk[bb->index]));
+      gsi_insert_seq_before_without_update (&dgsi,
+                                           sunk[bb->index], GSI_NEW_STMT);
+      sunk[bb->index] = NULL;
+    }
+  if (first_sunk)
+    {
+      /* Adjust virtual operands if we sunk across a virtual PHI.  */
+      if (vphi)
        {
-         gimple *use_stmt;
          imm_use_iterator iter;
          use_operand_p use_p;
-         FOR_EACH_IMM_USE_STMT (use_stmt, iter, vuse)
+         gimple *use_stmt;
+         tree phi_def = gimple_phi_result (vphi);
+         FOR_EACH_IMM_USE_STMT (use_stmt, iter, phi_def)
            FOR_EACH_IMM_USE_ON_STMT (use_p, iter)
-             SET_USE (use_p, gimple_vdef (stmt));
-         if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (vuse))
+              SET_USE (use_p, gimple_vdef (first_sunk));
+         if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (phi_def))
            {
-             SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_vdef (stmt)) = 1;
-             SSA_NAME_OCCURS_IN_ABNORMAL_PHI (vuse) = 0;
+             SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_vdef (first_sunk)) = 1;
+             SSA_NAME_OCCURS_IN_ABNORMAL_PHI (phi_def) = 0;
            }
-         /* Adjust the incoming virtual operand.  */
-         SET_USE (PHI_ARG_DEF_PTR_FROM_EDGE (vphi, succe), gimple_vuse (stmt));
-         SET_USE (gimple_vuse_op (stmt), vuse);
+         SET_USE (PHI_ARG_DEF_PTR_FROM_EDGE (vphi, succe),
+                  gimple_vuse (last_sunk));
+         SET_USE (gimple_vuse_op (last_sunk), phi_def);
        }
       /* If there isn't a single predecessor but no virtual PHI node
          arrange for virtual operands to be renamed.  */
-      else if (gimple_vuse_op (stmt) != NULL_USE_OPERAND_P
-              && !single_pred_p (succbb))
+      else if (!single_pred_p (succbb)
+              && TREE_CODE (gimple_vuse (last_sunk)) == SSA_NAME)
        {
-         /* In this case there will be no use of the VDEF of this stmt. 
-            ???  Unless this is a secondary opportunity and we have not
-            removed unreachable blocks yet, so we cannot assert this.  
-            Which also means we will end up renaming too many times.  */
-         SET_USE (gimple_vuse_op (stmt), gimple_vop (cfun));
-         mark_virtual_operands_for_renaming (cfun);
+         mark_virtual_operand_for_renaming (gimple_vuse (last_sunk));
          todo |= TODO_update_ssa_only_virtuals;
        }
     }
@@ -3730,7 +3772,7 @@ lower_eh_dispatch (basic_block src, geh_dispatch *stmt)
            while (tp_node);
            if (! have_label)
              {
-               remove_edge (find_edge (src, label_to_block (lab)));
+               remove_edge (find_edge (src, label_to_block (cfun, lab)));
                redirected = true;
              }
          }
@@ -3763,6 +3805,7 @@ lower_eh_dispatch (basic_block src, geh_dispatch *stmt)
            filter = create_tmp_var (TREE_TYPE (TREE_TYPE (fn)));
            filter = make_ssa_name (filter, x);
            gimple_call_set_lhs (x, filter);
+           gimple_set_location (x, gimple_location (stmt));
            gsi_insert_before (&gsi, x, GSI_SAME_STMT);
 
            /* Turn the default label into a default case.  */
@@ -3770,6 +3813,7 @@ lower_eh_dispatch (basic_block src, geh_dispatch *stmt)
            sort_case_labels (labels);
 
            x = gimple_build_switch (filter, default_label, labels);
+           gimple_set_location (x, gimple_location (stmt));
            gsi_insert_before (&gsi, x, GSI_SAME_STMT);
          }
       }
@@ -3786,6 +3830,7 @@ lower_eh_dispatch (basic_block src, geh_dispatch *stmt)
        filter = create_tmp_var (TREE_TYPE (TREE_TYPE (fn)));
        filter = make_ssa_name (filter, x);
        gimple_call_set_lhs (x, filter);
+       gimple_set_location (x, gimple_location (stmt));
        gsi_insert_before (&gsi, x, GSI_SAME_STMT);
 
        r->u.allowed.label = NULL;
@@ -3843,6 +3888,7 @@ pass_lower_eh_dispatch::execute (function *fun)
   basic_block bb;
   int flags = 0;
   bool redirected = false;
+  bool any_resx_to_process = false;
 
   assign_filter_values ();
 
@@ -3859,18 +3905,46 @@ pass_lower_eh_dispatch::execute (function *fun)
        }
       else if (gimple_code (last) == GIMPLE_RESX)
        {
-         if (stmt_can_throw_external (last))
+         if (stmt_can_throw_external (fun, last))
            optimize_clobbers (bb);
-         else
-           flags |= sink_clobbers (bb);
+         else if (!any_resx_to_process)
+           sink_clobbers (bb, NULL, &any_resx_to_process);
        }
     }
-
   if (redirected)
     {
       free_dominance_info (CDI_DOMINATORS);
       delete_unreachable_blocks ();
     }
+
+  if (any_resx_to_process)
+    {
+      /* Make sure to catch all secondary sinking opportunities by processing
+        blocks in RPO order and after all CFG modifications from lowering
+        and unreachable block removal.  */
+      int *rpo = XNEWVEC  (int, n_basic_blocks_for_fn (fun));
+      int rpo_n = pre_and_rev_post_order_compute_fn (fun, NULL, rpo, false);
+      gimple_seq *sunk = XCNEWVEC (gimple_seq, last_basic_block_for_fn (fun));
+      for (int i = 0; i < rpo_n; ++i)
+       {
+         bb = BASIC_BLOCK_FOR_FN (fun, rpo[i]);
+         gimple *last = last_stmt (bb);
+         if (last
+             && gimple_code (last) == GIMPLE_RESX
+             && !stmt_can_throw_external (fun, last))
+           flags |= sink_clobbers (bb, sunk);
+         /* If there were any clobbers sunk into this BB, insert them now.  */
+         if (!gimple_seq_empty_p (sunk[bb->index]))
+           {
+             gimple_stmt_iterator gsi = gsi_after_labels (bb);
+             gsi_insert_seq_before (&gsi, sunk[bb->index], GSI_NEW_STMT);
+             sunk[bb->index] = NULL;
+           }
+       }
+      free (rpo);
+      free (sunk);
+    }
+
   return flags;
 }
 
@@ -4039,15 +4113,14 @@ maybe_remove_unreachable_handlers (void)
 
   if (cfun->eh == NULL)
     return;
-           
+
   FOR_EACH_VEC_SAFE_ELT (cfun->eh->lp_array, i, lp)
-    if (lp && lp->post_landing_pad)
+    if (lp
+       && (lp->post_landing_pad == NULL_TREE
+           || label_to_block (cfun, lp->post_landing_pad) == NULL))
       {
-       if (label_to_block (lp->post_landing_pad) == NULL)
-         {
-           remove_unreachable_handlers ();
-           return;
-         }
+       remove_unreachable_handlers ();
+       return;
       }
 }
 
@@ -4107,7 +4180,7 @@ remove_unreachable_handlers_no_lp (void)
 static bool
 unsplit_eh (eh_landing_pad lp)
 {
-  basic_block bb = label_to_block (lp->post_landing_pad);
+  basic_block bb = label_to_block (cfun, lp->post_landing_pad);
   gimple_stmt_iterator gsi;
   edge e_in, e_out;
 
@@ -4210,6 +4283,27 @@ unsplit_all_eh (void)
   return changed;
 }
 
+/* Wrapper around unsplit_all_eh that makes it usable everywhere.  */
+
+void
+unsplit_eh_edges (void)
+{
+  bool changed;
+
+  /* unsplit_all_eh can die looking up unreachable landing pads.  */
+  maybe_remove_unreachable_handlers ();
+
+  changed = unsplit_all_eh ();
+
+  /* If EH edges have been unsplit, delete unreachable forwarder blocks.  */
+  if (changed)
+    {
+      free_dominance_info (CDI_DOMINATORS);
+      free_dominance_info (CDI_POST_DOMINATORS);
+      delete_unreachable_blocks ();
+    }
+}
+
 /* A subroutine of cleanup_empty_eh.  Redirect all EH edges incoming
    to OLD_BB to NEW_BB; return true on success, false on failure.
 
@@ -4238,9 +4332,10 @@ cleanup_empty_eh_merge_phis (basic_block new_bb, basic_block old_bb,
        |  | EH
        <..>
      which CFG verification would choke on.  See PR45172 and PR51089.  */
-  FOR_EACH_EDGE (e, ei, old_bb->preds)
-    if (find_edge (e->src, new_bb))
-      return false;
+  if (!single_pred_p (new_bb))
+    FOR_EACH_EDGE (e, ei, old_bb->preds)
+      if (find_edge (e->src, new_bb))
+       return false;
 
   FOR_EACH_EDGE (e, ei, old_bb->preds)
     redirect_edge_var_map_clear (e);
@@ -4472,7 +4567,7 @@ infinite_empty_loop_p (edge e_first)
 static bool
 cleanup_empty_eh (eh_landing_pad lp)
 {
-  basic_block bb = label_to_block (lp->post_landing_pad);
+  basic_block bb = label_to_block (cfun, lp->post_landing_pad);
   gimple_stmt_iterator gsi;
   gimple *resx;
   eh_region new_region;
@@ -4499,7 +4594,7 @@ cleanup_empty_eh (eh_landing_pad lp)
   resx = gsi_stmt (gsi);
   if (resx && is_gimple_resx (resx))
     {
-      if (stmt_can_throw_external (resx))
+      if (stmt_can_throw_external (cfun, resx))
        optimize_clobbers (bb);
       else if (sink_clobbers (bb))
        ret = true;
@@ -4629,9 +4724,15 @@ cleanup_all_empty_eh (void)
   eh_landing_pad lp;
   int i;
 
-  for (i = 1; vec_safe_iterate (cfun->eh->lp_array, i, &lp); ++i)
-    if (lp)
-      changed |= cleanup_empty_eh (lp);
+  /* Ideally we'd walk the region tree and process LPs inner to outer
+     to avoid quadraticness in EH redirection.  Walking the LP array
+     in reverse seems to be an approximation of that.  */
+  for (i = vec_safe_length (cfun->eh->lp_array) - 1; i >= 1; --i)
+    {
+      lp = (*cfun->eh->lp_array)[i];
+      if (lp)
+       changed |= cleanup_empty_eh (lp);
+    }
 
   return changed;
 }
@@ -4739,6 +4840,14 @@ make_pass_cleanup_eh (gcc::context *ctxt)
   return new pass_cleanup_eh (ctxt);
 }
 \f
+/* Disable warnings about missing quoting in GCC diagnostics for
+   the verification errors.  Their format strings don't follow GCC
+   diagnostic conventions but are only used for debugging.  */
+#if __GNUC__ >= 10
+#  pragma GCC diagnostic push
+#  pragma GCC diagnostic ignored "-Wformat-diag"
+#endif
+
 /* Verify that BB containing STMT as the last statement, has precisely the
    edge that make_eh_edges would create.  */
 
@@ -4774,13 +4883,13 @@ verify_eh_edges (gimple *stmt)
     {
       if (eh_edge)
        {
-         error ("BB %i can not throw but has an EH edge", bb->index);
+         error ("BB %i cannot throw but has an EH edge", bb->index);
          return true;
        }
       return false;
     }
 
-  if (!stmt_could_throw_p (stmt))
+  if (!stmt_could_throw_p (cfun, stmt))
     {
       error ("BB %i last statement has incorrectly set lp", bb->index);
       return true;
@@ -4792,7 +4901,7 @@ verify_eh_edges (gimple *stmt)
       return true;
     }
 
-  if (eh_edge->dest != label_to_block (lp->post_landing_pad))
+  if (eh_edge->dest != label_to_block (cfun, lp->post_landing_pad))
     {
       error ("Incorrect EH edge %i->%i", bb->index, eh_edge->dest->index);
       return true;
@@ -4824,7 +4933,7 @@ verify_eh_dispatch_edge (geh_dispatch *stmt)
     case ERT_TRY:
       for (c = r->u.eh_try.first_catch; c ; c = c->next_catch)
        {
-         dst = label_to_block (c->label);
+         dst = label_to_block (cfun, c->label);
          e = find_edge (src, dst);
          if (e == NULL)
            {
@@ -4843,7 +4952,7 @@ verify_eh_dispatch_edge (geh_dispatch *stmt)
       break;
 
     case ERT_ALLOWED_EXCEPTIONS:
-      dst = label_to_block (r->u.allowed.label);
+      dst = label_to_block (cfun, r->u.allowed.label);
       e = find_edge (src, dst);
       if (e == NULL)
        {
@@ -4885,3 +4994,7 @@ verify_eh_dispatch_edge (geh_dispatch *stmt)
 
   return false;
 }
+
+#if __GNUC__ >= 10
+#  pragma GCC diagnostic pop
+#endif