Patchwork Fix up cross-jumping with __builtin_trap or __builtin_unreachable (PR middle-end/55094)

login
register
mail settings
Submitter Jakub Jelinek
Date Nov. 19, 2012, 8:46 p.m.
Message ID <20121119204629.GO2315@tucnak.redhat.com>
Download mbox | patch
Permalink /patch/200154/
State New
Headers show

Comments

Jakub Jelinek - Nov. 19, 2012, 8:46 p.m.
Hi!

As the following testcase (with disabled LRA) shows, cross-jumping
sometimes leads into dwarf2cfi ICEs, because traps or even arbitrary insns
followed by __builtin_unreachable () with different args size depth can be
cross-jumped together.

Fixed by emitting REG_ARGS_SIZE notes on traps, and for
__builtin_unreachable where we have nothing to attach it to just giving up
cross-jumping if there is no REG_ARGS_SIZE note and
!ACCUMULATE_OUTGOING_ARGS.  Additionally it fixes a -fcompare-debug insn,
because with __builtin_unreachable the block might not end up with any
jump/call/trap insn, but e.g. debug insns.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk and
after a while for 4.7 too?

2012-11-19  Jakub Jelinek  <jakub@redhat.com>

	PR middle-end/55094
	* builtins.c (expand_builtin_trap): Add REG_ARGS_SIZE note
	on the trap insn for !ACCUMULATE_OUTGOING_ARGS.
	* cfgcleanup.c (outgoing_edges_match): Don't look at debug insns
	on the first old_insns_match_p call.  For !ACCUMULATE_OUTGOING_ARGS
	fail if the last real insn doesn't have REG_ARGS_SIZE note.

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


	Jakub
Richard Henderson - Nov. 19, 2012, 9:43 p.m.
On 11/19/2012 12:46 PM, Jakub Jelinek wrote:
> 2012-11-19  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR middle-end/55094
> 	* builtins.c (expand_builtin_trap): Add REG_ARGS_SIZE note
> 	on the trap insn for !ACCUMULATE_OUTGOING_ARGS.
> 	* cfgcleanup.c (outgoing_edges_match): Don't look at debug insns
> 	on the first old_insns_match_p call.  For !ACCUMULATE_OUTGOING_ARGS
> 	fail if the last real insn doesn't have REG_ARGS_SIZE note.
> 
> 	* gcc.dg/pr55094.c: New test.


Ok.



r~

Patch

--- gcc/builtins.c.jj	2012-11-19 14:41:14.000000000 +0100
+++ gcc/builtins.c	2012-11-19 18:21:30.324047247 +0100
@@ -4666,7 +4666,14 @@  expand_builtin_trap (void)
 {
 #ifdef HAVE_trap
   if (HAVE_trap)
-    emit_insn (gen_trap ());
+    {
+      rtx insn = emit_insn (gen_trap ());
+      /* For trap insns when not accumulating outgoing args force
+	 REG_ARGS_SIZE note to prevent crossjumping of calls with
+	 different args sizes.  */
+      if (!ACCUMULATE_OUTGOING_ARGS)
+	add_reg_note (insn, REG_ARGS_SIZE, GEN_INT (stack_pointer_delta));
+    }
   else
 #endif
     emit_library_call (abort_libfunc, LCT_NORETURN, VOIDmode, 0);
--- gcc/cfgcleanup.c.jj	2012-11-19 14:41:24.000000000 +0100
+++ gcc/cfgcleanup.c	2012-11-19 18:21:30.324047247 +0100
@@ -1702,9 +1702,15 @@  outgoing_edges_match (int mode, basic_bl
 	}
     }
 
+  rtx last1 = BB_END (bb1);
+  rtx last2 = BB_END (bb2);
+  if (DEBUG_INSN_P (last1))
+    last1 = prev_nondebug_insn (last1);
+  if (DEBUG_INSN_P (last2))
+    last2 = prev_nondebug_insn (last2);
   /* First ensure that the instructions match.  There may be many outgoing
      edges so this test is generally cheaper.  */
-  if (old_insns_match_p (mode, BB_END (bb1), BB_END (bb2)) != dir_both)
+  if (old_insns_match_p (mode, last1, last2) != dir_both)
     return false;
 
   /* Search the outgoing edges, ensure that the counts do match, find possible
@@ -1713,10 +1719,14 @@  outgoing_edges_match (int mode, basic_bl
   if (EDGE_COUNT (bb1->succs) != EDGE_COUNT (bb2->succs))
     return false;
 
+  bool nonfakeedges = false;
   FOR_EACH_EDGE (e1, ei, bb1->succs)
     {
       e2 = EDGE_SUCC (bb2, ei.index);
 
+      if ((e1->flags & EDGE_FAKE) == 0)
+	nonfakeedges = true;
+
       if (e1->flags & EDGE_EH)
 	nehedges1++;
 
@@ -1734,6 +1744,18 @@  outgoing_edges_match (int mode, basic_bl
       || (fallthru1 != 0) != (fallthru2 != 0))
     return false;
 
+  /* If !ACCUMULATE_OUTGOING_ARGS, bb1 (and bb2) have no successors
+     and the last real insn doesn't have REG_ARGS_SIZE note, don't
+     attempt to optimize, as the two basic blocks might have different
+     REG_ARGS_SIZE depths.  For noreturn calls and unconditional
+     traps there should be REG_ARG_SIZE notes, they could be missing
+     for __builtin_unreachable () uses though.  */
+  if (!nonfakeedges
+      && !ACCUMULATE_OUTGOING_ARGS
+      && (!INSN_P (last1)
+          || !find_reg_note (last1, REG_ARGS_SIZE, NULL)))
+    return false;
+
   /* fallthru edges must be forwarded to the same destination.  */
   if (fallthru1)
     {
--- gcc/testsuite/gcc.dg/pr55094.c.jj	2012-11-19 18:21:56.120897505 +0100
+++ gcc/testsuite/gcc.dg/pr55094.c	2012-11-19 18:20:52.000000000 +0100
@@ -0,0 +1,45 @@ 
+/* PR middle-end/55094 */
+/* { dg-do compile } */
+/* { dg-options "-fcompare-debug -Os" } */
+/* { dg-additional-options "-fomit-frame-pointer -fno-asynchronous-unwind-tables -mpreferred-stack-boundary=2" { target { { i?86-*-* x86_64-*-* } && ia32 } } } */
+
+extern int fn (long);
+int v;
+
+int
+foo (int x, long *y)
+{
+  if (x)
+    {
+      fn (y[0]);
+      __builtin_trap ();
+    }
+  __builtin_trap ();
+}
+
+int
+bar (int x, long *y)
+{
+  if (x)
+    {
+      fn (y[0]);
+      v = 1;
+      __builtin_unreachable ();
+    }
+  v = 1;
+  __builtin_unreachable ();
+}
+
+int
+baz (int x, long *y)
+{
+  if (x)
+    {
+      fn (y[0]);
+      v = 1;
+      __builtin_unreachable ();
+    }
+  v = 1;
+  int w = 1;
+  __builtin_unreachable ();
+}