re PR tree-optimization/89280 (ICE: Segmentation fault (in is_gimple_reg_type))
authorJakub Jelinek <jakub@redhat.com>
Wed, 27 Feb 2019 08:41:01 +0000 (09:41 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Wed, 27 Feb 2019 08:41:01 +0000 (09:41 +0100)
PR tree-optimization/89280
* tree-cfgcleanup.c (maybe_dead_abnormal_edge_p,
builtin_setjmp_setup_bb): New functions.
(cleanup_control_flow_pre): Ignore maybe_dead_abnormal_edge_p edges.
When visiting __builtin_setjmp_setup block, queue in special
setjmp_vec vector edges from .ABNORMAL_DISPATCHER to corresponding
__builtin_setjmp_receiver.  Remove .ABNORMAL_DISPATCHER basic blocks
from visited after the loop if they don't have any visited successor
blocks.

* gcc.c-torture/compile/pr89280.c: New test.
* gcc.dg/torture/pr57147-2.c: Don't expect a setjmp after noreturn
function.  Skip the test for -O0.

From-SVN: r269243

gcc/ChangeLog
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.c-torture/compile/pr89280.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/torture/pr57147-2.c
gcc/tree-cfgcleanup.c

index b46e08fca6c952805054d8e75b81d585e9faf535..961131bcf03b286b4c7a438dbb37e13abbc62014 100644 (file)
@@ -1,3 +1,15 @@
+2019-02-27  Jakub Jelinek  <jakub@redhat.com>
+
+       PR tree-optimization/89280
+       * tree-cfgcleanup.c (maybe_dead_abnormal_edge_p,
+       builtin_setjmp_setup_bb): New functions.
+       (cleanup_control_flow_pre): Ignore maybe_dead_abnormal_edge_p edges.
+       When visiting __builtin_setjmp_setup block, queue in special
+       setjmp_vec vector edges from .ABNORMAL_DISPATCHER to corresponding
+       __builtin_setjmp_receiver.  Remove .ABNORMAL_DISPATCHER basic blocks
+       from visited after the loop if they don't have any visited successor
+       blocks.
+
 2018-02-26  Steve Ellcey  <sellcey@marvell.com>
 
        * config/aarch64/aarch64.c (aarch64_get_multilib_abi_name):
index 807c04567f5136893e2c28e4dadd9b1a6f8e939c..71ff3fa9aa99abe6191cf6d6d524e4289cc93e1a 100644 (file)
@@ -1,3 +1,10 @@
+2019-02-27  Jakub Jelinek  <jakub@redhat.com>
+
+       PR tree-optimization/89280
+       * gcc.c-torture/compile/pr89280.c: New test.
+       * gcc.dg/torture/pr57147-2.c: Don't expect a setjmp after noreturn
+       function.  Skip the test for -O0.
+
 2018-02-26  Steve Ellcey  <sellcey@marvell.com>
 
        * gfortran.dg/simd-builtins-1.f90: Update for aarch64*-*-*.
diff --git a/gcc/testsuite/gcc.c-torture/compile/pr89280.c b/gcc/testsuite/gcc.c-torture/compile/pr89280.c
new file mode 100644 (file)
index 0000000..9db9965
--- /dev/null
@@ -0,0 +1,48 @@
+/* PR tree-optimization/89280 */
+
+int a;
+void foo (void);
+__attribute__ ((returns_twice)) int bar (void);
+void baz (int, int);
+void *buf[5];
+
+static inline void
+inl (int x)
+{
+  while (x)
+    foo ();
+}
+
+void
+test1 (void)
+{
+  for (;;)
+    foo ();
+  baz (bar (), a);
+}
+
+void
+test2 (void)
+{
+  for (;;)
+    foo ();
+  baz (__builtin_setjmp (buf), a);
+  if (a)
+    __builtin_longjmp (buf, 1);
+}
+
+void
+test3 (void)
+{
+  inl (1);
+  baz (bar (), a);
+}
+
+void
+test4 (void)
+{
+  inl (2);
+  baz (__builtin_setjmp (buf), a);
+  if (a)
+    __builtin_longjmp (buf, 1);
+}
index fc5fb39eed5409ebee5b17af0324eac35761e188..bbfd13d2932b7f0329a6b985f81373e05b2eb994 100644 (file)
@@ -1,6 +1,7 @@
 /* { dg-do compile } */
 /* { dg-options "-fdump-tree-optimized" } */
 /* { dg-skip-if "" { *-*-* } { "-fno-fat-lto-objects" } { "" } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } { "" } } */
 /* { dg-require-effective-target indirect_jumps } */
 
 struct __jmp_buf_tag {};
@@ -19,4 +20,4 @@ void TestSyscall(void)
   _setjmp (g_return_jmp_buf);
 }
 
-/* { dg-final { scan-tree-dump "setjmp" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "setjmp" "optimized" } } */
index f2e8b96ee8ef556e955d8c38b9b9afc1b68d5108..63db3308344341cbcae1ca1694aa4070684ea571 100644 (file)
@@ -723,6 +723,97 @@ cleanup_tree_cfg_bb (basic_block bb)
   return false;
 }
 
+/* Return true if E is an EDGE_ABNORMAL edge for returns_twice calls,
+   i.e. one going from .ABNORMAL_DISPATCHER to basic block which doesn't
+   start with a forced or nonlocal label.  Calls which return twice can return
+   the second time only if they are called normally the first time, so basic
+   blocks which can be only entered through these abnormal edges but not
+   normally are effectively unreachable as well.  Additionally ignore
+   __builtin_setjmp_receiver starting blocks, which have one FORCED_LABEL
+   and which are always only reachable through EDGE_ABNORMAL edge.  They are
+   handled in cleanup_control_flow_pre.  */
+
+static bool
+maybe_dead_abnormal_edge_p (edge e)
+{
+  if ((e->flags & (EDGE_ABNORMAL | EDGE_EH)) != EDGE_ABNORMAL)
+    return false;
+
+  gimple_stmt_iterator gsi = gsi_start_nondebug_after_labels_bb (e->src);
+  gimple *g = gsi_stmt (gsi);
+  if (!g || !gimple_call_internal_p (g, IFN_ABNORMAL_DISPATCHER))
+    return false;
+
+  tree target = NULL_TREE;
+  for (gsi = gsi_start_bb (e->dest); !gsi_end_p (gsi); gsi_next (&gsi))
+    if (glabel *label_stmt = dyn_cast <glabel *> (gsi_stmt (gsi)))
+      {
+       tree this_target = gimple_label_label (label_stmt);
+       if (DECL_NONLOCAL (this_target))
+         return false;
+       if (FORCED_LABEL (this_target))
+         {
+           if (target)
+             return false;
+           target = this_target;
+         }
+      }
+    else
+      break;
+
+  if (target)
+    {
+      /* If there was a single FORCED_LABEL, check for
+        __builtin_setjmp_receiver with address of that label.  */
+      if (!gsi_end_p (gsi) && is_gimple_debug (gsi_stmt (gsi)))
+       gsi_next_nondebug (&gsi);
+      if (gsi_end_p (gsi))
+       return false;
+      if (!gimple_call_builtin_p (gsi_stmt (gsi), BUILT_IN_SETJMP_RECEIVER))
+       return false;
+
+      tree arg = gimple_call_arg (gsi_stmt (gsi), 0);
+      if (TREE_CODE (arg) != ADDR_EXPR || TREE_OPERAND (arg, 0) != target)
+       return false;
+    }
+  return true;
+}
+
+/* If BB is a basic block ending with __builtin_setjmp_setup, return edge
+   from .ABNORMAL_DISPATCHER basic block to corresponding
+   __builtin_setjmp_receiver basic block, otherwise return NULL.  */
+static edge
+builtin_setjmp_setup_bb (basic_block bb)
+{
+  if (EDGE_COUNT (bb->succs) != 2
+      || ((EDGE_SUCC (bb, 0)->flags
+          & (EDGE_ABNORMAL | EDGE_EH)) != EDGE_ABNORMAL
+         && (EDGE_SUCC (bb, 1)->flags
+             & (EDGE_ABNORMAL | EDGE_EH)) != EDGE_ABNORMAL))
+    return NULL;
+
+  gimple_stmt_iterator gsi = gsi_last_nondebug_bb (bb);
+  if (gsi_end_p (gsi)
+      || !gimple_call_builtin_p (gsi_stmt (gsi), BUILT_IN_SETJMP_SETUP))
+    return NULL;
+
+  tree arg = gimple_call_arg (gsi_stmt (gsi), 1);
+  if (TREE_CODE (arg) != ADDR_EXPR
+      || TREE_CODE (TREE_OPERAND (arg, 0)) != LABEL_DECL)
+    return NULL;
+
+  basic_block recv_bb = label_to_block (cfun, TREE_OPERAND (arg, 0));
+  if (EDGE_COUNT (recv_bb->preds) != 1
+      || (EDGE_PRED (recv_bb, 0)->flags
+         & (EDGE_ABNORMAL | EDGE_EH)) != EDGE_ABNORMAL
+      || (EDGE_SUCC (bb, 0)->dest != EDGE_PRED (recv_bb, 0)->src
+         && EDGE_SUCC (bb, 1)->dest != EDGE_PRED (recv_bb, 0)->src))
+    return NULL;
+
+  /* EDGE_PRED (recv_bb, 0)->src should be the .ABNORMAL_DISPATCHER bb.  */
+  return EDGE_PRED (recv_bb, 0);
+}
+
 /* Do cleanup_control_flow_bb in PRE order.  */
 
 static bool
@@ -736,10 +827,13 @@ cleanup_control_flow_pre ()
   dom_state saved_state = dom_info_state (CDI_DOMINATORS);
   set_dom_info_availability (CDI_DOMINATORS, DOM_NONE);
 
-  auto_vec<edge_iterator, 20> stack (n_basic_blocks_for_fn (cfun) + 1);
+  auto_vec<edge_iterator, 20> stack (n_basic_blocks_for_fn (cfun) + 2);
   auto_sbitmap visited (last_basic_block_for_fn (cfun));
   bitmap_clear (visited);
 
+  vec<edge, va_gc> *setjmp_vec = NULL;
+  auto_vec<basic_block, 4> abnormal_dispatchers;
+
   stack.quick_push (ei_start (ENTRY_BLOCK_PTR_FOR_FN (cfun)->succs));
 
   while (! stack.is_empty ())
@@ -749,12 +843,37 @@ cleanup_control_flow_pre ()
       basic_block dest = ei_edge (ei)->dest;
 
       if (dest != EXIT_BLOCK_PTR_FOR_FN (cfun)
-         && ! bitmap_bit_p (visited, dest->index))
+         && !bitmap_bit_p (visited, dest->index)
+         && (ei_container (ei) == setjmp_vec
+             || !maybe_dead_abnormal_edge_p (ei_edge (ei))))
        {
          bitmap_set_bit (visited, dest->index);
          /* We only possibly remove edges from DEST here, leaving
             possibly unreachable code in the IL.  */
          retval |= cleanup_control_flow_bb (dest);
+
+         /* Check for __builtin_setjmp_setup.  Edges from .ABNORMAL_DISPATCH
+            to __builtin_setjmp_receiver will be normally ignored by
+            maybe_dead_abnormal_edge_p.  If DEST is a visited
+            __builtin_setjmp_setup, queue edge from .ABNORMAL_DISPATCH
+            to __builtin_setjmp_receiver, so that it will be visited too.  */
+         if (edge e = builtin_setjmp_setup_bb (dest))
+           {
+             vec_safe_push (setjmp_vec, e);
+             if (vec_safe_length (setjmp_vec) == 1)
+               stack.quick_push (ei_start (setjmp_vec));
+           }
+
+         if ((ei_edge (ei)->flags
+              & (EDGE_ABNORMAL | EDGE_EH)) == EDGE_ABNORMAL)
+           {
+             gimple_stmt_iterator gsi
+               = gsi_start_nondebug_after_labels_bb (dest);
+             gimple *g = gsi_stmt (gsi);
+             if (g && gimple_call_internal_p (g, IFN_ABNORMAL_DISPATCHER))
+               abnormal_dispatchers.safe_push (dest);
+           }
+
          if (EDGE_COUNT (dest->succs) > 0)
            stack.quick_push (ei_start (dest->succs));
        }
@@ -763,10 +882,32 @@ cleanup_control_flow_pre ()
          if (!ei_one_before_end_p (ei))
            ei_next (&stack.last ());
          else
-           stack.pop ();
+           {
+             if (ei_container (ei) == setjmp_vec)
+               vec_safe_truncate (setjmp_vec, 0);
+             stack.pop ();
+           }
        }
     }
 
+  vec_free (setjmp_vec);
+
+  /* If we've marked .ABNORMAL_DISPATCHER basic block(s) as visited
+     above, but haven't marked any of their successors as visited,
+     unmark them now, so that they can be removed as useless.  */
+  basic_block dispatcher_bb;
+  unsigned int k;
+  FOR_EACH_VEC_ELT (abnormal_dispatchers, k, dispatcher_bb)
+    {
+      edge e;
+      edge_iterator ei;
+      FOR_EACH_EDGE (e, ei, dispatcher_bb->succs)
+       if (bitmap_bit_p (visited, e->dest->index))
+         break;
+      if (e == NULL)
+       bitmap_clear_bit (visited, dispatcher_bb->index);
+    }
+
   set_dom_info_availability (CDI_DOMINATORS, saved_state);
 
   /* We are deleting BBs in non-reverse dominator order, make sure