[PATCH] Fix libcall_dead_p (PR middle-end/19551)

Jakub Jelinek jakub@redhat.com
Fri Jan 21 21:34:00 GMT 2005


Hi!

libcall_dead_p is too optimistic in its decisions what libcalls are
actually dead.  The attached testcase shows several things that it gets
wrong on at least i386, x86_64, ia64, ppc and ppc64 (but not on e.g.
s390/s390x).
E.g. in:
(call_insn/u 17 16 18 0 (set (reg:SC 0 ax)
        (call (mem:QI (symbol_ref:SI ("foo_float") [flags 0x3] <function_decl 0x2a97cb7750 foo_float>) [0 S1 A8])
            (const_int 4 [0x4]))) -1 (nil)
    (expr_list:REG_EH_REGION (const_int -1 [0xffffffffffffffff])
        (nil))
    (expr_list (use (mem:BLK (scratch) [0 A8]))
        (nil)))

(insn 18 17 19 0 (set (reg:SF 63)
        (reg:SF 0 ax)) 91 {*movsf_1_nointerunit} (nil)
    (nil))

(insn 19 18 25 0 (set (reg:SF 64 [+4 ])
        (reg:SF 1 dx [+4 ])) 91 {*movsf_1_nointerunit} (nil)
    (insn_list:REG_RETVAL 16 (nil)))

(insn 25 19 27 0 (set (mem:SF (reg/v/f:SI 58 [ x ]) [3 S4 A32])
        (reg:SF 63)) 91 {*movsf_1_nointerunit} (nil)
    (nil))
;; End of basic block 0, registers live:
 (nil)

(note 27 25 0 NOTE_INSN_FUNCTION_END)
insn 19 is dead and ax is not live at the end either, so libcall_dead_p
decides the whole libcall is dead, while reg 63 is actually live.
The attached patch seems to fix this.
On 3.4 branch it causes some really dead libcalls not to be deleted
on at least i386, x86_64 and ia64 while they used to be previously
optimized out, but it seems to do that only for a subset of the cases
that it got wrong if the libcalls are actually used and it deleted
them anyway.  Particularly for CQI, CHI and on 64-bit CSI returns,
where the libcall's value is stored into memory and then read from
it again.  On HEAD this shouldn't be a problem, as tree-SSA kills
the dead calls far earlier.

Ok to commit? HEAD and 3.4?

	Jakub
-------------- next part --------------
2005-01-21  Jakub Jelinek  <jakub@redhat.com>

	* flow.c (propagate_one_insn): Formatting.

	PR middle-end/19551
	* flow.c (libcall_dead_p): Be more conservative if unsure.
	If there are any instructions between insn and call, see if they are
	all dead before saying the libcall is dead.

	* gcc.c-torture/execute/20050121-1.c: New test.
	* gcc.dg/20050121-2.c: New test.

--- gcc/flow.c.jj	2005-01-18 17:02:41.000000000 +0100
+++ gcc/flow.c	2005-01-21 19:46:30.000000000 +0100
@@ -1587,7 +1587,7 @@ propagate_one_insn (struct propagate_blo
       pbi->cc0_live = 0;
 
       if (libcall_is_dead)
-	prev = propagate_block_delete_libcall ( insn, note);
+	prev = propagate_block_delete_libcall (insn, note);
       else
 	{
 
@@ -2290,7 +2290,7 @@ libcall_dead_p (struct propagate_block_i
     {
       rtx r = SET_SRC (x);
 
-      if (REG_P (r))
+      if (REG_P (r) || GET_CODE (r) == SUBREG)
 	{
 	  rtx call = XEXP (note, 0);
 	  rtx call_pat;
@@ -2324,10 +2324,20 @@ libcall_dead_p (struct propagate_block_i
 	      call_pat = XVECEXP (call_pat, 0, i);
 	    }
 
-	  return insn_dead_p (pbi, call_pat, 1, REG_NOTES (call));
+	  if (! insn_dead_p (pbi, call_pat, 1, REG_NOTES (call)))
+	    return 0;
+
+	  while ((insn = PREV_INSN (insn)) != call)
+	    {
+	      if (! INSN_P (insn))
+		continue;
+	      if (! insn_dead_p (pbi, PATTERN (insn), 0, REG_NOTES (insn)))
+		return 0;
+	    }
+	  return 1;
 	}
     }
-  return 1;
+  return 0;
 }
 
 /* 1 if register REGNO was alive at a place where `setjmp' was called
--- gcc/testsuite/gcc.c-torture/execute/20050121-1.c.jj	2005-01-21 19:53:52.000000000 +0100
+++ gcc/testsuite/gcc.c-torture/execute/20050121-1.c	2005-01-21 19:49:45.000000000 +0100
@@ -0,0 +1,63 @@
+/* PR middle-end/19551 */
+
+extern void abort ();
+
+#define T(type, name) \
+__attribute__((pure)) _Complex type		\
+foo_##name (int x)				\
+{						\
+  _Complex type r;				\
+  __real r = x + 1;				\
+  __imag r = x - 1;				\
+  return r;					\
+}						\
+						\
+void						\
+bar_##name (type *x)				\
+{						\
+  *x = __real foo_##name (5);			\
+}						\
+						\
+void						\
+baz_##name (type *x)				\
+{						\
+  *x = __imag foo_##name (5);			\
+}
+
+typedef long double ldouble_t;
+typedef long long llong;
+
+T (float, float)
+T (double, double)
+T (long double, ldouble_t)
+T (char, char)
+T (short, short)
+T (int, int)
+T (long, long)
+T (long long, llong)
+#undef T
+
+int
+main (void)
+{
+#define T(type, name) \
+  {						\
+    type var = 0;				\
+    bar_##name (&var);				\
+    if (var != 6)				\
+      abort ();					\
+    var = 0;					\
+    baz_##name (&var);				\
+    if (var != 4)				\
+      abort ();					\
+  }
+  T (float, float)
+  T (double, double)
+  T (long double, ldouble_t)
+  T (char, char)
+  T (short, short)
+  T (int, int)
+  T (long, long)
+  T (long long, llong)
+  return 0;
+}
--- gcc/testsuite/gcc.dg/20050121-2.c.jj	2005-01-21 22:18:43.433594254 +0100
+++ gcc/testsuite/gcc.dg/20050121-2.c	2005-01-21 21:52:05.000000000 +0100
@@ -0,0 +1,67 @@
+/* PR middle-end/19551 */
+/* { dg-options "-O2" } */
+/* { dg-do link } */
+
+extern void abort ();
+
+#define T(type, name) \
+extern __attribute__((pure)) _Complex type	\
+foo_c##name (int x);				\
+						\
+void						\
+bar_c##name (type *x)				\
+{						\
+  type f = __real foo_c##name (5);		\
+  if (0) *x = f;				\
+}						\
+						\
+void						\
+baz_c##name (type *x)				\
+{						\
+  type f = __imag foo_c##name (5);		\
+  if (0) *x = f;				\
+}						\
+						\
+extern __attribute__((pure)) type		\
+foo_##name (int x);				\
+						\
+void						\
+bar_##name (type *x)				\
+{						\
+  type f = foo_##name (5);			\
+  if (0) *x = f;				\
+}
+
+typedef long double ldouble_t;
+typedef long long llong;
+
+T (float, float)
+T (double, double)
+T (long double, ldouble_t)
+T (char, char)
+T (short, short)
+T (int, int)
+T (long, long)
+T (long long, llong)
+#undef T
+
+int
+main (void)
+{
+#define T(type, name) \
+  {						\
+    type var = 0;				\
+    bar_c##name (&var);				\
+    baz_c##name (&var);				\
+    bar_##name (&var);				\
+  }
+  T (float, float)
+  T (double, double)
+  T (long double, ldouble_t)
+  T (char, char)
+  T (short, short)
+  T (int, int)
+  T (long, long)
+  T (long long, llong)
+  return 0;
+}
-------------- next part --------------
2005-01-21  Jakub Jelinek  <jakub@redhat.com>

	* flow.c (propagate_one_insn): Formatting.

	PR middle-end/19551
	* flow.c (libcall_dead_p): Be more conservative if unsure.
	If there are any instructions between insn and call, see if they are
	all dead before saying the libcall is dead.

	* gcc.c-torture/execute/20050121-1.c: New test.

--- gcc/flow.c.jj	2005-01-18 17:02:41.000000000 +0100
+++ gcc/flow.c	2005-01-21 19:46:30.000000000 +0100
@@ -1591,7 +1591,7 @@ propagate_one_insn (struct propagate_blo
       pbi->cc0_live = 0;
 
       if (libcall_is_dead)
-	prev = propagate_block_delete_libcall ( insn, note);
+	prev = propagate_block_delete_libcall (insn, note);
       else
 	{
 
@@ -2268,7 +2268,7 @@ libcall_dead_p (struct propagate_block_i
     {
       rtx r = SET_SRC (x);
 
-      if (GET_CODE (r) == REG)
+      if (GET_CODE (r) == REG || GET_CODE (r) == SUBREG)
 	{
 	  rtx call = XEXP (note, 0);
 	  rtx call_pat;
@@ -2302,10 +2302,20 @@ libcall_dead_p (struct propagate_block_i
 	      call_pat = XVECEXP (call_pat, 0, i);
 	    }
 
-	  return insn_dead_p (pbi, call_pat, 1, REG_NOTES (call));
+	  if (! insn_dead_p (pbi, call_pat, 1, REG_NOTES (call)))
+	    return 0;
+
+	  while ((insn = PREV_INSN (insn)) != call)
+	    {
+	      if (! INSN_P (insn))
+		continue;
+	      if (! insn_dead_p (pbi, PATTERN (insn), 0, REG_NOTES (insn)))
+		return 0;
+	    }
+	  return 1;
 	}
     }
-  return 1;
+  return 0;
 }
 
 /* Return 1 if register REGNO was used before it was set, i.e. if it is
--- gcc/testsuite/gcc.c-torture/execute/20050121-1.c.jj	2005-01-21 19:53:52.000000000 +0100
+++ gcc/testsuite/gcc.c-torture/execute/20050121-1.c	2005-01-21 19:49:45.000000000 +0100
@@ -0,0 +1,63 @@
+/* PR middle-end/19551 */
+
+extern void abort ();
+
+#define T(type, name) \
+__attribute__((pure)) _Complex type		\
+foo_##name (int x)				\
+{						\
+  _Complex type r;				\
+  __real r = x + 1;				\
+  __imag r = x - 1;				\
+  return r;					\
+}						\
+						\
+void						\
+bar_##name (type *x)				\
+{						\
+  *x = __real foo_##name (5);			\
+}						\
+						\
+void						\
+baz_##name (type *x)				\
+{						\
+  *x = __imag foo_##name (5);			\
+}
+
+typedef long double ldouble_t;
+typedef long long llong;
+
+T (float, float)
+T (double, double)
+T (long double, ldouble_t)
+T (char, char)
+T (short, short)
+T (int, int)
+T (long, long)
+T (long long, llong)
+#undef T
+
+int
+main (void)
+{
+#define T(type, name) \
+  {						\
+    type var = 0;				\
+    bar_##name (&var);				\
+    if (var != 6)				\
+      abort ();					\
+    var = 0;					\
+    baz_##name (&var);				\
+    if (var != 4)				\
+      abort ();					\
+  }
+  T (float, float)
+  T (double, double)
+  T (long double, ldouble_t)
+  T (char, char)
+  T (short, short)
+  T (int, int)
+  T (long, long)
+  T (long long, llong)
+  return 0;
+}
y


More information about the Gcc-patches mailing list