Patchwork Fix PR rtl-optimization/46315

login
register
mail settings
Submitter Eric Botcazou
Date Nov. 16, 2010, 10:16 p.m.
Message ID <201011162316.37064.ebotcazou@adacore.com>
Download mbox | patch
Permalink /patch/71464/
State New
Headers show

Comments

Eric Botcazou - Nov. 16, 2010, 10:16 p.m.
This is a regression with -O2 -fno-strict-overflow present on the 4.5 branch.
The compiler generates wrong code because of a problematic transformation done 
by if-convert.  REG_EQUAL notes aren't taken into account to compute liveness 
info so when insns are moved above a conditional branch in the transformation

   (2)
	if (test) goto E; // x not live
	x = big();
	goto L;
	E:
	x = b;
	goto M;

   becomes

	x = b;
	if (test) goto M;
	x = big();
	goto L;

they can change the reaching def of a REG_EQUAL note referring to x in the 
big() part.  A related problem was fixed by Joern in PR rtl-opt/21767, but 
this was for the REG_EQUAL notes attached to the insns being moved.

Fixed by removing the REG_EQUAL notes referring to registers set in the insns 
being moved; these registers are already computed during the transformation.
The patch also fixes inconsistencies in the dumping routines of DF, where uses 
in REG_EQUAL notes were sometimes printed as 'e' and sometimes as 'u'; they 
should now be printed as 'e' by all routines.

Tested on {x86_64,i586}-suse-linux, applied on the mainline and 4.5 branch.


2010-11-16  Eric Botcazou  <ebotcazou@adacore.com>

	PR rtl-optimization/46315
	* rtl.h (remove_reg_equal_equiv_notes_for_regno): Declare.
	* rtlanal.c (remove_reg_equal_equiv_notes_for_regno): New function
	extracted from...
	* dce.c (delete_corresponding_reg_eq_notes): ...here.  Rename into...
	(remove_reg_equal_equiv_notes_for_defs): ...this.
	(delete_unmarked_insns): Adjust to above renaming.
	* ifcvt.c (dead_or_predicable): Remove REG_EQUAL and REG_EQUIV notes
	referring to registers set in the insns being moved, if any.

	* df-core.c (df_ref_dump): New function extracted from...
	(df_refs_chain_dump): ...here.  Call it.
	(df_regs_chain_dump): Likewise.
	* df-problems.c (df_chain_dump): Print 'e' for uses in notes.
	* df-scan.c (df_scan_start_dump): Likewise.  Fix long line.


2010-11-16  Eric Botcazou  <ebotcazou@adacore.com>

	* gcc.dg/pr46315.c: New test.

Patch

Index: rtlanal.c
===================================================================
--- rtlanal.c	(revision 166792)
+++ rtlanal.c	(working copy)
@@ -1941,6 +1941,33 @@  remove_reg_equal_equiv_notes (rtx insn)
     }
 }
 
+/* Remove all REG_EQUAL and REG_EQUIV notes referring to REGNO.  */
+
+void
+remove_reg_equal_equiv_notes_for_regno (unsigned int regno)
+{
+  df_ref eq_use;
+
+  if (!df)
+    return;
+
+  /* This loop is a little tricky.  We cannot just go down the chain because
+     it is being modified by some actions in the loop.  So we just iterate
+     over the head.  We plan to drain the list anyway.  */
+  while ((eq_use = DF_REG_EQ_USE_CHAIN (regno)) != NULL)
+    {
+      rtx insn = DF_REF_INSN (eq_use);
+      rtx note = find_reg_equal_equiv_note (insn);
+
+      /* This assert is generally triggered when someone deletes a REG_EQUAL
+	 or REG_EQUIV note by hacking the list manually rather than calling
+	 remove_note.  */
+      gcc_assert (note);
+
+      remove_note (insn, note);
+    }
+}
+
 /* Search LISTP (an EXPR_LIST) for an entry whose first operand is NODE and
    return 1 if it is found.  A simple equality test is used to determine if
    NODE matches.  */
Index: df-scan.c
===================================================================
--- df-scan.c	(revision 166792)
+++ df-scan.c	(working copy)
@@ -445,7 +445,7 @@  df_scan_start_dump (FILE *file ATTRIBUTE
 	  }
 	if (DF_REG_EQ_USE_COUNT (i))
 	  {
-	    fprintf (file, "%s%dd", sep, DF_REG_EQ_USE_COUNT (i));
+	    fprintf (file, "%s%de", sep, DF_REG_EQ_USE_COUNT (i));
 	    ecount += DF_REG_EQ_USE_COUNT (i);
 	  }
 	fprintf (file, "} ");
@@ -461,8 +461,10 @@  df_scan_start_dump (FILE *file ATTRIBUTE
 	    icount++;
 	}
 
-  fprintf (file, "\n;;    total ref usage %d{%dd,%du,%de} in %d{%d regular + %d call} insns.\n",
-	   dcount + ucount + ecount, dcount, ucount, ecount, icount + ccount, icount, ccount);
+  fprintf (file, "\n;;    total ref usage %d{%dd,%du,%de}"
+		 " in %d{%d regular + %d call} insns.\n",
+		 dcount + ucount + ecount, dcount, ucount, ecount,
+		 icount + ccount, icount, ccount);
 }
 
 /* Dump the bb_info for a given basic block. */
Index: df-core.c
===================================================================
--- df-core.c	(revision 166792)
+++ df-core.c	(working copy)
@@ -2051,6 +2051,17 @@  df_dump_bottom (basic_block bb, FILE *fi
 }
 
 
+static void
+df_ref_dump (df_ref ref, FILE *file)
+{
+  fprintf (file, "%c%d(%d)",
+	   DF_REF_REG_DEF_P (ref)
+	   ? 'd'
+	   : (DF_REF_FLAGS (ref) & DF_REF_IN_NOTE) ? 'e' : 'u',
+	   DF_REF_ID (ref),
+	   DF_REF_REGNO (ref));
+}
+
 void
 df_refs_chain_dump (df_ref *ref_rec, bool follow_chain, FILE *file)
 {
@@ -2058,10 +2069,7 @@  df_refs_chain_dump (df_ref *ref_rec, boo
   while (*ref_rec)
     {
       df_ref ref = *ref_rec;
-      fprintf (file, "%c%d(%d)",
-	       DF_REF_REG_DEF_P (ref) ? 'd' : (DF_REF_FLAGS (ref) & DF_REF_IN_NOTE) ? 'e' : 'u',
-	       DF_REF_ID (ref),
-	       DF_REF_REGNO (ref));
+      df_ref_dump (ref, file);
       if (follow_chain)
 	df_chain_dump (DF_REF_CHAIN (ref), file);
       ref_rec++;
@@ -2078,10 +2086,7 @@  df_regs_chain_dump (df_ref ref,  FILE *f
   fprintf (file, "{ ");
   while (ref)
     {
-      fprintf (file, "%c%d(%d) ",
-	       DF_REF_REG_DEF_P (ref) ? 'd' : 'u',
-	       DF_REF_ID (ref),
-	       DF_REF_REGNO (ref));
+      df_ref_dump (ref, file);
       ref = DF_REF_NEXT_REG (ref);
     }
   fprintf (file, "}");
Index: ifcvt.c
===================================================================
--- ifcvt.c	(revision 166792)
+++ ifcvt.c	(working copy)
@@ -3998,6 +3998,7 @@  dead_or_predicable (basic_block test_bb,
 		    basic_block other_bb, basic_block new_dest, int reversep)
 {
   rtx head, end, jump, earliest = NULL_RTX, old_dest, new_label = NULL_RTX;
+  bitmap merge_set = NULL, merge_set_noclobber = NULL;
   /* Number of pending changes.  */
   int n_validated_changes = 0;
 
@@ -4086,6 +4087,7 @@  dead_or_predicable (basic_block test_bb,
       earliest = jump;
     }
 #endif
+
   /* Try the NCE path if the CE path did not result in any changes.  */
   if (n_validated_changes == 0)
     {
@@ -4094,9 +4096,8 @@  dead_or_predicable (basic_block test_bb,
 	 that any registers modified are dead at the branch site.  */
 
       rtx insn, cond, prev;
-      bitmap merge_set, merge_set_noclobber, test_live, test_set;
-      unsigned i, fail = 0;
-      bitmap_iterator bi;
+      bitmap test_live, test_set;
+      bool intersect = false;
 
       /* Check for no calls or trapping operations.  */
       for (insn = head; ; insn = NEXT_INSN (insn))
@@ -4138,12 +4139,7 @@  dead_or_predicable (basic_block test_bb,
 
       merge_set = BITMAP_ALLOC (&reg_obstack);
       merge_set_noclobber = BITMAP_ALLOC (&reg_obstack);
-      test_live = BITMAP_ALLOC (&reg_obstack);
-      test_set = BITMAP_ALLOC (&reg_obstack);
 
-      /* ??? bb->local_set is only valid during calculate_global_regs_live,
-	 so we must recompute usage for MERGE_BB.  Not so bad, I suppose,
-         since we've already asserted that MERGE_BB is small.  */
       /* If we allocated new pseudos (e.g. in the conditional move
 	 expander called from noce_emit_cmove), we must resize the
 	 array first.  */
@@ -4164,17 +4160,22 @@  dead_or_predicable (basic_block test_bb,
       if (! reload_completed
 	  && targetm.small_register_classes_for_mode_p (VOIDmode))
 	{
+	  unsigned i;
+	  bitmap_iterator bi;
+
           EXECUTE_IF_SET_IN_BITMAP (merge_set_noclobber, 0, i, bi)
 	    {
 	      if (i < FIRST_PSEUDO_REGISTER
 		  && ! fixed_regs[i]
 		  && ! global_regs[i])
-		fail = 1;
+		goto fail;
 	    }
 	}
 
       /* For TEST, we're interested in a range of insns, not a whole block.
 	 Moreover, we're interested in the insns live from OTHER_BB.  */
+      test_live = BITMAP_ALLOC (&reg_obstack);
+      test_set = BITMAP_ALLOC (&reg_obstack);
 
       /* The loop below takes the set of live registers
          after JUMP, and calculates the live set before EARLIEST. */
@@ -4195,23 +4196,21 @@  dead_or_predicable (basic_block test_bb,
       /* We can perform the transformation if
 	   MERGE_SET_NOCLOBBER & TEST_SET
 	 and
-	   MERGE_SET & TEST_LIVE)
+	   MERGE_SET & TEST_LIVE
 	 and
 	   TEST_SET & DF_LIVE_IN (merge_bb)
 	 are empty.  */
 
-      if (bitmap_intersect_p (test_set, merge_set_noclobber)
-	  || bitmap_intersect_p (test_live, merge_set)
+      if (bitmap_intersect_p (merge_set_noclobber, test_set)
+	  || bitmap_intersect_p (merge_set, test_live)
 	  || bitmap_intersect_p (test_set, df_get_live_in (merge_bb)))
-	fail = 1;
+	intersect = true;
 
-      BITMAP_FREE (merge_set_noclobber);
-      BITMAP_FREE (merge_set);
       BITMAP_FREE (test_live);
       BITMAP_FREE (test_set);
 
-      if (fail)
-	return FALSE;
+      if (intersect)
+	goto fail;
     }
 
  no_body:
@@ -4261,8 +4260,8 @@  dead_or_predicable (basic_block test_bb,
       if (end == BB_END (merge_bb))
 	BB_END (merge_bb) = PREV_INSN (head);
 
-      /* PR 21767: When moving insns above a conditional branch, REG_EQUAL
-	 notes might become invalid.  */
+      /* PR 21767: when moving insns above a conditional branch, the REG_EQUAL
+	 notes being moved might become invalid.  */
       insn = head;
       do
 	{
@@ -4279,6 +4278,20 @@  dead_or_predicable (basic_block test_bb,
 	    remove_note (insn, note);
 	} while (insn != end && (insn = NEXT_INSN (insn)));
 
+      /* PR46315: when moving insns above a conditional branch, the REG_EQUAL
+	 notes referring to the registers being set might become invalid.  */
+      if (merge_set)
+	{
+	  unsigned i;
+	  bitmap_iterator bi;
+
+	  EXECUTE_IF_SET_IN_BITMAP (merge_set_noclobber, 0, i, bi)
+	    remove_reg_equal_equiv_notes_for_regno (i);
+
+	  BITMAP_FREE (merge_set);
+	  BITMAP_FREE (merge_set_noclobber);
+	}
+
       reorder_insns (head, end, PREV_INSN (earliest));
     }
 
@@ -4295,6 +4308,12 @@  dead_or_predicable (basic_block test_bb,
 
  cancel:
   cancel_changes (0);
+ fail:
+  if (merge_set)
+    {
+      BITMAP_FREE (merge_set);
+      BITMAP_FREE (merge_set_noclobber);
+    }
   return FALSE;
 }
 
Index: rtl.h
===================================================================
--- rtl.h	(revision 166792)
+++ rtl.h	(working copy)
@@ -1892,6 +1892,7 @@  extern rtx alloc_reg_note (enum reg_note
 extern void add_reg_note (rtx, enum reg_note, rtx);
 extern void remove_note (rtx, const_rtx);
 extern void remove_reg_equal_equiv_notes (rtx);
+extern void remove_reg_equal_equiv_notes_for_regno (unsigned int);
 extern int side_effects_p (const_rtx);
 extern int volatile_refs_p (const_rtx);
 extern int volatile_insn_p (const_rtx);
Index: df-problems.c
===================================================================
--- df-problems.c	(revision 166792)
+++ df-problems.c	(working copy)
@@ -109,10 +109,13 @@  df_chain_dump (struct df_link *link, FIL
   for (; link; link = link->next)
     {
       fprintf (file, "%c%d(bb %d insn %d) ",
-	       DF_REF_REG_DEF_P (link->ref) ? 'd' : 'u',
+	       DF_REF_REG_DEF_P (link->ref)
+	       ? 'd'
+	       : (DF_REF_FLAGS (link->ref) & DF_REF_IN_NOTE) ? 'e' : 'u',
 	       DF_REF_ID (link->ref),
 	       DF_REF_BBNO (link->ref),
-	       DF_REF_IS_ARTIFICIAL (link->ref) ? -1 : DF_REF_INSN_UID (link->ref));
+	       DF_REF_IS_ARTIFICIAL (link->ref)
+	       ? -1 : DF_REF_INSN_UID (link->ref));
     }
   fprintf (file, "}");
 }
Index: dce.c
===================================================================
--- dce.c	(revision 166792)
+++ dce.c	(working copy)
@@ -466,36 +466,16 @@  find_call_stack_args (rtx call_insn, boo
 }
 
 
-/* Delete all REG_EQUAL notes of the registers INSN writes, to prevent
-   bad dangling REG_EQUAL notes. */
+/* Remove all REG_EQUAL and REG_EQUIV notes referring to the registers INSN
+   writes to.  */
 
 static void
-delete_corresponding_reg_eq_notes (rtx insn)
+remove_reg_equal_equiv_notes_for_defs (rtx insn)
 {
   df_ref *def_rec;
+
   for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++)
-    {
-      df_ref def = *def_rec;
-      unsigned int regno = DF_REF_REGNO (def);
-      /* This loop is a little tricky.  We cannot just go down the
-	 chain because it is being modified by the actions in the
-	 loop.  So we just get the head.  We plan to drain the list
-	 anyway.  */
-      while (DF_REG_EQ_USE_CHAIN (regno))
-	{
-	  df_ref eq_use = DF_REG_EQ_USE_CHAIN (regno);
-	  rtx noted_insn = DF_REF_INSN (eq_use);
-	  rtx note = find_reg_note (noted_insn, REG_EQUAL, NULL_RTX);
-	  if (!note)
-	    note = find_reg_note (noted_insn, REG_EQUIV, NULL_RTX);
-
-	  /* This assert is generally triggered when someone deletes a
-	     REG_EQUAL or REG_EQUIV note by hacking the list manually
-	     rather than calling remove_note.  */
-	  gcc_assert (note);
-	  remove_note (noted_insn, note);
-	}
-    }
+    remove_reg_equal_equiv_notes_for_regno (DF_REF_REGNO (*def_rec));
 }
 
 
@@ -544,9 +524,9 @@  delete_unmarked_insns (void)
 	  if (dump_file)
 	    fprintf (dump_file, "DCE: Deleting insn %d\n", INSN_UID (insn));
 
-	  /* Before we delete the insn we have to delete REG_EQUAL notes
+	  /* Before we delete the insn we have to remove the REG_EQUAL notes
 	     for the destination regs in order to avoid dangling notes.  */
-	  delete_corresponding_reg_eq_notes (insn);
+	  remove_reg_equal_equiv_notes_for_defs (insn);
 
 	  /* If a pure or const call is deleted, this may make the cfg
 	     have unreachable blocks.  We rememeber this and call