vec: add exact argument for various grow functions.
[gcc.git] / gcc / cfgrtl.c
index a2ad075db85f63c0dfca71b4f42b7aaf88f6e2f2..03fa688fed681dec2f25d72e721189834350a839 100644 (file)
@@ -1,5 +1,5 @@
 /* Control flow graph manipulation code for GNU compiler.
-   Copyright (C) 1987-2017 Free Software Foundation, Inc.
+   Copyright (C) 1987-2020 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -62,6 +62,12 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-pass.h"
 #include "print-rtl.h"
 
+/* Disable warnings about missing quoting in GCC diagnostics.  */
+#if __GNUC__ >= 10
+#  pragma GCC diagnostic push
+#  pragma GCC diagnostic ignored "-Wformat-diag"
+#endif
+
 /* Holds the interesting leading and trailing notes for the function.
    Only applicable if the CFG is in cfglayout mode.  */
 static GTY(()) rtx_insn *cfg_layout_function_footer;
@@ -224,10 +230,20 @@ delete_insn_and_edges (rtx_insn *insn)
 {
   bool purge = false;
 
-  if (INSN_P (insn)
-      && BLOCK_FOR_INSN (insn)
-      && BB_END (BLOCK_FOR_INSN (insn)) == insn)
-    purge = true;
+  if (INSN_P (insn) && BLOCK_FOR_INSN (insn))
+    {
+      basic_block bb = BLOCK_FOR_INSN (insn);
+      if (BB_END (bb) == insn)
+       purge = true;
+      else if (DEBUG_INSN_P (BB_END (bb)))
+       for (rtx_insn *dinsn = NEXT_INSN (insn);
+            DEBUG_INSN_P (dinsn); dinsn = NEXT_INSN (dinsn))
+         if (BB_END (bb) == dinsn)
+           {
+             purge = true;
+             break;
+           }
+    }
   delete_insn (insn);
   if (purge)
     return purge_dead_edges (BLOCK_FOR_INSN (insn));
@@ -362,7 +378,7 @@ rtl_create_basic_block (void *headp, void *endp, basic_block after)
       size_t new_size =
        (last_basic_block_for_fn (cfun)
         + (last_basic_block_for_fn (cfun) + 3) / 4);
-      vec_safe_grow_cleared (basic_block_info_for_fn (cfun), new_size);
+      vec_safe_grow_cleared (basic_block_info_for_fn (cfun), new_size, true);
     }
 
   n_basic_blocks_for_fn (cfun)++;
@@ -543,7 +559,7 @@ update_bb_for_insn (basic_block bb)
 }
 
 \f
-/* Like active_insn_p, except keep the return value clobber around
+/* Like active_insn_p, except keep the return value use or clobber around
    even after reload.  */
 
 static bool
@@ -556,8 +572,12 @@ flow_active_insn_p (const rtx_insn *insn)
      programs that fail to return a value.  Its effect is to
      keep the return value from being live across the entire
      function.  If we allow it to be skipped, we introduce the
-     possibility for register lifetime confusion.  */
-  if (GET_CODE (PATTERN (insn)) == CLOBBER
+     possibility for register lifetime confusion.
+     Similarly, keep a USE of the function return value, otherwise
+     the USE is dropped and we could fail to thread jump if USE
+     appears on some paths and not on others, see PR90257.  */
+  if ((GET_CODE (PATTERN (insn)) == CLOBBER
+       || GET_CODE (PATTERN (insn)) == USE)
       && REG_P (XEXP (PATTERN (insn), 0))
       && REG_FUNCTION_VALUE_P (XEXP (PATTERN (insn), 0)))
     return true;
@@ -813,10 +833,14 @@ emit_nop_for_unique_locus_between (basic_block a, basic_block b)
 static void
 rtl_merge_blocks (basic_block a, basic_block b)
 {
+  /* If B is a forwarder block whose outgoing edge has no location, we'll
+     propagate the locus of the edge between A and B onto it.  */
+  const bool forward_edge_locus
+    = (b->flags & BB_FORWARDER_BLOCK) != 0
+      && LOCATION_LOCUS (EDGE_SUCC (b, 0)->goto_locus) == UNKNOWN_LOCATION;
   rtx_insn *b_head = BB_HEAD (b), *b_end = BB_END (b), *a_end = BB_END (a);
   rtx_insn *del_first = NULL, *del_last = NULL;
   rtx_insn *b_debug_start = b_end, *b_debug_end = b_end;
-  bool forwarder_p = (b->flags & BB_FORWARDER_BLOCK) != 0;
   int b_empty = 0;
 
   if (dump_file)
@@ -887,9 +911,11 @@ rtl_merge_blocks (basic_block a, basic_block b)
   BB_HEAD (b) = b_empty ? NULL : b_head;
   delete_insn_chain (del_first, del_last, true);
 
-  /* When not optimizing and the edge is the only place in RTL which holds
-     some unique locus, emit a nop with that locus in between.  */
-  if (!optimize)
+  /* If not optimizing, preserve the locus of the single edge between
+     blocks A and B if necessary by emitting a nop.  */
+  if (!optimize
+      && !forward_edge_locus
+      && !DECL_IGNORED_P (current_function_decl))
     {
       emit_nop_for_unique_locus_between (a, b);
       a_end = BB_END (a);
@@ -918,9 +944,7 @@ rtl_merge_blocks (basic_block a, basic_block b)
 
   df_bb_delete (b->index);
 
-  /* If B was a forwarder block, propagate the locus on the edge.  */
-  if (forwarder_p
-      && LOCATION_LOCUS (EDGE_SUCC (b, 0)->goto_locus) == UNKNOWN_LOCATION)
+  if (forward_edge_locus)
     EDGE_SUCC (b, 0)->goto_locus = EDGE_SUCC (a, 0)->goto_locus;
 
   if (dump_file)
@@ -984,6 +1008,31 @@ block_label (basic_block block)
   return as_a <rtx_code_label *> (BB_HEAD (block));
 }
 
+/* Remove all barriers from BB_FOOTER of a BB.  */
+
+static void
+remove_barriers_from_footer (basic_block bb)
+{
+  rtx_insn *insn = BB_FOOTER (bb);
+
+  /* Remove barriers but keep jumptables.  */
+  while (insn)
+    {
+      if (BARRIER_P (insn))
+       {
+         if (PREV_INSN (insn))
+           SET_NEXT_INSN (PREV_INSN (insn)) = NEXT_INSN (insn);
+         else
+           BB_FOOTER (bb) = NEXT_INSN (insn);
+         if (NEXT_INSN (insn))
+           SET_PREV_INSN (NEXT_INSN (insn)) = PREV_INSN (insn);
+       }
+      if (LABEL_P (insn))
+       return;
+      insn = NEXT_INSN (insn);
+    }
+}
+
 /* Attempt to perform edge redirection by replacing possibly complex jump
    instruction by unconditional jump or removing jump completely.  This can
    apply only if all edges now point to the same block.  The parameters and
@@ -1047,26 +1096,8 @@ try_redirect_by_replacing_jump (edge e, basic_block target, bool in_cfglayout)
       /* Selectively unlink whole insn chain.  */
       if (in_cfglayout)
        {
-         rtx_insn *insn = BB_FOOTER (src);
-
          delete_insn_chain (kill_from, BB_END (src), false);
-
-         /* Remove barriers but keep jumptables.  */
-         while (insn)
-           {
-             if (BARRIER_P (insn))
-               {
-                 if (PREV_INSN (insn))
-                   SET_NEXT_INSN (PREV_INSN (insn)) = NEXT_INSN (insn);
-                 else
-                   BB_FOOTER (src) = NEXT_INSN (insn);
-                 if (NEXT_INSN (insn))
-                   SET_PREV_INSN (NEXT_INSN (insn)) = PREV_INSN (insn);
-               }
-             if (LABEL_P (insn))
-               break;
-             insn = NEXT_INSN (insn);
-           }
+         remove_barriers_from_footer (src);
        }
       else
        delete_insn_chain (kill_from, PREV_INSN (BB_HEAD (target)),
@@ -1117,7 +1148,7 @@ try_redirect_by_replacing_jump (edge e, basic_block target, bool in_cfglayout)
       if (tablejump_p (insn, &label, &table))
        delete_insn_chain (label, table, false);
 
-      barrier = next_nonnote_insn (BB_END (src));
+      barrier = next_nonnote_nondebug_insn (BB_END (src));
       if (!barrier || !BARRIER_P (barrier))
        emit_barrier_after (BB_END (src));
       else
@@ -1193,10 +1224,7 @@ patch_jump_insn (rtx_insn *insn, rtx_insn *old_label, basic_block new_bb)
          }
 
       /* Handle casesi dispatch insns.  */
-      if ((tmp = single_set (insn)) != NULL
-         && SET_DEST (tmp) == pc_rtx
-         && GET_CODE (SET_SRC (tmp)) == IF_THEN_ELSE
-         && GET_CODE (XEXP (SET_SRC (tmp), 2)) == LABEL_REF
+      if ((tmp = tablejump_casesi_pattern (insn)) != NULL_RTX
          && label_ref_label (XEXP (SET_SRC (tmp), 2)) == old_label)
        {
          XEXP (SET_SRC (tmp), 2) = gen_rtx_LABEL_REF (Pmode,
@@ -1264,11 +1292,13 @@ patch_jump_insn (rtx_insn *insn, rtx_insn *old_label, basic_block new_bb)
 
          /* If the substitution doesn't succeed, die.  This can happen
             if the back end emitted unrecognizable instructions or if
-            target is exit block on some arches.  */
+            target is exit block on some arches.  Or for crossing
+            jumps.  */
          if (!redirect_jump (as_a <rtx_jump_insn *> (insn),
                              block_label (new_bb), 0))
            {
-             gcc_assert (new_bb == EXIT_BLOCK_PTR_FOR_FN (cfun));
+             gcc_assert (new_bb == EXIT_BLOCK_PTR_FOR_FN (cfun)
+                         || CROSSING_JUMP_P (insn));
              return false;
            }
        }
@@ -1534,6 +1564,9 @@ force_nonfallthru_and_redirect (edge e, basic_block target, rtx jump_label)
                                               ENTRY_BLOCK_PTR_FOR_FN (cfun));
          bb->count = ENTRY_BLOCK_PTR_FOR_FN (cfun)->count;
 
+         /* Make sure new block ends up in correct hot/cold section.  */
+         BB_COPY_PARTITION (bb, e->dest);
+
          /* Change the existing edge's source to be the new block, and add
             a new edge from the entry block to the new block.  */
          e->src = bb;
@@ -1625,11 +1658,6 @@ force_nonfallthru_and_redirect (edge e, basic_block target, rtx jump_label)
       else
        new_head = BB_END (e->src);
       new_head = NEXT_INSN (new_head);
-      /* Make sure we don't split a call and its corresponding
-        CALL_ARG_LOCATION note.  */
-      if (new_head && NOTE_P (new_head)
-         && NOTE_KIND (new_head) == NOTE_INSN_CALL_ARG_LOCATION)
-       new_head = NEXT_INSN (new_head);
 
       jump_block = create_basic_block (new_head, NULL, e->src);
       jump_block->count = count;
@@ -1750,7 +1778,7 @@ rtl_tidy_fallthru_edge (edge e)
      the head of block C and assert that we really do fall through.  */
 
   for (q = NEXT_INSN (BB_END (b)); q != BB_HEAD (c); q = NEXT_INSN (q))
-    if (INSN_P (q))
+    if (NONDEBUG_INSN_P (q))
       return;
 
   /* Remove what will soon cease being the jump insn from the source block.
@@ -2084,7 +2112,8 @@ commit_edge_insertions (void)
      which will be done by fixup_partitions.  */
   fixup_partitions ();
 
-  checking_verify_flow_info ();
+  if (!currently_expanding_to_rtl)
+    checking_verify_flow_info ();
 
   FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR_FOR_FN (cfun),
                  EXIT_BLOCK_PTR_FOR_FN (cfun), next_bb)
@@ -2094,7 +2123,11 @@ commit_edge_insertions (void)
 
       FOR_EACH_EDGE (e, ei, bb->succs)
        if (e->insns.r)
-         commit_one_edge_insertion (e);
+         {
+           if (currently_expanding_to_rtl)
+             rebuild_jump_labels_chain (e->insns.r);
+           commit_one_edge_insertion (e);
+         }
     }
 }
 \f
@@ -2172,7 +2205,7 @@ print_rtl_with_bb (FILE *outf, const rtx_insn *rtx_first, dump_flags_t flags)
       if (df)
        df_dump_start (outf);
 
-      if (flags & TDF_BLOCKS)
+      if (cfun->curr_properties & PROP_cfg)
        {
          FOR_EACH_BB_REVERSE_FN (bb, cfun)
            {
@@ -2180,21 +2213,24 @@ print_rtl_with_bb (FILE *outf, const rtx_insn *rtx_first, dump_flags_t flags)
 
              start[INSN_UID (BB_HEAD (bb))] = bb;
              end[INSN_UID (BB_END (bb))] = bb;
-             for (x = BB_HEAD (bb); x != NULL_RTX; x = NEXT_INSN (x))
+             if (flags & TDF_BLOCKS)
                {
-                 enum bb_state state = IN_MULTIPLE_BB;
+                 for (x = BB_HEAD (bb); x != NULL_RTX; x = NEXT_INSN (x))
+                   {
+                     enum bb_state state = IN_MULTIPLE_BB;
 
-                 if (in_bb_p[INSN_UID (x)] == NOT_IN_BB)
-                   state = IN_ONE_BB;
-                 in_bb_p[INSN_UID (x)] = state;
+                     if (in_bb_p[INSN_UID (x)] == NOT_IN_BB)
+                       state = IN_ONE_BB;
+                     in_bb_p[INSN_UID (x)] = state;
 
-                 if (x == BB_END (bb))
-                   break;
+                     if (x == BB_END (bb))
+                       break;
+                   }
                }
            }
        }
 
-      for (tmp_rtx = rtx_first; NULL != tmp_rtx; tmp_rtx = NEXT_INSN (tmp_rtx))
+      for (tmp_rtx = rtx_first; tmp_rtx != NULL; tmp_rtx = NEXT_INSN (tmp_rtx))
        {
          if (flags & TDF_BLOCKS)
            {
@@ -2223,16 +2259,36 @@ print_rtl_with_bb (FILE *outf, const rtx_insn *rtx_first, dump_flags_t flags)
          if (flags & TDF_DETAILS)
            df_dump_insn_bottom (tmp_rtx, outf);
 
-         if (flags & TDF_BLOCKS)
+         bb = end[INSN_UID (tmp_rtx)];
+         if (bb != NULL)
            {
-             bb = end[INSN_UID (tmp_rtx)];
-             if (bb != NULL)
+             if (flags & TDF_BLOCKS)
                {
                  dump_bb_info (outf, bb, 0, dump_flags, false, true);
                  if (df && (flags & TDF_DETAILS))
                    df_dump_bottom (bb, outf);
                  putc ('\n', outf);
                }
+             /* Emit a hint if the fallthrough target of current basic block
+                isn't the one placed right next.  */
+             else if (EDGE_COUNT (bb->succs) > 0)
+               {
+                 gcc_assert (BB_END (bb) == tmp_rtx);
+                 const rtx_insn *ninsn = NEXT_INSN (tmp_rtx);
+                 /* Bypass intervening deleted-insn notes and debug insns.  */
+                 while (ninsn
+                        && !NONDEBUG_INSN_P (ninsn)
+                        && !start[INSN_UID (ninsn)])
+                   ninsn = NEXT_INSN (ninsn);
+                 edge e = find_fallthru_edge (bb->succs);
+                 if (e && ninsn)
+                   {
+                     basic_block dest = e->dest;
+                     if (start[INSN_UID (ninsn)] != dest)
+                       fprintf (outf, "%s      ; pc falls through to BB %d\n",
+                                print_rtx_head, dest->index);
+                   }
+               }
            }
        }
 
@@ -2285,11 +2341,11 @@ get_last_bb_insn (basic_block bb)
     end = table;
 
   /* Include any barriers that may follow the basic block.  */
-  tmp = next_nonnote_insn_bb (end);
+  tmp = next_nonnote_nondebug_insn_bb (end);
   while (tmp && BARRIER_P (tmp))
     {
       end = tmp;
-      tmp = next_nonnote_insn_bb (end);
+      tmp = next_nonnote_nondebug_insn_bb (end);
     }
 
   return end;
@@ -2329,7 +2385,6 @@ static vec<basic_block>
 find_partition_fixes (bool flag_only)
 {
   basic_block bb;
-  vec<basic_block> bbs_in_cold_partition = vNULL;
   vec<basic_block> bbs_to_fix = vNULL;
   hash_set<basic_block> set;
 
@@ -2348,7 +2403,6 @@ find_partition_fixes (bool flag_only)
        else
          BB_SET_PARTITION (bb, BB_COLD_PARTITION);
        bbs_to_fix.safe_push (bb);
-       bbs_in_cold_partition.safe_push (bb);
       }
 
   return bbs_to_fix;
@@ -2542,15 +2596,15 @@ rtl_verify_edges (void)
            n_abnormal++;
        }
 
-        if (!has_crossing_edge
-           && JUMP_P (BB_END (bb))
-           && CROSSING_JUMP_P (BB_END (bb)))
-          {
-           print_rtl_with_bb (stderr, get_insns (), TDF_BLOCKS | TDF_DETAILS);
-            error ("Region crossing jump across same section in bb %i",
-                   bb->index);
-            err = 1;
-          }
+      if (!has_crossing_edge
+         && JUMP_P (BB_END (bb))
+         && CROSSING_JUMP_P (BB_END (bb)))
+       {
+         print_rtl_with_bb (stderr, get_insns (), TDF_BLOCKS | TDF_DETAILS);
+         error ("Region crossing jump across same section in bb %i",
+                bb->index);
+         err = 1;
+       }
 
       if (n_eh && !find_reg_note (BB_END (bb), REG_EH_REGION, NULL_RTX))
        {
@@ -2608,11 +2662,25 @@ rtl_verify_edges (void)
          error ("abnormal edges for no purpose in bb %i", bb->index);
          err = 1;
        }
+
+      int has_eh = -1;
+      FOR_EACH_EDGE (e, ei, bb->preds)
+       {
+         if (has_eh == -1)
+           has_eh = (e->flags & EDGE_EH);
+         if ((e->flags & EDGE_EH) == has_eh)
+           continue;
+         error ("EH incoming edge mixed with non-EH incoming edges "
+                "in bb %i", bb->index);
+         err = 1;
+         break;
+       }
     }
 
   /* If there are partitions, do a sanity check on them: A basic block in
      a cold partition cannot dominate a basic block in a hot partition.  */
-  if (crtl->has_bb_partition && !err)
+  if (crtl->has_bb_partition && !err
+      && current_ir_type () == IR_RTL_CFGLAYOUT)
     {
       vec<basic_block> bbs_to_fix = find_partition_fixes (true);
       err = !bbs_to_fix.is_empty ();
@@ -2905,7 +2973,7 @@ rtl_verify_fallthru (void)
          else
            for (insn = NEXT_INSN (BB_END (e->src)); insn != BB_HEAD (e->dest);
                 insn = NEXT_INSN (insn))
-             if (BARRIER_P (insn) || INSN_P (insn))
+             if (BARRIER_P (insn) || NONDEBUG_INSN_P (insn))
                {
                  error ("verify_flow_info: Incorrect fallthru %i->%i",
                         e->src->index, e->dest->index);
@@ -2927,13 +2995,12 @@ rtl_verify_bb_layout (void)
 {
   basic_block bb;
   int err = 0;
-  rtx_insn *x;
+  rtx_insn *x, *y;
   int num_bb_notes;
   rtx_insn * const rtx_first = get_insns ();
   basic_block last_bb_seen = ENTRY_BLOCK_PTR_FOR_FN (cfun), curr_bb = NULL;
 
   num_bb_notes = 0;
-  last_bb_seen = ENTRY_BLOCK_PTR_FOR_FN (cfun);
 
   for (x = rtx_first; x; x = NEXT_INSN (x))
     {
@@ -2972,7 +3039,8 @@ rtl_verify_bb_layout (void)
 
       if (JUMP_P (x)
          && returnjump_p (x) && ! condjump_p (x)
-         && ! (next_nonnote_insn (x) && BARRIER_P (next_nonnote_insn (x))))
+         && ! ((y = next_nonnote_nondebug_insn (x))
+               && BARRIER_P (y)))
            fatal_insn ("return not followed by barrier", x);
 
       if (curr_bb && x == BB_END (curr_bb))
@@ -3032,7 +3100,7 @@ purge_dead_edges (basic_block bb)
   bool found;
   edge_iterator ei;
 
-  if (DEBUG_INSN_P (insn) && insn != BB_HEAD (bb))
+  if ((DEBUG_INSN_P (insn) || NOTE_P (insn)) && insn != BB_HEAD (bb))
     do
       insn = PREV_INSN (insn);
     while ((DEBUG_INSN_P (insn) || NOTE_P (insn)) && insn != BB_HEAD (bb));
@@ -3315,8 +3383,15 @@ fixup_abnormal_edges (void)
                         If it's placed after a trapping call (i.e. that
                         call is the last insn anyway), we have no fallthru
                         edge.  Simply delete this use and don't try to insert
-                        on the non-existent edge.  */
-                     if (GET_CODE (PATTERN (insn)) != USE)
+                        on the non-existent edge.
+                        Similarly, sometimes a call that can throw is
+                        followed in the source with __builtin_unreachable (),
+                        meaning that there is UB if the call returns rather
+                        than throws.  If there weren't any instructions
+                        following such calls before, supposedly even the ones
+                        we've deleted aren't significant and can be
+                        removed.  */
+                     if (e)
                        {
                          /* We're not deleting it, we're moving it.  */
                          insn->set_undeleted ();
@@ -3633,13 +3708,13 @@ relink_block_chain (bool stay_in_cfglayout_mode)
        {
          fprintf (dump_file, " %i ", index);
          if (get_bb_original (bb))
-           fprintf (dump_file, "duplicate of %i ",
+           fprintf (dump_file, "duplicate of %i\n",
                     get_bb_original (bb)->index);
          else if (forwarder_block_p (bb)
                   && !LABEL_P (BB_HEAD (bb)))
-           fprintf (dump_file, "compensation ");
+           fprintf (dump_file, "compensation\n");
          else
-           fprintf (dump_file, "bb %i ", bb->index);
+           fprintf (dump_file, "bb %i\n", bb->index);
        }
     }
 
@@ -3903,9 +3978,9 @@ fixup_reorder_chain (void)
        force_nonfallthru (e);
     }
 
-  /* Ensure goto_locus from edges has some instructions with that locus
-     in RTL.  */
-  if (!optimize)
+  /* Ensure goto_locus from edges has some instructions with that locus in RTL
+     when not optimizing.  */
+  if (!optimize && !DECL_IGNORED_P (current_function_decl))
     FOR_EACH_BB_FN (bb, cfun)
       {
         edge e;
@@ -4141,7 +4216,8 @@ duplicate_insn_chain (rtx_insn *from, rtx_insn *to)
        {
        case DEBUG_INSN:
          /* Don't duplicate label debug insns.  */
-         if (TREE_CODE (INSN_VAR_LOCATION_DECL (insn)) == LABEL_DECL)
+         if (DEBUG_BIND_INSN_P (insn)
+             && TREE_CODE (INSN_VAR_LOCATION_DECL (insn)) == LABEL_DECL)
            break;
          /* FALLTHRU */
        case INSN:
@@ -4216,7 +4292,7 @@ duplicate_insn_chain (rtx_insn *from, rtx_insn *to)
 /* Create a duplicate of the basic block BB.  */
 
 static basic_block
-cfg_layout_duplicate_bb (basic_block bb)
+cfg_layout_duplicate_bb (basic_block bb, copy_bb_data *)
 {
   rtx_insn *insn;
   basic_block new_bb;
@@ -4317,7 +4393,6 @@ break_superblocks (void)
 void
 cfg_layout_finalize (void)
 {
-  checking_verify_flow_info ();
   free_dominance_info (CDI_DOMINATORS);
   force_one_exit_fallthru ();
   rtl_register_cfg_hooks ();
@@ -4361,6 +4436,19 @@ cfg_layout_redirect_edge_and_branch (edge e, basic_block dest)
   if (e->dest == dest)
     return e;
 
+  if (e->flags & EDGE_CROSSING
+      && BB_PARTITION (e->src) == BB_PARTITION (dest)
+      && simplejump_p (BB_END (src)))
+    {
+      if (dump_file)
+       fprintf (dump_file,
+                "Removing crossing jump while redirecting edge form %i to %i\n",
+                e->src->index, dest->index);
+      delete_insn (BB_END (src));
+      remove_barriers_from_footer (src);
+      e->flags |= EDGE_FALLTHRU;
+    }
+
   if (e->src != ENTRY_BLOCK_PTR_FOR_FN (cfun)
       && (ret = try_redirect_by_replacing_jump (e, dest, true)))
     {
@@ -4424,8 +4512,12 @@ cfg_layout_redirect_edge_and_branch (edge e, basic_block dest)
   else
     ret = redirect_branch_edge (e, dest);
 
+  if (!ret)
+    return NULL;
+
+  fixup_partition_crossing (ret);
   /* We don't want simplejumps in the insn stream during cfglayout.  */
-  gcc_assert (!simplejump_p (BB_END (src)));
+  gcc_assert (!simplejump_p (BB_END (src)) || CROSSING_JUMP_P (BB_END (src)));
 
   df_set_bb_dirty (src);
   return ret;
@@ -4579,7 +4671,11 @@ cfg_layout_can_merge_blocks_p (basic_block a, basic_block b)
 static void
 cfg_layout_merge_blocks (basic_block a, basic_block b)
 {
-  bool forwarder_p = (b->flags & BB_FORWARDER_BLOCK) != 0;
+  /* If B is a forwarder block whose outgoing edge has no location, we'll
+     propagate the locus of the edge between A and B onto it.  */
+  const bool forward_edge_locus
+    = (b->flags & BB_FORWARDER_BLOCK) != 0
+      && LOCATION_LOCUS (EDGE_SUCC (b, 0)->goto_locus) == UNKNOWN_LOCATION;
   rtx_insn *insn;
 
   gcc_checking_assert (cfg_layout_can_merge_blocks_p (a, b));
@@ -4600,9 +4696,11 @@ cfg_layout_merge_blocks (basic_block a, basic_block b)
     try_redirect_by_replacing_jump (EDGE_SUCC (a, 0), b, true);
   gcc_assert (!JUMP_P (BB_END (a)));
 
-  /* When not optimizing and the edge is the only place in RTL which holds
-     some unique locus, emit a nop with that locus in between.  */
-  if (!optimize)
+  /* If not optimizing, preserve the locus of the single edge between
+     blocks A and B if necessary by emitting a nop.  */
+  if (!optimize
+      && !forward_edge_locus
+      && !DECL_IGNORED_P (current_function_decl))
     emit_nop_for_unique_locus_between (a, b);
 
   /* Move things from b->footer after a->footer.  */
@@ -4669,9 +4767,7 @@ cfg_layout_merge_blocks (basic_block a, basic_block b)
 
   df_bb_delete (b->index);
 
-  /* If B was a forwarder block, propagate the locus on the edge.  */
-  if (forwarder_p
-      && LOCATION_LOCUS (EDGE_SUCC (b, 0)->goto_locus) == UNKNOWN_LOCATION)
+  if (forward_edge_locus)
     EDGE_SUCC (b, 0)->goto_locus = EDGE_SUCC (a, 0)->goto_locus;
 
   if (dump_file)
@@ -5026,30 +5122,28 @@ rtl_can_remove_branch_p (const_edge e)
 }
 
 static basic_block
-rtl_duplicate_bb (basic_block bb)
+rtl_duplicate_bb (basic_block bb, copy_bb_data *id)
 {
-  bb = cfg_layout_duplicate_bb (bb);
+  bb = cfg_layout_duplicate_bb (bb, id);
   bb->aux = NULL;
   return bb;
 }
 
 /* Do book-keeping of basic block BB for the profile consistency checker.
-   If AFTER_PASS is 0, do pre-pass accounting, or if AFTER_PASS is 1
-   then do post-pass accounting.  Store the counting in RECORD.  */
+   Store the counting in RECORD.  */
 static void
-rtl_account_profile_record (basic_block bb, int after_pass,
-                           struct profile_record *record)
+rtl_account_profile_record (basic_block bb, struct profile_record *record)
 {
   rtx_insn *insn;
   FOR_BB_INSNS (bb, insn)
     if (INSN_P (insn))
       {
-       record->size[after_pass] += insn_cost (insn, false);
+       record->size += insn_cost (insn, false);
        if (bb->count.initialized_p ())
-         record->time[after_pass]
+         record->time
            += insn_cost (insn, true) * bb->count.to_gcov_type ();
        else if (profile_status_for_fn (cfun) == PROFILE_GUESSED)
-         record->time[after_pass]
+         record->time
            += insn_cost (insn, true) * bb->count.to_frequency (cfun);
       }
 }
@@ -5135,3 +5229,7 @@ struct cfg_hooks cfg_layout_rtl_cfg_hooks = {
 };
 
 #include "gt-cfgrtl.h"
+
+#if __GNUC__ >= 10
+#  pragma GCC diagnostic pop
+#endif