[PATCH] Fix EH labels on IA-64 (PR target/30230)

Jakub Jelinek jakub@redhat.com
Sat Dec 16 13:04:00 GMT 2006


Hi!

The attached testcase fails on ia64-linux at -O2, both with GCC trunk and
4.1.1.
The problem is a br.call not at the end of a bundle where the EH region
ends right after it:
	.mbb
	ld8 r39 = [r15]
	br.call.sptk.many b0 = _ZN1F3barEPvR1B#
[.LEHE0:]
	nop 0
	;;
As br.call sets rp to the address of the next bundle, when the personality
routine is called it doesn't think this call is inside of the .LEHB0 .. .LEHE0
EH region and calls std::terminate, as _Unwind_GetIP (context) is
.LEHE0 - 2 + 16, so even when we subtract one it still is far above .LEHE0.
The only 2 bundles which have a branch slot not in the last position are
.mbb and .bbb.  GCC generates an explicit stop after a br.call if it is
followed by some branch instruction or another call, so I think br.call
not in the last slot is only possible if the rest of the .mbb or .bbb bundle
is filled with nops.
When not emitting explicit bundling (-O0 or -fno-schedule-insns2),
we are IMHO safe, as GCC then doesn't emit nops after br.call instructions
(and there is explicit ;; between br.call and another br or br.call),
so for
	br.call.sptk.many b0 = something#
[.LEHEN:]
the br.call will be either in the last slot of a bundle (therefore
(.LEHEN & 15) == 0, or the assembler will emit one or two nop.b's between
the br.call and the label.

The following patch should cure it when emitting explicit bundling
(-O2, -fschedule-insns2).  When adding a bundle selector for the
.mbb or .bbb bundles, it checks for a br.call insn with REG_EH_REGION
note followed by nops in the same bundle and in that case adds REG_EH_REGION
notes to the nops as well.  This tells except.c to move the region end label
to the end of the bundle.

On the attached testcase, the difference is just:
 	.mbb
 	ld8 r39 = [r15]
 	br.call.sptk.many b0 = _ZN1F3barEPvR1B#
-[.LEHE0:]
 	nop 0
+[.LEHE0:]
 	;;

Bootstrapped/regtested on ia64-linux, ok for trunk/4.2/4.1?

2006-12-16  Jakub Jelinek  <jakub@redhat.com>

	PR target/30230
	* config/ia64/ia64.c (ia64_add_bundle_selector_before): New function.
	(bundling): Use it.

	* g++.dg/eh/ia64-2.C: New test.

--- gcc/config/ia64/ia64.c.jj	2006-12-08 15:58:05.000000000 +0100
+++ gcc/config/ia64/ia64.c	2006-12-16 10:01:38.000000000 +0100
@@ -7693,6 +7693,51 @@ get_next_important_insn (rtx insn, rtx t
   return NULL_RTX;
 }
 
+/* Add a bundle selector TEMPLATE0 before INSN.  */
+
+static void
+ia64_add_bundle_selector_before (int template0, rtx insn)
+{
+  rtx b = gen_bundle_selector (GEN_INT (template0));
+  ia64_emit_insn_before (b, insn);
+#if NR_BUNDLES == 10
+  if ((template0 == 4 || template0 == 5)
+      && (flag_unwind_tables || (flag_exceptions && !USING_SJLJ_EXCEPTIONS)))
+    {
+      int i;
+      rtx note = NULL_RTX;
+
+      /* In .mbb and .bbb bundles, check if CALL_INSN isn't in the
+	 first or second slot.  If it is and has REG_EH_NOTE set, copy it
+	 to following nops, as br.call sets rp to the address of following
+	 bundle and therefore an EH region end must be on a bundle
+	 boundary.  */
+      insn = PREV_INSN (insn);
+      for (i = 0; i < 3; i++)
+	{
+	  do
+	    insn = next_active_insn (insn);
+	  while (GET_CODE (insn) == INSN
+		 && get_attr_empty (insn) == EMPTY_YES);
+	  if (GET_CODE (insn) == CALL_INSN)
+	    note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
+	  else if (note)
+	    {
+	      int code;
+	      gcc_assert ((code = recog_memoized (insn)) == CODE_FOR_nop
+			  || code == CODE_FOR_nop_b);
+	      if (find_reg_note (insn, REG_EH_REGION, NULL_RTX))
+		note = NULL_RTX;
+	      else
+		REG_NOTES (insn)
+		  = gen_rtx_EXPR_LIST (REG_EH_REGION, XEXP (note, 0),
+				       REG_NOTES (insn));
+	    }
+	}
+    }
+#endif
+}
+
 /* The following function does insn bundling.  Bundling means
    inserting templates and nop insns to fit insn groups into permitted
    templates.  Instruction scheduling uses NDFA (non-deterministic
@@ -7974,8 +8019,7 @@ bundling (FILE *dump, int verbose, rtx p
 		/* We are at the start of a bundle: emit the template
 		   (it should be defined).  */
 		gcc_assert (template0 >= 0);
-		b = gen_bundle_selector (GEN_INT (template0));
-		ia64_emit_insn_before (b, nop);
+		ia64_add_bundle_selector_before (template0, nop);
 		/* If we have two bundle window, we make one bundle
 		   rotation.  Otherwise template0 will be undefined
 		   (negative value).  */
@@ -8001,8 +8045,7 @@ bundling (FILE *dump, int verbose, rtx p
 	  /* The current insn is at the bundle start: emit the
 	     template.  */
 	  gcc_assert (template0 >= 0);
-	  b = gen_bundle_selector (GEN_INT (template0));
-	  ia64_emit_insn_before (b, insn);
+	  ia64_add_bundle_selector_before (template0, insn);
 	  b = PREV_INSN (insn);
 	  insn = b;
 	  /* See comment above in analogous place for emitting nops
@@ -8024,8 +8067,7 @@ bundling (FILE *dump, int verbose, rtx p
 	      /* See comment above in analogous place for emitting nops
 		 after the insn.  */
 	      gcc_assert (template0 >= 0);
-	      b = gen_bundle_selector (GEN_INT (template0));
-	      ia64_emit_insn_before (b, insn);
+	      ia64_add_bundle_selector_before (template0, insn);
 	      b = PREV_INSN (insn);
 	      insn = b;
 	      template0 = template1;
@@ -8119,8 +8161,7 @@ bundling (FILE *dump, int verbose, rtx p
 	      }
 	    /* Put the MM-insn in the same slot of a bundle with the
 	       same template as the original one.  */
-	    ia64_emit_insn_before (gen_bundle_selector (GEN_INT (template0)),
-				   insn);
+	    ia64_add_bundle_selector_before (template0, insn);
 	    /* To put the insn in the same slot, add necessary number
 	       of nops.  */
 	    for (j = n; j > 0; j --)
--- gcc/testsuite/g++.dg/eh/ia64-2.C.jj	2006-12-16 10:06:38.000000000 +0100
+++ gcc/testsuite/g++.dg/eh/ia64-2.C	2006-12-16 10:09:03.000000000 +0100
@@ -0,0 +1,57 @@
+// PR target/30230
+// This testcase failed on IA-64, where end of an EH region ended
+// in the middle of a bundle (with br.call insn in first or second
+// slot of .bbb/.mbb bundles and EH region end right after it).
+// But br.call returns to the start of the next bundlem so during
+// unwinding the call was considered to be outside of the EH region
+// while it should have been inside.
+// { dg-options "-O2" }
+// { dg-do run }
+
+struct A {};
+struct B { virtual ~B(); };
+B::~B () {}
+struct C { void foo (short &, B &); };
+struct D { void *d1; C *d2; virtual void virt (void) {} };
+struct E { D *e1; B *e2; };
+struct F { void bar (void *, B &); };
+F *p __attribute__((weak));
+volatile int r;
+
+void C::foo (short &x, B &)
+{
+  if (r)
+    throw A ();
+  x = 1;
+}
+
+void F::bar (void *, B &)
+{
+  throw A ();
+}
+
+void baz (E &x)
+{
+  short g = 0;
+  B b = *x.e2;
+  x.e1->d2->foo (g, b);
+  if (g)
+    p->bar(x.e1->d1, b);
+}
+
+int main ()
+{
+  F g;
+  D s;
+  E h;
+  p = &g;
+  h.e1 = &s;
+  try
+    {
+      baz (h);
+    }
+  catch (A &)
+    {
+    }
+  return 0;
+}

	Jakub



More information about the Gcc-patches mailing list