This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: fix left-over debug insns in DCE


On Jun  6, 2011, Alexandre Oliva <aoliva@redhat.com> wrote:

> On Jun  6, 2011, Eric Botcazou <ebotcazou@adacore.com> wrote:
>>> It might be too late for DF to do anything sensible to preserve the
>>> debug info rather than just throw it away, as your partial approval
>>> suggests.

>> OK, let me think about this a little more.

Ping?

> Here are the remaining bits.

Some more context here: this enables DCE to turn removed insns into
debug temps when they're useful for debug info.  It further improves
debug info quality when installed along with the patch I just posted for
PR 48866.  Without it, a number of chains of debug temps that lead to a
real insn that gets deleted end up useless.

Regstrapped on x86_64-linux-gnu and i686-pc-linux-gnu.  Ok to install?

for  gcc/ChangeLog
from  Alexandre Oliva  <aoliva@redhat.com>

	PR debug/48866
	* df.h (enum debug_temp_where): New.
	(dead_debug_init, dead_debug_finish) Declare.
	(dead_debug_add, dead_debug_insert_temp): Declare.
	(struct dead_debug_use, struct dead_debug): Moved from...
	* df-problems.c: ... here.
	(df_set_unused_notes_for_mw): Bind debug uses of unused regno
	to a debug temp.
	(df_create_unused_note): Likewise.
	(df_set_dead_notes_for_mw): Move comment where it belongs.
	(dead_debug_init): Export.
	(dead_debug_reset_uses): New, factored out of...
	(dead_debug_finish): ...this.  Export.
	(dead_debug_reset): Remove.
	(dead_debug_add): Export.
	(dead_debug_insert_before): Rename to...
	(dead_debug_insert_temp): ... this.  Add where argument.  Export.
	Locate stored value for BEFORE_WITH_VALUE.  Avoid repeat inserts.
	Return insertion count.
	(df_note_bb_compute): Adjust.
	* dce.c (word_dce_process_block): Adjust dead debug uses.
	(dce_process_block): Likewise.

Index: gcc/df.h
===================================================================
--- gcc/df.h.orig	2012-03-01 04:26:27.926134403 -0300
+++ gcc/df.h	2012-03-26 11:19:12.300584463 -0300
@@ -1101,4 +1101,35 @@ extern void union_defs (df_ref, struct w
 			unsigned int *used, struct web_entry *,
 			bool (*fun) (struct web_entry *, struct web_entry *));
 
+/* Debug uses of dead regs.  */
+
+/* Node of a linked list of uses of dead REGs in debug insns.  */
+struct dead_debug_use
+{
+  df_ref use;
+  struct dead_debug_use *next;
+};
+
+/* Linked list of the above, with a bitmap of the REGs in the
+   list.  */
+struct dead_debug
+{
+  struct dead_debug_use *head;
+  bitmap used;
+  bitmap to_rescan;
+};
+
+enum debug_temp_where
+  {
+    DEBUG_TEMP_BEFORE_WITH_REG = -1,
+    DEBUG_TEMP_BEFORE_WITH_VALUE = 0,
+    DEBUG_TEMP_AFTER_WITH_REG = 1
+  };
+
+void dead_debug_init (struct dead_debug *, bitmap);
+void dead_debug_finish (struct dead_debug *, bitmap);
+void dead_debug_add (struct dead_debug *, df_ref, unsigned int);
+int dead_debug_insert_temp (struct dead_debug *, unsigned int, rtx,
+			    enum debug_temp_where);
+
 #endif /* GCC_DF_H */
Index: gcc/df-problems.c
===================================================================
--- gcc/df-problems.c.orig	2012-01-30 16:47:05.000000000 -0200
+++ gcc/df-problems.c	2012-03-26 11:49:17.848542873 -0300
@@ -2886,25 +2886,6 @@ df_whole_mw_reg_unused_p (struct df_mw_h
 }
 
 
-/* Node of a linked list of uses of dead REGs in debug insns.  */
-struct dead_debug_use
-{
-  df_ref use;
-  struct dead_debug_use *next;
-};
-
-/* Linked list of the above, with a bitmap of the REGs in the
-   list.  */
-struct dead_debug
-{
-  struct dead_debug_use *head;
-  bitmap used;
-  bitmap to_rescan;
-};
-
-static void dead_debug_reset (struct dead_debug *, unsigned int);
-
-
 /* Set the REG_UNUSED notes for the multiword hardreg defs in INSN
    based on the bits in LIVE.  Do not generate notes for registers in
    artificial uses.  DO_NOT_GEN is updated so that REG_DEAD notes are
@@ -2930,7 +2911,7 @@ df_set_unused_notes_for_mw (rtx insn, st
     {
       unsigned int regno = mws->start_regno;
       df_set_note (REG_UNUSED, insn, mws->mw_reg);
-      dead_debug_reset (debug, regno);
+      dead_debug_insert_temp (debug, regno, insn, DEBUG_TEMP_AFTER_WITH_REG);
 
 #ifdef REG_DEAD_DEBUGGING
       df_print_note ("adding 1: ", insn, REG_NOTES (insn));
@@ -2945,7 +2926,7 @@ df_set_unused_notes_for_mw (rtx insn, st
 	    && !bitmap_bit_p (artificial_uses, r))
 	  {
 	    df_set_note (REG_UNUSED, insn, regno_reg_rtx[r]);
-	    dead_debug_reset (debug, r);
+	    dead_debug_insert_temp (debug, r, insn, DEBUG_TEMP_AFTER_WITH_REG);
 #ifdef REG_DEAD_DEBUGGING
 	    df_print_note ("adding 2: ", insn, REG_NOTES (insn));
 #endif
@@ -3013,12 +2994,12 @@ df_set_dead_notes_for_mw (rtx insn, stru
 
   if (df_whole_mw_reg_dead_p (mws, live, artificial_uses, do_not_gen))
     {
-      /* Add a dead note for the entire multi word register.  */
       if (is_debug)
 	{
 	  *added_notes_p = true;
 	  return;
 	}
+      /* Add a dead note for the entire multi word register.  */
       df_set_note (REG_DEAD, insn, mws->mw_reg);
 #ifdef REG_DEAD_DEBUGGING
       df_print_note ("adding 1: ", insn, REG_NOTES (insn));
@@ -3072,7 +3053,7 @@ df_create_unused_note (rtx insn, df_ref 
       rtx reg = (DF_REF_LOC (def))
                 ? *DF_REF_REAL_LOC (def): DF_REF_REG (def);
       df_set_note (REG_UNUSED, insn, reg);
-      dead_debug_reset (debug, dregno);
+      dead_debug_insert_temp (debug, dregno, insn, DEBUG_TEMP_AFTER_WITH_REG);
 #ifdef REG_DEAD_DEBUGGING
       df_print_note ("adding 3: ", insn, REG_NOTES (insn));
 #endif
@@ -3083,7 +3064,7 @@ df_create_unused_note (rtx insn, df_ref 
 
 
 /* Initialize DEBUG to an empty list, and clear USED, if given.  */
-static inline void
+void
 dead_debug_init (struct dead_debug *debug, bitmap used)
 {
   debug->head = NULL;
@@ -3093,32 +3074,83 @@ dead_debug_init (struct dead_debug *debu
     bitmap_clear (used);
 }
 
-/* Reset all debug insns with pending uses.  Release the bitmap in it,
-   unless it is USED.  USED must be the same bitmap passed to
-   dead_debug_init.  */
-static inline void
-dead_debug_finish (struct dead_debug *debug, bitmap used)
+/* Reset all debug uses in HEAD, and clear DEBUG->to_rescan bits of
+   each reset insn.  DEBUG is not otherwise modified.  If HEAD is
+   DEBUG->head, DEBUG->head will be set to NULL at the end.
+   Otherwise, entries from DEBUG->head that pertain to reset insns
+   will be removed, and only then rescanned.  */
+
+static void
+dead_debug_reset_uses (struct dead_debug *debug, struct dead_debug_use *head)
 {
-  struct dead_debug_use *head;
-  rtx insn = NULL;
+  bool got_head = (debug->head == head);
+  bitmap rescan;
+  struct dead_debug_use **tailp = &debug->head;
+  struct dead_debug_use *cur;
+  bitmap_iterator bi;
+  unsigned int uid;
 
-  if (debug->used != used)
-    BITMAP_FREE (debug->used);
+  if (got_head)
+    rescan = NULL;
+  else
+    rescan = BITMAP_ALLOC (NULL);
 
-  while ((head = debug->head))
+  while (head)
     {
+      struct dead_debug_use *next = head->next;
+      rtx insn;
+
       insn = DF_REF_INSN (head->use);
-      if (!head->next || DF_REF_INSN (head->next->use) != insn)
+      if (!next || DF_REF_INSN (next->use) != insn)
 	{
 	  INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC ();
-	  df_insn_rescan_debug_internal (insn);
+	  if (got_head)
+	    df_insn_rescan_debug_internal (insn);
+	  else
+	    bitmap_set_bit (rescan, INSN_UID (insn));
 	  if (debug->to_rescan)
 	    bitmap_clear_bit (debug->to_rescan, INSN_UID (insn));
 	}
-      debug->head = head->next;
       XDELETE (head);
+      head = next;
     }
 
+  if (got_head)
+    {
+      debug->head = NULL;
+      return;
+    }
+
+  while ((cur = *tailp))
+    if (bitmap_bit_p (rescan, INSN_UID (DF_REF_INSN (cur->use))))
+      {
+	*tailp = cur->next;
+	XDELETE (cur);
+      }
+    else
+      tailp = &cur->next;
+
+  EXECUTE_IF_SET_IN_BITMAP (rescan, 0, uid, bi)
+    {
+      struct df_insn_info *insn_info = DF_INSN_UID_SAFE_GET (uid);
+      if (insn_info)
+	df_insn_rescan_debug_internal (insn_info->insn);
+    }
+
+  BITMAP_FREE (rescan);
+}
+
+/* Reset all debug insns with pending uses.  Release the bitmap in it,
+   unless it is USED.  USED must be the same bitmap passed to
+   dead_debug_init.  */
+void
+dead_debug_finish (struct dead_debug *debug, bitmap used)
+{
+  if (debug->used != used)
+    BITMAP_FREE (debug->used);
+
+  dead_debug_reset_uses (debug, debug->head);
+
   if (debug->to_rescan)
     {
       bitmap_iterator bi;
@@ -3134,54 +3166,9 @@ dead_debug_finish (struct dead_debug *de
     }
 }
 
-/* Reset DEBUG_INSNs with pending uses of DREGNO.  */
-static void
-dead_debug_reset (struct dead_debug *debug, unsigned int dregno)
-{
-  struct dead_debug_use **tailp = &debug->head;
-  struct dead_debug_use **insnp = &debug->head;
-  struct dead_debug_use *cur;
-  rtx insn;
-
-  if (!debug->used || !bitmap_clear_bit (debug->used, dregno))
-    return;
-
-  while ((cur = *tailp))
-    {
-      if (DF_REF_REGNO (cur->use) == dregno)
-	{
-	  *tailp = cur->next;
-	  insn = DF_REF_INSN (cur->use);
-	  INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC ();
-	  if (debug->to_rescan == NULL)
-	    debug->to_rescan = BITMAP_ALLOC (NULL);
-	  bitmap_set_bit (debug->to_rescan, INSN_UID (insn));
-	  XDELETE (cur);
-	  /* If the current use isn't the first one attached to INSN, go back
-	     to this first use.  We assume that the uses attached to an insn
-	     are adjacent.  */                                                                       
-	  if (tailp != insnp && DF_REF_INSN ((*insnp)->use) == insn)
-	    tailp = insnp;
-	  /* Then remove all the other uses attached to INSN.  */
-	  while ((cur = *tailp) && DF_REF_INSN (cur->use) == insn)
-	    {
-	      *tailp = cur->next;
-	      XDELETE (cur);
-	    }
-	  insnp = tailp;
-	}
-      else
-	{
-	  if (DF_REF_INSN ((*insnp)->use) != DF_REF_INSN (cur->use))
-	    insnp = tailp;
-	  tailp = &(*tailp)->next;
-	}
-    }
-}
-
 /* Add USE to DEBUG.  It must be a dead reference to UREGNO in a debug
    insn.  Create a bitmap for DEBUG as needed.  */
-static inline void
+void
 dead_debug_add (struct dead_debug *debug, df_ref use, unsigned int uregno)
 {
   struct dead_debug_use *newddu = XNEW (struct dead_debug_use);
@@ -3197,23 +3184,27 @@ dead_debug_add (struct dead_debug *debug
 }
 
 /* If UREGNO is referenced by any entry in DEBUG, emit a debug insn
-   before INSN that binds the REG to a debug temp, and replace all
-   uses of UREGNO in DEBUG with uses of the debug temp.  INSN must be
-   the insn where UREGNO dies.  */
-static inline void
-dead_debug_insert_before (struct dead_debug *debug, unsigned int uregno,
-			  rtx insn)
+   before or after INSN (depending on WHERE), that binds a debug temp
+   to the widest-mode use of UREGNO, if WHERE is _WITH_REG, or
+   _WITH_VALUE otherwise, and replace all uses of UREGNO in DEBUG with
+   uses of the debug temp.  INSN must be where UREGNO dies, if WHERE
+   is DEAD_DEBUG_BEFORE_WITH_REG, or where it is set otherwise.
+   Return the number of debug insns emitted.  */
+int
+dead_debug_insert_temp (struct dead_debug *debug, unsigned int uregno,
+			rtx insn, enum debug_temp_where where)
 {
   struct dead_debug_use **tailp = &debug->head;
   struct dead_debug_use *cur;
   struct dead_debug_use *uses = NULL;
   struct dead_debug_use **usesp = &uses;
   rtx reg = NULL;
+  rtx breg;
   rtx dval;
   rtx bind;
 
   if (!debug->used || !bitmap_clear_bit (debug->used, uregno))
-    return;
+    return 0;
 
   /* Move all uses of uregno from debug->head to uses, setting mode to
      the widest referenced mode.  */
@@ -3237,17 +3228,99 @@ dead_debug_insert_before (struct dead_de
   /* We may have dangling bits in debug->used for registers that were part
      of a multi-register use, one component of which has been reset.  */
   if (reg == NULL)
-    return;
+    {
+      gcc_checking_assert (!uses);
+      return 0;
+    }
+
+  gcc_checking_assert (uses);
+
+  breg = reg;
+  if (where == DEBUG_TEMP_BEFORE_WITH_VALUE)
+    {
+      rtx set = single_set (insn);
+      rtx dest, src;
+
+      if (set)
+	{
+	  dest = SET_DEST (set);
+	  src = SET_SRC (set);
+	  if (GET_CODE (src) == CALL)
+	    {
+	      while (uses)
+		{
+		  cur = uses->next;
+		  XDELETE (uses);
+		  uses = cur;
+		}
+	      return 0;
+	    }
+	}
+      else
+	set = NULL;
+
+      if (!set)
+	breg = NULL;
+      else if (dest == reg)
+	breg = copy_rtx (src);
+      else if (REG_P (dest))
+	{
+	  if (REGNO (dest) != REGNO (reg))
+	    breg = NULL;
+	  else
+	    breg = lowpart_subreg (GET_MODE (reg), copy_rtx (src),
+				   GET_MODE (dest));
+	}
+      else if (GET_CODE (dest) == SUBREG)
+	{
+	  if (REGNO (SUBREG_REG (dest)) != REGNO (reg))
+	    breg = NULL;
+	  else if (!subreg_lowpart_p (dest))
+	    breg = NULL;
+	  else if (REGNO (reg) < FIRST_PSEUDO_REGISTER
+		   && (hard_regno_nregs[REGNO (reg)][GET_MODE (reg)]
+		       == hard_regno_nregs[REGNO (reg)][GET_MODE (dest)]))
+	    breg = NULL;
+	  else
+	    breg = lowpart_subreg (GET_MODE (reg), copy_rtx (src),
+				   GET_MODE (dest));
+	}
+      else
+	breg = NULL;
+
+      if (!breg)
+	{
+	  dead_debug_reset_uses (debug, uses);
+	  return 0;
+	}
+    }
+
+  /* If there's a single (debug) use of an otherwise unused REG, and
+     the debug use is not part of a larger expression, then it
+     probably doesn't make sense to introduce a new debug temp.  */
+  if (where == DEBUG_TEMP_AFTER_WITH_REG && !uses->next)
+    {
+      rtx next = DF_REF_INSN (uses->use);
+
+      if (DEBUG_INSN_P (next) && reg == INSN_VAR_LOCATION_LOC (next))
+	{
+	  XDELETE (uses);
+	  return 0;
+	}
+    }
 
   /* Create DEBUG_EXPR (and DEBUG_EXPR_DECL).  */
   dval = make_debug_expr_from_rtl (reg);
 
   /* Emit a debug bind insn before the insn in which reg dies.  */
   bind = gen_rtx_VAR_LOCATION (GET_MODE (reg),
-			       DEBUG_EXPR_TREE_DECL (dval), reg,
+			       DEBUG_EXPR_TREE_DECL (dval), breg,
 			       VAR_INIT_STATUS_INITIALIZED);
 
-  bind = emit_debug_insn_before (bind, insn);
+  if (where == DEBUG_TEMP_AFTER_WITH_REG)
+    bind = emit_debug_insn_after (bind, insn);
+  else
+    bind = emit_debug_insn_before (bind, insn);
   df_insn_rescan (bind);
 
   /* Adjust all uses.  */
@@ -3265,6 +3338,8 @@ dead_debug_insert_before (struct dead_de
       uses = cur->next;
       XDELETE (cur);
     }
+
+  return 1;
 }
 
 /* Recompute the REG_DEAD and REG_UNUSED notes and compute register
@@ -3459,7 +3534,8 @@ df_note_bb_compute (unsigned int bb_inde
 		  break;
 		}
 	      else
-		dead_debug_insert_before (&debug, uregno, insn);
+		dead_debug_insert_temp (&debug, uregno, insn,
+					DEBUG_TEMP_BEFORE_WITH_REG);
 
 	      if ( (!(DF_REF_FLAGS (use)
 		      & (DF_REF_MW_HARDREG | DF_REF_READ_WRITE)))
Index: gcc/dce.c
===================================================================
--- gcc/dce.c.orig	2011-09-11 14:43:40.000000000 -0300
+++ gcc/dce.c	2012-03-26 11:19:12.271584817 -0300
@@ -807,6 +807,7 @@ word_dce_process_block (basic_block bb, 
   bitmap local_live = BITMAP_ALLOC (&dce_tmp_bitmap_obstack);
   rtx insn;
   bool block_changed;
+  struct dead_debug debug;
 
   if (redo_out)
     {
@@ -828,11 +829,24 @@ word_dce_process_block (basic_block bb, 
     }
 
   bitmap_copy (local_live, DF_WORD_LR_OUT (bb));
+  dead_debug_init (&debug, NULL);
 
   FOR_BB_INSNS_REVERSE (bb, insn)
-    if (NONDEBUG_INSN_P (insn))
+    if (DEBUG_INSN_P (insn))
+      {
+	df_ref *use_rec;
+	for (use_rec = DF_INSN_USES (insn); *use_rec; use_rec++)
+	  if (DF_REF_REGNO (*use_rec) >= FIRST_PSEUDO_REGISTER
+	      && (GET_MODE_SIZE (GET_MODE (DF_REF_REAL_REG (*use_rec)))
+		  == 2 * UNITS_PER_WORD)
+	      && !bitmap_bit_p (local_live, 2 * DF_REF_REGNO (*use_rec))
+	      && !bitmap_bit_p (local_live, 2 * DF_REF_REGNO (*use_rec) + 1))
+	    dead_debug_add (&debug, *use_rec, DF_REF_REGNO (*use_rec));
+      }
+    else if (INSN_P (insn))
       {
 	bool any_changed;
+
 	/* No matter if the instruction is needed or not, we remove
 	   any regno in the defs from the live set.  */
 	any_changed = df_word_lr_simulate_defs (insn, local_live);
@@ -844,6 +858,15 @@ word_dce_process_block (basic_block bb, 
 	if (marked_insn_p (insn))
 	  df_word_lr_simulate_uses (insn, local_live);
 
+	if (debug.used && !bitmap_empty_p (debug.used))
+	  {
+	    df_ref *def_rec;
+
+	    for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++)
+	      dead_debug_insert_temp (&debug, DF_REF_REGNO (*def_rec), insn,
+				      DEBUG_TEMP_BEFORE_WITH_VALUE);
+	  }
+
 	if (dump_file)
 	  {
 	    fprintf (dump_file, "finished processing insn %d live out = ",
@@ -856,6 +879,7 @@ word_dce_process_block (basic_block bb, 
   if (block_changed)
     bitmap_copy (DF_WORD_LR_IN (bb), local_live);
 
+  dead_debug_finish (&debug, NULL);
   BITMAP_FREE (local_live);
   return block_changed;
 }
@@ -873,6 +897,7 @@ dce_process_block (basic_block bb, bool 
   rtx insn;
   bool block_changed;
   df_ref *def_rec;
+  struct dead_debug debug;
 
   if (redo_out)
     {
@@ -896,22 +921,36 @@ dce_process_block (basic_block bb, bool 
   bitmap_copy (local_live, DF_LR_OUT (bb));
 
   df_simulate_initialize_backwards (bb, local_live);
+  dead_debug_init (&debug, NULL);
 
   FOR_BB_INSNS_REVERSE (bb, insn)
-    if (INSN_P (insn))
+    if (DEBUG_INSN_P (insn))
+      {
+	df_ref *use_rec;
+	for (use_rec = DF_INSN_USES (insn); *use_rec; use_rec++)
+	  if (!bitmap_bit_p (local_live, DF_REF_REGNO (*use_rec))
+	      && !bitmap_bit_p (au, DF_REF_REGNO (*use_rec)))
+	    dead_debug_add (&debug, *use_rec, DF_REF_REGNO (*use_rec));
+      }
+    else if (INSN_P (insn))
       {
 	bool needed = marked_insn_p (insn);
 
 	/* The insn is needed if there is someone who uses the output.  */
 	if (!needed)
 	  for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++)
-	    if (bitmap_bit_p (local_live, DF_REF_REGNO (*def_rec))
-		|| bitmap_bit_p (au, DF_REF_REGNO (*def_rec)))
-	      {
-		needed = true;
-		mark_insn (insn, true);
-		break;
-	      }
+	    {
+	      dead_debug_insert_temp (&debug, DF_REF_REGNO (*def_rec), insn,
+				      DEBUG_TEMP_BEFORE_WITH_VALUE);
+
+	      if (bitmap_bit_p (local_live, DF_REF_REGNO (*def_rec))
+		  || bitmap_bit_p (au, DF_REF_REGNO (*def_rec)))
+		{
+		  needed = true;
+		  mark_insn (insn, true);
+		  break;
+		}
+	    }
 
 	/* No matter if the instruction is needed or not, we remove
 	   any regno in the defs from the live set.  */
@@ -923,6 +962,7 @@ dce_process_block (basic_block bb, bool 
 	  df_simulate_uses (insn, local_live);
       }
 
+  dead_debug_finish (&debug, NULL);
   df_simulate_finalize_backwards (bb, local_live);
 
   block_changed = !bitmap_equal_p (local_live, DF_LR_IN (bb));

-- 
Alexandre Oliva, freedom fighter    http://FSFLA.org/~lxoliva/
You must be the change you wish to see in the world. -- Gandhi
Be Free! -- http://FSFLA.org/   FSF Latin America board member
Free Software Evangelist      Red Hat Brazil Compiler Engineer

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]