OSDN Git Service

Fix misapplied patch.
[pf3gnuchains/gcc-fork.git] / gcc / sel-sched.c
index a75ed04..102dc19 100644 (file)
@@ -448,7 +448,7 @@ struct code_motion_path_driver_info_def *code_motion_path_driver_info;
 
 /* Set of hooks for performing move_op and find_used_regs routines with
    code_motion_path_driver.  */
-struct code_motion_path_driver_info_def move_op_hooks, fur_hooks;
+extern struct code_motion_path_driver_info_def move_op_hooks, fur_hooks;
 
 /* True if/when we want to emulate Haifa scheduler in the common code.  
    This is used in sched_rgn_local_init and in various places in 
@@ -557,6 +557,7 @@ static int stat_substitutions_total;
 static bool rtx_ok_for_substitution_p (rtx, rtx);
 static int sel_rank_for_schedule (const void *, const void *);
 static av_set_t find_sequential_best_exprs (bnd_t, expr_t, bool);
+static basic_block find_block_for_bookkeeping (edge e1, edge e2, bool lax);
 
 static rtx get_dest_from_orig_ops (av_set_t);
 static basic_block generate_bookkeeping_insn (expr_t, edge, edge);
@@ -1169,7 +1170,7 @@ static void
 init_hard_regs_data (void)
 {
   int cur_reg = 0;
-  enum machine_mode cur_mode = 0;
+  int cur_mode = 0;
 
   CLEAR_HARD_REG_SET (sel_hrd.regs_ever_used);
   for (cur_reg = 0; cur_reg < FIRST_PSEUDO_REGISTER; cur_reg++)
@@ -2059,6 +2060,56 @@ moveup_expr_inside_insn_group (expr_t expr, insn_t through_insn)
 /* True when a conflict on a target register was found during moveup_expr.  */
 static bool was_target_conflict = false;
 
+/* Return true when moving a debug INSN across THROUGH_INSN will
+   create a bookkeeping block.  We don't want to create such blocks,
+   for they would cause codegen differences between compilations with
+   and without debug info.  */
+
+static bool
+moving_insn_creates_bookkeeping_block_p (insn_t insn,
+                                        insn_t through_insn)
+{
+  basic_block bbi, bbt;
+  edge e1, e2;
+  edge_iterator ei1, ei2;
+
+  if (!bookkeeping_can_be_created_if_moved_through_p (through_insn))
+    {
+      if (sched_verbose >= 9)
+       sel_print ("no bookkeeping required: ");
+      return FALSE;
+    }
+
+  bbi = BLOCK_FOR_INSN (insn);
+
+  if (EDGE_COUNT (bbi->preds) == 1)
+    {
+      if (sched_verbose >= 9)
+       sel_print ("only one pred edge: ");
+      return TRUE;
+    }
+
+  bbt = BLOCK_FOR_INSN (through_insn);
+
+  FOR_EACH_EDGE (e1, ei1, bbt->succs)
+    {
+      FOR_EACH_EDGE (e2, ei2, bbi->preds)
+       {
+         if (find_block_for_bookkeeping (e1, e2, TRUE))
+           {
+             if (sched_verbose >= 9)
+               sel_print ("found existing block: ");
+             return FALSE;
+           }
+       }
+    }
+
+  if (sched_verbose >= 9)
+    sel_print ("would create bookkeeping block: ");
+
+  return TRUE;
+}
+
 /* Modifies EXPR so it can be moved through the THROUGH_INSN,
    performing necessary transformations.  Record the type of transformation 
    made in PTRANS_TYPE, when it is not NULL.  When INSIDE_INSN_GROUP, 
@@ -2110,7 +2161,8 @@ moveup_expr (expr_t expr, insn_t through_insn, bool inside_insn_group,
           /* And it should be mutually exclusive with through_insn, or 
              be an unconditional jump.  */
           if (! any_uncondjump_p (insn)
-              && ! sched_insns_conditions_mutex_p (insn, through_insn))
+              && ! sched_insns_conditions_mutex_p (insn, through_insn)
+             && ! DEBUG_INSN_P (through_insn))
             return MOVEUP_EXPR_NULL;
         }
 
@@ -2131,6 +2183,12 @@ moveup_expr (expr_t expr, insn_t through_insn, bool inside_insn_group,
   else
     gcc_assert (!control_flow_insn_p (insn));
 
+  /* Don't move debug insns if this would require bookkeeping.  */
+  if (DEBUG_INSN_P (insn)
+      && BLOCK_FOR_INSN (through_insn) != BLOCK_FOR_INSN (insn)
+      && moving_insn_creates_bookkeeping_block_p (insn, through_insn))
+    return MOVEUP_EXPR_NULL;
+
   /* Deal with data dependencies.  */
   was_target_conflict = false;
   full_ds = has_dependence_p (expr, through_insn, &has_dep_p);
@@ -2440,7 +2498,12 @@ moveup_expr_cached (expr_t expr, insn_t insn, bool inside_insn_group)
       sel_print (" through %d: ", INSN_UID (insn));
     }
 
-  if (try_bitmap_cache (expr, insn, inside_insn_group, &res))
+  if (DEBUG_INSN_P (EXPR_INSN_RTX (expr))
+      && (sel_bb_head (BLOCK_FOR_INSN (EXPR_INSN_RTX (expr)))
+         == EXPR_INSN_RTX (expr)))
+    /* Don't use cached information for debug insns that are heads of
+       basic blocks.  */;
+  else if (try_bitmap_cache (expr, insn, inside_insn_group, &res))
     /* When inside insn group, we do not want remove stores conflicting
        with previosly issued loads.  */
     got_answer = ! inside_insn_group || res != MOVEUP_EXPR_NULL;
@@ -2852,6 +2915,9 @@ compute_av_set_inside_bb (insn_t first_insn, ilist_t p, int ws,
          break;          
        }
 
+      if (DEBUG_INSN_P (last_insn))
+       continue;
+
       if (end_ws > max_ws)
        {
          /* We can reach max lookahead size at bb_header, so clean av_set 
@@ -3261,6 +3327,12 @@ sel_rank_for_schedule (const void *x, const void *y)
   tmp_insn = EXPR_INSN_RTX (tmp);
   tmp2_insn = EXPR_INSN_RTX (tmp2);
   
+  /* Schedule debug insns as early as possible.  */
+  if (DEBUG_INSN_P (tmp_insn) && !DEBUG_INSN_P (tmp2_insn))
+    return -1;
+  else if (DEBUG_INSN_P (tmp2_insn))
+    return 1;
+
   /* Prefer SCHED_GROUP_P insns to any others.  */
   if (SCHED_GROUP_P (tmp_insn) != SCHED_GROUP_P (tmp2_insn))
     {
@@ -3332,9 +3404,6 @@ sel_rank_for_schedule (const void *x, const void *y)
        return dw;
     }
 
-  tmp_insn = EXPR_INSN_RTX (tmp);
-  tmp2_insn = EXPR_INSN_RTX (tmp2);
-
   /* Prefer an old insn to a bookkeeping insn.  */
   if (INSN_UID (tmp_insn) < first_emitted_uid 
       && INSN_UID (tmp2_insn) >= first_emitted_uid)
@@ -4412,15 +4481,16 @@ block_valid_for_bookkeeping_p (basic_block bb)
 /* Attempt to find a block that can hold bookkeeping code for path(s) incoming
    into E2->dest, except from E1->src (there may be a sequence of empty basic
    blocks between E1->src and E2->dest).  Return found block, or NULL if new
-   one must be created.  */
+   one must be created.  If LAX holds, don't assume there is a simple path
+   from E1->src to E2->dest.  */
 static basic_block
-find_block_for_bookkeeping (edge e1, edge e2)
+find_block_for_bookkeeping (edge e1, edge e2, bool lax)
 {
   basic_block candidate_block = NULL;
   edge e;
 
   /* Loop over edges from E1 to E2, inclusive.  */
-  for (e = e1; ; e = EDGE_SUCC (e->dest, 0))
+  for (e = e1; !lax || e->dest != EXIT_BLOCK_PTR; e = EDGE_SUCC (e->dest, 0))
     {
       if (EDGE_COUNT (e->dest->preds) == 2)
        {
@@ -4438,10 +4508,18 @@ find_block_for_bookkeeping (edge e1, edge e2)
        return NULL;
 
       if (e == e2)
-       return (block_valid_for_bookkeeping_p (candidate_block)
+       return ((!lax || candidate_block)
+               && block_valid_for_bookkeeping_p (candidate_block)
                ? candidate_block
                : NULL);
+
+      if (lax && EDGE_COUNT (e->dest->succs) != 1)
+       return NULL;
     }
+
+  if (lax)
+    return NULL;
+
   gcc_unreachable ();
 }
 
@@ -4485,6 +4563,101 @@ create_block_for_bookkeeping (edge e1, edge e2)
   gcc_assert (e1->dest == new_bb);
   gcc_assert (sel_bb_empty_p (bb));
 
+  /* To keep basic block numbers in sync between debug and non-debug
+     compilations, we have to rotate blocks here.  Consider that we
+     started from (a,b)->d, (c,d)->e, and d contained only debug
+     insns.  It would have been removed before if the debug insns
+     weren't there, so we'd have split e rather than d.  So what we do
+     now is to swap the block numbers of new_bb and
+     single_succ(new_bb) == e, so that the insns that were in e before
+     get the new block number.  */
+
+  if (MAY_HAVE_DEBUG_INSNS)
+    {
+      basic_block succ;
+      insn_t insn = sel_bb_head (new_bb);
+      insn_t last;
+
+      if (DEBUG_INSN_P (insn)
+         && single_succ_p (new_bb)
+         && (succ = single_succ (new_bb))
+         && succ != EXIT_BLOCK_PTR
+         && DEBUG_INSN_P ((last = sel_bb_end (new_bb))))
+       {
+         while (insn != last && (DEBUG_INSN_P (insn) || NOTE_P (insn)))
+           insn = NEXT_INSN (insn);
+
+         if (insn == last)
+           {
+             sel_global_bb_info_def gbi;
+             sel_region_bb_info_def rbi;
+             int i;
+
+             if (sched_verbose >= 2)
+               sel_print ("Swapping block ids %i and %i\n",
+                          new_bb->index, succ->index);
+
+             i = new_bb->index;
+             new_bb->index = succ->index;
+             succ->index = i;
+
+             SET_BASIC_BLOCK (new_bb->index, new_bb);
+             SET_BASIC_BLOCK (succ->index, succ);
+
+             memcpy (&gbi, SEL_GLOBAL_BB_INFO (new_bb), sizeof (gbi));
+             memcpy (SEL_GLOBAL_BB_INFO (new_bb), SEL_GLOBAL_BB_INFO (succ),
+                     sizeof (gbi));
+             memcpy (SEL_GLOBAL_BB_INFO (succ), &gbi, sizeof (gbi));
+
+             memcpy (&rbi, SEL_REGION_BB_INFO (new_bb), sizeof (rbi));
+             memcpy (SEL_REGION_BB_INFO (new_bb), SEL_REGION_BB_INFO (succ),
+                     sizeof (rbi));
+             memcpy (SEL_REGION_BB_INFO (succ), &rbi, sizeof (rbi));
+
+             i = BLOCK_TO_BB (new_bb->index);
+             BLOCK_TO_BB (new_bb->index) = BLOCK_TO_BB (succ->index);
+             BLOCK_TO_BB (succ->index) = i;
+
+             i = CONTAINING_RGN (new_bb->index);
+             CONTAINING_RGN (new_bb->index) = CONTAINING_RGN (succ->index);
+             CONTAINING_RGN (succ->index) = i;
+
+             for (i = 0; i < current_nr_blocks; i++)
+               if (BB_TO_BLOCK (i) == succ->index)
+                 BB_TO_BLOCK (i) = new_bb->index;
+               else if (BB_TO_BLOCK (i) == new_bb->index)
+                 BB_TO_BLOCK (i) = succ->index;
+
+             FOR_BB_INSNS (new_bb, insn)
+               if (INSN_P (insn))
+                 EXPR_ORIG_BB_INDEX (INSN_EXPR (insn)) = new_bb->index;
+
+             FOR_BB_INSNS (succ, insn)
+               if (INSN_P (insn))
+                 EXPR_ORIG_BB_INDEX (INSN_EXPR (insn)) = succ->index;
+
+             if (bitmap_bit_p (code_motion_visited_blocks, new_bb->index))
+               {
+                 bitmap_set_bit (code_motion_visited_blocks, succ->index);
+                 bitmap_clear_bit (code_motion_visited_blocks, new_bb->index);
+               }
+
+             gcc_assert (LABEL_P (BB_HEAD (new_bb))
+                         && LABEL_P (BB_HEAD (succ)));
+
+             if (sched_verbose >= 4)
+               sel_print ("Swapping code labels %i and %i\n",
+                          CODE_LABEL_NUMBER (BB_HEAD (new_bb)),
+                          CODE_LABEL_NUMBER (BB_HEAD (succ)));
+
+             i = CODE_LABEL_NUMBER (BB_HEAD (new_bb));
+             CODE_LABEL_NUMBER (BB_HEAD (new_bb))
+               = CODE_LABEL_NUMBER (BB_HEAD (succ));
+             CODE_LABEL_NUMBER (BB_HEAD (succ)) = i;
+           }
+       }
+    }
+
   return bb;
 }
 
@@ -4496,12 +4669,42 @@ find_place_for_bookkeeping (edge e1, edge e2)
   insn_t place_to_insert;
   /* Find a basic block that can hold bookkeeping.  If it can be found, do not
      create new basic block, but insert bookkeeping there.  */
-  basic_block book_block = find_block_for_bookkeeping (e1, e2);
+  basic_block book_block = find_block_for_bookkeeping (e1, e2, FALSE);
 
-  if (!book_block)
-    book_block = create_block_for_bookkeeping (e1, e2);
+  if (book_block)
+    {
+      place_to_insert = BB_END (book_block);
+
+      /* Don't use a block containing only debug insns for
+        bookkeeping, this causes scheduling differences between debug
+        and non-debug compilations, for the block would have been
+        removed already.  */
+      if (DEBUG_INSN_P (place_to_insert))
+       {
+         rtx insn = sel_bb_head (book_block);
+
+         while (insn != place_to_insert &&
+                (DEBUG_INSN_P (insn) || NOTE_P (insn)))
+           insn = NEXT_INSN (insn);
+
+         if (insn == place_to_insert)
+           book_block = NULL;
+       }
+    }
 
-  place_to_insert = BB_END (book_block);
+  if (!book_block)
+    {
+      book_block = create_block_for_bookkeeping (e1, e2);
+      place_to_insert = BB_END (book_block);
+      if (sched_verbose >= 9)
+       sel_print ("New block is %i, split from bookkeeping block %i\n",
+                  EDGE_SUCC (book_block, 0)->dest->index, book_block->index);
+    }
+  else
+    {
+      if (sched_verbose >= 9)
+       sel_print ("Pre-existing bookkeeping block is %i\n", book_block->index);
+    }
 
   /* If basic block ends with a jump, insert bookkeeping code right before it.  */
   if (INSN_P (place_to_insert) && control_flow_insn_p (place_to_insert))
@@ -4524,11 +4727,27 @@ find_seqno_for_bookkeeping (insn_t place_to_insert, insn_t join_point)
   if (INSN_P (next) 
       && JUMP_P (next)
       && BLOCK_FOR_INSN (next) == BLOCK_FOR_INSN (place_to_insert))
-    seqno = INSN_SEQNO (next);
+    {
+      gcc_assert (INSN_SCHED_TIMES (next) == 0);
+      seqno = INSN_SEQNO (next);
+    }
   else if (INSN_SEQNO (join_point) > 0)
     seqno = INSN_SEQNO (join_point);
   else
-    seqno = get_seqno_by_preds (place_to_insert);
+    {
+      seqno = get_seqno_by_preds (place_to_insert);
+
+      /* Sometimes the fences can move in such a way that there will be 
+         no instructions with positive seqno around this bookkeeping.  
+         This means that there will be no way to get to it by a regular
+         fence movement.  Never mind because we pick up such pieces for
+         rescheduling anyways, so any positive value will do for now.  */
+      if (seqno < 0)
+        {
+          gcc_assert (pipelining_p);
+          seqno = 1;
+        }
+    }
   
   gcc_assert (seqno > 0);
   return seqno;
@@ -4571,6 +4790,8 @@ generate_bookkeeping_insn (expr_t c_expr, edge e1, edge e2)
 
   join_point = sel_bb_head (e2->dest);
   place_to_insert = find_place_for_bookkeeping (e1, e2);
+  if (!place_to_insert)
+    return NULL;
   new_seqno = find_seqno_for_bookkeeping (place_to_insert, join_point);
   need_to_exchange_data_sets
     = sel_bb_empty_p (BLOCK_FOR_INSN (place_to_insert));
@@ -4732,7 +4953,7 @@ move_cond_jump (rtx insn, bnd_t bnd)
 /* Remove nops generated during move_op for preventing removal of empty
    basic blocks.  */
 static void
-remove_temp_moveop_nops (void)
+remove_temp_moveop_nops (bool full_tidying)
 {
   int i;
   insn_t insn;
@@ -4740,7 +4961,7 @@ remove_temp_moveop_nops (void)
   for (i = 0; VEC_iterate (insn_t, vec_temp_moveop_nops, i, insn); i++)
     {
       gcc_assert (INSN_NOP_P (insn));
-      return_nop_to_pool (insn);
+      return_nop_to_pool (insn, full_tidying);
     }
 
   /* Empty the vector.  */
@@ -4933,8 +5154,20 @@ prepare_place_to_insert (bnd_t bnd)
     {
       /* Add it after last scheduled.  */
       place_to_insert = ILIST_INSN (BND_PTR (bnd));
+      if (DEBUG_INSN_P (place_to_insert))
+       {
+         ilist_t l = BND_PTR (bnd);
+         while ((l = ILIST_NEXT (l)) &&
+                DEBUG_INSN_P (ILIST_INSN (l)))
+           ;
+         if (!l)
+           place_to_insert = NULL;
+       }
     }
   else
+    place_to_insert = NULL;
+
+  if (!place_to_insert)
     {
       /* Add it before BND_TO.  The difference is in the
          basic block, where INSN will be added.  */
@@ -5042,7 +5275,8 @@ advance_state_on_fence (fence_t fence, insn_t insn)
 
   if (sched_verbose >= 2)
     debug_state (FENCE_STATE (fence));
-  FENCE_STARTS_CYCLE_P (fence) = 0;
+  if (!DEBUG_INSN_P (insn))
+    FENCE_STARTS_CYCLE_P (fence) = 0;
   return asm_p;
 }
 
@@ -5101,10 +5335,11 @@ update_fence_and_insn (fence_t fence, insn_t insn, int need_stall)
     }
 }
 
-/* Update boundary BND with INSN, remove the old boundary from
-   BNDSP, add new boundaries to BNDS_TAIL_P and return it.  */
+/* Update boundary BND (and, if needed, FENCE) with INSN, remove the
+   old boundary from BNDSP, add new boundaries to BNDS_TAIL_P and
+   return it.  */
 static blist_t *
-update_boundaries (bnd_t bnd, insn_t insn, blist_t *bndsp, 
+update_boundaries (fence_t fence, bnd_t bnd, insn_t insn, blist_t *bndsp,
                    blist_t *bnds_tailp)
 {
   succ_iterator si;
@@ -5117,6 +5352,21 @@ update_boundaries (bnd_t bnd, insn_t insn, blist_t *bndsp,
       ilist_t ptr = ilist_copy (BND_PTR (bnd));
       
       ilist_add (&ptr, insn);
+
+      if (DEBUG_INSN_P (insn) && sel_bb_end_p (insn)
+         && is_ineligible_successor (succ, ptr))
+       {
+         ilist_clear (&ptr);
+         continue;
+       }
+
+      if (FENCE_INSN (fence) == insn && !sel_bb_end_p (insn))
+       {
+         if (sched_verbose >= 9)
+           sel_print ("Updating fence insn from %i to %i\n",
+                      INSN_UID (insn), INSN_UID (succ));
+         FENCE_INSN (fence) = succ;
+       }
       blist_add (bnds_tailp, succ, ptr, BND_DC (bnd));
       bnds_tailp = &BLIST_NEXT (*bnds_tailp);
     }
@@ -5176,8 +5426,8 @@ schedule_expr_on_boundary (bnd_t bnd, expr_t expr_vliw, int seqno)
   /* Return the nops generated for preserving of data sets back
      into pool.  */
   if (INSN_NOP_P (place_to_insert))
-    return_nop_to_pool (place_to_insert);
-  remove_temp_moveop_nops ();
+    return_nop_to_pool (place_to_insert, !DEBUG_INSN_P (insn));
+  remove_temp_moveop_nops (!DEBUG_INSN_P (insn));
 
   av_set_clear (&expr_seq);
  
@@ -5235,7 +5485,9 @@ fill_insns (fence_t fence, int seqno, ilist_t **scheduled_insns_tailpp)
       int was_stall = 0, scheduled_insns = 0, stall_iterations = 0;
       int max_insns = pipelining_p ? issue_rate : 2 * issue_rate;
       int max_stall = pipelining_p ? 1 : 3;
-      
+      bool last_insn_was_debug = false;
+      bool was_debug_bb_end_p = false;
+
       compute_av_set_on_boundaries (fence, bnds, &av_vliw);
       remove_insns_that_need_bookkeeping (fence, &av_vliw);
       remove_insns_for_debug (bnds, &av_vliw);
@@ -5293,8 +5545,11 @@ fill_insns (fence_t fence, int seqno, ilist_t **scheduled_insns_tailpp)
            }
           
           insn = schedule_expr_on_boundary (bnd, expr_vliw, seqno);
+         last_insn_was_debug = DEBUG_INSN_P (insn);
+         if (last_insn_was_debug)
+           was_debug_bb_end_p = (insn == BND_TO (bnd) && sel_bb_end_p (insn));
           update_fence_and_insn (fence, insn, need_stall);
-          bnds_tailp = update_boundaries (bnd, insn, bndsp, bnds_tailp);
+          bnds_tailp = update_boundaries (fence, bnd, insn, bndsp, bnds_tailp);
 
          /* Add insn to the list of scheduled on this cycle instructions.  */
          ilist_add (*scheduled_insns_tailpp, insn);
@@ -5303,13 +5558,14 @@ fill_insns (fence_t fence, int seqno, ilist_t **scheduled_insns_tailpp)
       while (*bndsp != *bnds_tailp1);
 
       av_set_clear (&av_vliw);
-      scheduled_insns++;
+      if (!last_insn_was_debug)
+       scheduled_insns++;
 
       /* We currently support information about candidate blocks only for
         one 'target_bb' block.  Hence we can't schedule after jump insn,
         as this will bring two boundaries and, hence, necessity to handle
         information for two or more blocks concurrently.  */
-      if (sel_bb_end_p (insn)
+      if ((last_insn_was_debug ? was_debug_bb_end_p : sel_bb_end_p (insn))
           || (was_stall 
               && (was_stall >= max_stall 
                   || scheduled_insns >= max_insns)))
@@ -5528,7 +5784,7 @@ track_scheduled_insns_and_blocks (rtx insn)
         instruction out of it.  */
       if (INSN_SCHED_TIMES (insn) > 0)
        bitmap_set_bit (blocks_to_reschedule, BLOCK_FOR_INSN (insn)->index);
-      else if (INSN_UID (insn) < first_emitted_uid)
+      else if (INSN_UID (insn) < first_emitted_uid && !DEBUG_INSN_P (insn))
        num_insns_scheduled++;
     }
   else
@@ -5620,32 +5876,63 @@ handle_emitting_transformations (rtx insn, expr_t expr,
   return insn_emitted;
 }  
 
-/* Remove INSN from stream.  When ONLY_DISCONNECT is true, its data 
-   is not removed but reused when INSN is re-emitted.  */
-static void
-remove_insn_from_stream (rtx insn, bool only_disconnect)
+/* If INSN is the only insn in the basic block (not counting JUMP,
+   which may be a jump to next insn, and DEBUG_INSNs), we want to
+   leave a NOP there till the return to fill_insns.  */
+
+static bool
+need_nop_to_preserve_insn_bb (rtx insn)
 {
-  insn_t nop, bb_head, bb_end;
-  bool need_nop_to_preserve_bb;
+  insn_t bb_head, bb_end, bb_next, in_next;
   basic_block bb = BLOCK_FOR_INSN (insn);
 
-  /* If INSN is the only insn in the basic block (not counting JUMP,
-     which may be a jump to next insn), leave NOP there till the 
-     return to fill_insns.  */
   bb_head = sel_bb_head (bb);
   bb_end = sel_bb_end (bb);
-  need_nop_to_preserve_bb = ((bb_head == bb_end)
-                             || (NEXT_INSN (bb_head) == bb_end 
-                                 && JUMP_P (bb_end))
-                             || IN_CURRENT_FENCE_P (NEXT_INSN (insn)));
 
+  if (bb_head == bb_end)
+    return true;
+
+  while (bb_head != bb_end && DEBUG_INSN_P (bb_head))
+    bb_head = NEXT_INSN (bb_head);
+
+  if (bb_head == bb_end)
+    return true;
+
+  while (bb_head != bb_end && DEBUG_INSN_P (bb_end))
+    bb_end = PREV_INSN (bb_end);
+
+  if (bb_head == bb_end)
+    return true;
+
+  bb_next = NEXT_INSN (bb_head);
+  while (bb_next != bb_end && DEBUG_INSN_P (bb_next))
+    bb_next = NEXT_INSN (bb_next);
+
+  if (bb_next == bb_end && JUMP_P (bb_end))
+    return true;
+
+  in_next = NEXT_INSN (insn);
+  while (DEBUG_INSN_P (in_next))
+    in_next = NEXT_INSN (in_next);
+
+  if (IN_CURRENT_FENCE_P (in_next))
+    return true;
+
+  return false;
+}
+
+/* Remove INSN from stream.  When ONLY_DISCONNECT is true, its data
+   is not removed but reused when INSN is re-emitted.  */
+static void
+remove_insn_from_stream (rtx insn, bool only_disconnect)
+{
   /* If there's only one insn in the BB, make sure that a nop is
      inserted into it, so the basic block won't disappear when we'll
      delete INSN below with sel_remove_insn. It should also survive
      till the return to fill_insns.  */             
-  if (need_nop_to_preserve_bb)
+  if (need_nop_to_preserve_insn_bb (insn))
     {
-      nop = get_nop_from_pool (insn);
+      insn_t nop = get_nop_from_pool (insn);
       gcc_assert (INSN_NOP_P (nop));
       VEC_safe_push (insn_t, heap, vec_temp_moveop_nops, nop);
     }
@@ -5909,6 +6196,8 @@ fur_orig_expr_not_found (insn_t insn, av_set_t orig_ops, void *static_params)
 
   if (CALL_P (insn))
     sparams->crosses_call = true;
+  else if (DEBUG_INSN_P (insn))
+    return true;
 
   /* If current insn we are looking at cannot be executed together
      with original insn, then we can skip it safely.
@@ -6483,9 +6772,10 @@ setup_current_loop_nest (int rgn)
 static void
 purge_empty_blocks (void)
 {
-  int i ;
+  /* Do not attempt to delete preheader.  */
+  int i = sel_is_loop_preheader_p (BASIC_BLOCK (BB_TO_BLOCK (0))) ? 1 : 0;
 
-  for (i = 1; i < current_nr_blocks; )
+  while (i < current_nr_blocks)
     {
       basic_block b = BASIC_BLOCK (BB_TO_BLOCK (i));