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]

[PATCH] Don't caller-save pseudos across calls that may throw (PR rtl-optimization/23478)


Hi!

The testcase below is miscompiled on gcc-3_4-branch (but a different
one could very well be miscompiled by 4.0 or HEAD).  The problem is
that if global alloc decides to put a pseudo that crosses a call
that can throw into a call used register, caller-save.c will save
it to the stack and restore it after the call, but not on the
EH edge as well.  For non-critical edge we perhaps could put it
into the EH pad, but for critical abnormal edge we can't.

The following patch avoids putting pseudos into call used registers
if they cross can_throw_internal calls (similarly how it avoids
putting pseudos into call used regs if nonlocal label is present
and they cross any calls).

Ok to commit? 4.0/3.4 as well (say after a week or two living on HEAD)?

2005-08-22  Jakub Jelinek  <jakub@redhat.com>

	PR rtl-optimization/23478
	* regs.h (reg_info): Add throw_calls_crossed.
	(REG_N_THROWING_CALLS_CROSSED): Define.
	* flow.c (allocate_reg_life_data): Initialize
	REG_N_THROWING_CALLS_CROSSED.
	(propagate_one_insn, attempt_auto_inc): Update
	REG_N_THROWING_CALLS_CROSSED.
	* global.c (global_alloc): Don't allocate pseudos across
	calls that may throw.

	* g++.dg/opt/pr23478.C: New test.

--- gcc/regs.h.jj	2005-07-02 02:27:21.000000000 +0200
+++ gcc/regs.h	2005-08-22 16:29:30.000000000 +0200
@@ -61,6 +61,7 @@ typedef struct reg_info_def
   int deaths;			/* # of times (REG n) dies */
   int live_length;		/* # of instructions (REG n) is live */
   int calls_crossed;		/* # of calls (REG n) is live across */
+  int throw_calls_crossed;	/* # of calls that may throw (REG n) is live across */
   int basic_block;		/* # of basic blocks (REG n) is used in */
 } reg_info;
 
@@ -125,6 +126,12 @@ extern varray_type reg_n_info;
 
 #define REG_N_CALLS_CROSSED(N) (VARRAY_REG (reg_n_info, N)->calls_crossed)
 
+/* Indexed by N, gives number of CALL_INSNS that may throw, across which
+   (REG n) is live.  */
+
+#define REG_N_THROWING_CALLS_CROSSED(N) \
+  (VARRAY_REG (reg_n_info, N)->throw_calls_crossed)
+
 /* Total number of instructions at which (REG n) is live.
    The larger this is, the less priority (REG n) gets for
    allocation in a hard register (in global-alloc).
--- gcc/global.c.jj	2005-07-07 13:04:09.000000000 +0200
+++ gcc/global.c	2005-08-22 16:37:39.000000000 +0200
@@ -465,7 +465,9 @@ global_alloc (FILE *file)
 	/* Don't allocate pseudos that cross calls,
 	   if this function receives a nonlocal goto.  */
 	&& (! current_function_has_nonlocal_label
-	    || REG_N_CALLS_CROSSED (i) == 0))
+	    || REG_N_CALLS_CROSSED (i) == 0)
+	/* Don't allocate pseudos that cross calls that may throw.  */
+	&& REG_N_THROWING_CALLS_CROSSED (i) == 0)
       {
 	if (reg_renumber[i] < 0
 	    && reg_may_share[i] && reg_allocno[reg_may_share[i]] >= 0)
--- gcc/flow.c.jj	2005-08-16 16:23:58.000000000 +0200
+++ gcc/flow.c	2005-08-22 16:32:31.000000000 +0200
@@ -104,7 +104,7 @@ Software Foundation, 51 Franklin Street,
 
    life_analysis fills in certain vectors containing information about
    register usage: REG_N_REFS, REG_N_DEATHS, REG_N_SETS, REG_LIVE_LENGTH,
-   REG_N_CALLS_CROSSED and REG_BASIC_BLOCK.
+   REG_N_CALLS_CROSSED, REG_N_THROWING_CALLS_CROSSED and REG_BASIC_BLOCK.
 
    life_analysis sets current_function_sp_is_unchanging if the function
    doesn't modify the stack pointer.  */
@@ -1589,6 +1589,7 @@ allocate_reg_life_data (void)
       REG_N_REFS (i) = 0;
       REG_N_DEATHS (i) = 0;
       REG_N_CALLS_CROSSED (i) = 0;
+      REG_N_THROWING_CALLS_CROSSED (i) = 0;
       REG_LIVE_LENGTH (i) = 0;
       REG_FREQ (i) = 0;
       REG_BASIC_BLOCK (i) = REG_BLOCK_UNKNOWN;
@@ -1820,6 +1821,9 @@ propagate_one_insn (struct propagate_blo
 	  reg_set_iterator rsi;
 	  EXECUTE_IF_SET_IN_REG_SET (pbi->reg_live, 0, i, rsi)
 	    REG_N_CALLS_CROSSED (i)++;
+          if (can_throw_internal (insn))
+	    EXECUTE_IF_SET_IN_REG_SET (pbi->reg_live, 0, i, rsi)
+	      REG_N_THROWING_CALLS_CROSSED (i)++;
 	}
 
       /* Record sets.  Do this even for dead instructions, since they
@@ -3512,7 +3516,11 @@ attempt_auto_inc (struct propagate_block
 	 that REGNO now crosses them.  */
       for (temp = insn; temp != incr; temp = NEXT_INSN (temp))
 	if (CALL_P (temp))
-	  REG_N_CALLS_CROSSED (regno)++;
+	  {
+	    REG_N_CALLS_CROSSED (regno)++;
+	    if (can_throw_internal (temp))
+	      REG_N_THROWING_CALLS_CROSSED (regno)++;
+	  }
 
       /* Invalidate alias info for Q since we just changed its value.  */
       clear_reg_alias_info (q);
--- gcc/testsuite/g++.dg/opt/pr23478.C.jj	2005-08-22 17:18:16.000000000 +0200
+++ gcc/testsuite/g++.dg/opt/pr23478.C	2005-08-22 17:19:23.000000000 +0200
@@ -0,0 +1,211 @@
+// PR rtl-optimization/23478
+// { dg-do run }
+// { dg-options "-O2" }
+
+extern "C" void abort ();
+bool tthrow;
+struct C3 { int i; };
+class C14 {};
+struct C7
+{
+  virtual ~C7 ();
+};
+
+C7::~C7 ()
+{
+  asm volatile ("" : : : "memory");
+}
+class C2 : public C7 {};
+
+template <class X> class C13
+{
+  bool ma;
+  X *mb;
+public:
+  explicit C13 (X *p = 0) throw () : ma (p != 0), mb (p) {}
+  ~C13 ();
+};
+
+template <class X>
+C13<X>::~C13 ()
+{
+  asm volatile ("" : : "r" (ma), "r" (mb) : "memory");
+}
+
+struct C1
+{
+  C1 (const C3 &, const C3 &, const C3 &, const C3 *&);
+};
+
+C1::C1 (const C3 &, const C3 &, const C3 &, const C3 *&)
+{
+  if (!tthrow)
+    throw 24;
+}
+
+struct C8
+{
+  struct C15 {};
+  typedef C15 *C9;
+  virtual void f1 (C2 &, long *, void *, C3 &, void *, bool) = 0;
+  virtual C13<C14> f3 () const = 0;
+  virtual ~C8 () {}
+};
+
+bool
+xx14 ()
+{
+  bool b = false;
+  if (tthrow)
+    throw 6;
+  asm volatile ("" : : "r" (&b) : "memory");
+  return b;
+}
+
+bool
+xx2 ()
+{
+  bool b = false;
+  if (tthrow)
+    throw 6;
+  asm volatile ("" : : "r" (&b) : "memory");
+  return b;
+}
+
+C13<C7>
+xx9 ()
+{
+  return C13<C7>();
+}
+
+C2 &
+xx10 ()
+{
+  static C2 c2;
+  return c2;
+}
+
+C3 &
+xx12 ()
+{
+  static C3 c3 = { 1 };
+  return c3;
+}
+
+const C3 &
+xx5 ()
+{
+  static const C3 c3 = { 2 };
+  return c3;
+}
+
+const C3 *&
+xx4 ()
+{
+  static const C3 *p;
+  if (tthrow)
+    throw 6;
+  return p;
+}
+
+long ll13;
+
+long
+xx13 ()
+{
+  long ret;
+  asm volatile ("" : "=r" (ret) : "r" (ll13));
+  return ret;
+}
+
+void
+xx15 (C3 &x, C13<C1> &y)
+{
+  asm volatile ("" : : "r" (&x), "r" (&y) : "memory");
+}
+
+long
+xx16 (const void *x)
+{
+  long ret;
+  asm volatile ("" : "=r" (ret) : "0" (1), "r" (x) : "memory");
+  return ret;
+}
+
+void
+xx1 (C13<C14> x)
+{
+  asm volatile ("" : : "r" (&x) : "memory");
+  if (tthrow)
+    throw 6;
+}
+
+void
+xx3 (const C7 *x)
+{
+  if (x)
+    abort ();
+}
+
+void
+xx7 ()
+{
+  asm volatile ("" : : : "memory");
+}
+
+struct C5
+{
+  C13<C7> f2 (C3 &v1, const void *v2, C8 *v6);
+  C7 *m2[2];
+  long m1[2];
+};
+
+C13<C7>
+C5::f2 (C3 &v1, const void *v2, C8 *v6)
+{
+  C13<C7> v13 = xx9 ();
+  C2 &v9 = xx10 ();
+  for (long i = 1; i < 2; i++)
+    xx3 (m2[i]);
+  const C3 &ld = xx5 ();
+  xx7 ();
+  if (xx2 ())
+    throw "";
+  xx4 ();
+  C3 &si = xx12 ();
+  for (long i = 0; i < xx16 (v2); ++i)
+    {
+      C13<C1> sk (new C1 (xx5 (), ld, xx5 (), xx4 ()));
+      xx15 (si, sk);
+    }
+  long v4 = xx13 ();
+  for (long i = v4 - 1; i >= 0; --i)
+    m1[i] = i;
+  bool v8 = xx2 ();
+  for (long i = 0; i < 2 && !xx14 (); i++)
+    {
+      v6[i].f1 (v9, 0, __null, v1, __null, v8);
+      if (v8)
+	xx1 (v6[i].f3 ());
+    }
+  return v13;
+}
+
+int
+main (void)
+{
+  C5 c5 = { { __null, __null }, { 0, 0 } };
+  bool seen = false;
+  try
+    {
+      c5.f2 (xx12 (), __null, __null);
+    }
+  catch (int n)
+    {
+      if (n != 24)
+	abort ();
+      seen = true;
+    }
+  if (!seen)
+    abort ();
+}

	Jakub


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