diff mbox

Fix VTA updating in the combiner (PR debug/48343)

Message ID 20110330195042.GS18914@tyan-ft48-01.lab.bos.redhat.com
State New
Headers show

Commit Message

Jakub Jelinek March 30, 2011, 7:50 p.m. UTC
Hi!

We ICE on the attached testcase, because combiner changes mode of
a pseudo (which was set just once and used once plus in debug insns)
from SImode to QImode, but the uses in debug insns aren't adjusted.

Combiner has code to adjust this too (propagate_for_debug), but only updates
debug insns between i2 and i3 (resp. i3 and undobuf.other_insn).
The problem on the testcase is that this is a retry, so first
try_combine with a later i3 calls propagate_for_debug and changes debug
insns before that later i3, then returns an earlier insn that should be
retried and we stop adjusting debug insns at that earlier i3.  Unfortunately
as later debug insns have been already updated earlier, they need to be
adjusted too.

The following patch fixes that by always stopping on the latest i3 that has
been successfully combined into in the current bb, bootstrapped/regtested
on x86_64-linux and i686-linux, ok for trunk/4.6?

2011-03-30  Jakub Jelinek  <jakub@redhat.com>

	PR debug/48343
	* combine.c (last_combined_insn): New variable.
	(combine_instructions): Clear it before processing each bb
	and after last bb.
	(try_combine): Set it to i3 if i3 is after it.
	Use it as the last argument to propagate_for_debug instead
	of i3.
	(distribute_notes): Assert SET_INSN_DELETE is not called
	on last_combined_insn.

	* gcc.dg/torture/pr48343.c: New test.


	Jakub

Comments

Eric Botcazou April 2, 2011, 3:42 p.m. UTC | #1
> Combiner has code to adjust this too (propagate_for_debug), but only
> updates debug insns between i2 and i3 (resp. i3 and undobuf.other_insn).
> The problem on the testcase is that this is a retry, so first
> try_combine with a later i3 calls propagate_for_debug and changes debug
> insns before that later i3, then returns an earlier insn that should be
> retried and we stop adjusting debug insns at that earlier i3. 
> Unfortunately as later debug insns have been already updated earlier, they
> need to be adjusted too.
>
> The following patch fixes that by always stopping on the latest i3 that has
> been successfully combined into in the current bb, bootstrapped/regtested
> on x86_64-linux and i686-linux, ok for trunk/4.6?

This seems to be a rare problem though so I'm not sure we should directly go 
for an "always" approach.  IIUC this can happen only when try_combine returns 
a NEXT to combine_instructions which is before INSN in the stream, right?
Can't we precisely detect this case under the "retry:" label and pass an 
additional argument to try_combine?  Do we really need to adjust all the calls 
to propagate_for_debug and not just the ones made for UNDO_MODE when the 
register is I2DEST?
Jakub Jelinek April 2, 2011, 3:55 p.m. UTC | #2
On Sat, Apr 02, 2011 at 05:42:55PM +0200, Eric Botcazou wrote:
> > Combiner has code to adjust this too (propagate_for_debug), but only
> > updates debug insns between i2 and i3 (resp. i3 and undobuf.other_insn).
> > The problem on the testcase is that this is a retry, so first
> > try_combine with a later i3 calls propagate_for_debug and changes debug
> > insns before that later i3, then returns an earlier insn that should be
> > retried and we stop adjusting debug insns at that earlier i3. 
> > Unfortunately as later debug insns have been already updated earlier, they
> > need to be adjusted too.
> >
> > The following patch fixes that by always stopping on the latest i3 that has
> > been successfully combined into in the current bb, bootstrapped/regtested
> > on x86_64-linux and i686-linux, ok for trunk/4.6?
> 
> This seems to be a rare problem though so I'm not sure we should directly go 
> for an "always" approach.  IIUC this can happen only when try_combine returns 
> a NEXT to combine_instructions which is before INSN in the stream, right?

Yeah, but that is when in the patch i3 will differ from last_combined_insn.

> Can't we precisely detect this case under the "retry:" label and pass an 
> additional argument to try_combine?  Do we really need to adjust all the calls 

We'd still need to do the LUID checking anyway, to find out when we still do
retry earlier insns and when we return back to looking at new insns.
Plus, I believe whenever we propagate_for_debug after i3 (the updating in
between i3 and undobuf.other_insn, we need to adjust those insns too.

> to propagate_for_debug and not just the ones made for UNDO_MODE when the 
> register is I2DEST?

I think we need to update there in all cases.  The reason we don't need to
update beyond i3 resp. undobuf.other_insn is that DF guarantees us that
there won't be debug insns referring to those pseudos afterwards, otherwise
either the pseudo must be live afterwards in real code (then it wouldn't
be a single use case), or debug insns would be reset, or a debug temporary
would be created, where the temporary is set before last place where
the pseudo is used in real code.  Now, once we propagate_for_debug after
some insn, DF hasn't been run in between and thus the pseudos might be live
afterwards.

If you just want to avoid a global variable, the code can be surely changed
to have a local variable from combine_instructions and pass address to that
to all try_combine calls, but other than that I think we should do what the
patch does.
Alex, do you agree?

	Jakub
Eric Botcazou April 3, 2011, 9:39 a.m. UTC | #3
> I think we need to update there in all cases.  The reason we don't need to
> update beyond i3 resp. undobuf.other_insn is that DF guarantees us that
> there won't be debug insns referring to those pseudos afterwards, otherwise
> either the pseudo must be live afterwards in real code (then it wouldn't
> be a single use case), or debug insns would be reset, or a debug temporary
> would be created, where the temporary is set before last place where
> the pseudo is used in real code.  Now, once we propagate_for_debug after
> some insn, DF hasn't been run in between and thus the pseudos might be live
> afterwards.

Frankly moving down last_combined_insn to undobuf.other_insn in the UNDO_MODE 
case seems a little overengineered at this point.

> If you just want to avoid a global variable, the code can be surely changed
> to have a local variable from combine_instructions and pass address to that
> to all try_combine calls, but other than that I think we should do what the
> patch does.

I'd eliminate the global variable and directly pass the insn to try_combine, 
this is good enough for now IMO.
diff mbox

Patch

--- gcc/combine.c.jj	2011-03-23 09:34:40.000000000 +0100
+++ gcc/combine.c	2011-03-30 17:39:09.000000000 +0200
@@ -337,6 +337,9 @@  static enum machine_mode nonzero_bits_mo
 
 static int nonzero_sign_valid;
 
+/* Last insn on which try_combine has been called in the current BB.  */
+
+static rtx last_combined_insn;
 
 /* Record one modification to rtl structure
    to be undone by storing old_contents into *where.  */
@@ -1168,6 +1171,7 @@  combine_instructions (rtx f, unsigned in
 	  || single_pred (this_basic_block) != last_bb)
 	label_tick_ebb_start = label_tick;
       last_bb = this_basic_block;
+      last_combined_insn = NULL_RTX;
 
       rtl_profile_for_bb (this_basic_block);
       for (insn = BB_HEAD (this_basic_block);
@@ -1379,6 +1383,7 @@  combine_instructions (rtx f, unsigned in
 	}
     }
 
+  last_combined_insn = NULL_RTX;
   default_rtl_profile ();
   clear_log_links ();
   clear_bb_flags ();
@@ -3830,6 +3835,10 @@  try_combine (rtx i3, rtx i2, rtx i1, rtx
       return 0;
     }
 
+  if (last_combined_insn == NULL_RTX
+      || DF_INSN_LUID (last_combined_insn) < DF_INSN_LUID (i3))
+    last_combined_insn = i3;
+
   if (MAY_HAVE_DEBUG_INSNS)
     {
       struct undo *undo;
@@ -3851,7 +3860,7 @@  try_combine (rtx i3, rtx i2, rtx i1, rtx
 		   i2src while its original mode is temporarily
 		   restored, and then clear i2scratch so that we don't
 		   do it again later.  */
-		propagate_for_debug (i2, i3, reg, i2src);
+		propagate_for_debug (i2, last_combined_insn, reg, i2src);
 		i2scratch = false;
 		/* Put back the new mode.  */
 		adjust_reg_mode (reg, new_mode);
@@ -3859,18 +3868,17 @@  try_combine (rtx i3, rtx i2, rtx i1, rtx
 	    else
 	      {
 		rtx tempreg = gen_raw_REG (old_mode, REGNO (reg));
-		rtx first, last;
+		rtx first;
 
 		if (reg == i2dest)
-		  {
-		    first = i2;
-		    last = i3;
-		  }
+		  first = i2;
 		else
 		  {
 		    first = i3;
-		    last = undobuf.other_insn;
-		    gcc_assert (last);
+		    gcc_assert (undobuf.other_insn);
+		    if (DF_INSN_LUID (undobuf.other_insn)
+			> DF_INSN_LUID (last_combined_insn))
+		      last_combined_insn = undobuf.other_insn;
 		  }
 
 		/* We're dealing with a reg that changed mode but not
@@ -3882,9 +3890,9 @@  try_combine (rtx i3, rtx i2, rtx i1, rtx
 		   with this copy we have created; then, replace the
 		   copy with the SUBREG of the original shared reg,
 		   once again changed to the new mode.  */
-		propagate_for_debug (first, last, reg, tempreg);
+		propagate_for_debug (first, last_combined_insn, reg, tempreg);
 		adjust_reg_mode (reg, new_mode);
-		propagate_for_debug (first, last, tempreg,
+		propagate_for_debug (first, last_combined_insn, tempreg,
 				     lowpart_subreg (old_mode, reg, new_mode));
 	      }
 	  }
@@ -4099,14 +4107,14 @@  try_combine (rtx i3, rtx i2, rtx i1, rtx
     if (newi2pat)
       {
 	if (MAY_HAVE_DEBUG_INSNS && i2scratch)
-	  propagate_for_debug (i2, i3, i2dest, i2src);
+	  propagate_for_debug (i2, last_combined_insn, i2dest, i2src);
 	INSN_CODE (i2) = i2_code_number;
 	PATTERN (i2) = newi2pat;
       }
     else
       {
 	if (MAY_HAVE_DEBUG_INSNS && i2src)
-	  propagate_for_debug (i2, i3, i2dest, i2src);
+	  propagate_for_debug (i2, last_combined_insn, i2dest, i2src);
 	SET_INSN_DELETED (i2);
       }
 
@@ -4115,7 +4123,7 @@  try_combine (rtx i3, rtx i2, rtx i1, rtx
 	LOG_LINKS (i1) = 0;
 	REG_NOTES (i1) = 0;
 	if (MAY_HAVE_DEBUG_INSNS)
-	  propagate_for_debug (i1, i3, i1dest, i1src);
+	  propagate_for_debug (i1, last_combined_insn, i1dest, i1src);
 	SET_INSN_DELETED (i1);
       }
 
@@ -4124,7 +4132,7 @@  try_combine (rtx i3, rtx i2, rtx i1, rtx
 	LOG_LINKS (i0) = 0;
 	REG_NOTES (i0) = 0;
 	if (MAY_HAVE_DEBUG_INSNS)
-	  propagate_for_debug (i0, i3, i0dest, i0src);
+	  propagate_for_debug (i0, last_combined_insn, i0dest, i0src);
 	SET_INSN_DELETED (i0);
       }
 
@@ -13454,6 +13462,7 @@  distribute_notes (rtx notes, rtx from_in
 					    NULL_RTX, NULL_RTX, NULL_RTX);
 			  distribute_links (LOG_LINKS (tem));
 
+			  gcc_assert (tem != last_combined_insn);
 			  SET_INSN_DELETED (tem);
 			  if (tem == i2)
 			    i2 = NULL_RTX;
@@ -13471,6 +13480,7 @@  distribute_notes (rtx notes, rtx from_in
 						NULL_RTX, NULL_RTX, NULL_RTX);
 			      distribute_links (LOG_LINKS (cc0_setter));
 
+			      gcc_assert (cc0_setter != last_combined_insn);
 			      SET_INSN_DELETED (cc0_setter);
 			      if (cc0_setter == i2)
 				i2 = NULL_RTX;
--- gcc/testsuite/gcc.dg/torture/pr48343.c.jj	2011-03-30 17:41:51.000000000 +0200
+++ gcc/testsuite/gcc.dg/torture/pr48343.c	2011-03-30 17:41:32.000000000 +0200
@@ -0,0 +1,19 @@ 
+/* PR debug/48343 */
+/* { dg-do compile } */
+/* { dg-options "-fcompare-debug" } */
+
+void foo (unsigned char *, unsigned char *);
+
+void
+test (unsigned int x, int y)
+{
+  unsigned int i, j = 0, k;
+  unsigned char s[256], t[64];
+  foo (s, t);
+  t[0] = y;
+  for (i = 0; i < 256; i++)
+    {
+      j = (j + s[i] + t[i % x]) & 0xff;
+      k = i; i = j; j = k;
+    }
+}