PR tree-optimization/68541
authorlaw <law@138bc75d-0d04-0410-961f-82ee72b054a4>
Fri, 5 Feb 2016 23:49:08 +0000 (23:49 +0000)
committerlaw <law@138bc75d-0d04-0410-961f-82ee72b054a4>
Fri, 5 Feb 2016 23:49:08 +0000 (23:49 +0000)
* gimple-ssa-split-paths.c: Include tree-cfg.h and params.h.
(count_stmts_in_block): New function.
(poor_ifcvt_candidate_code): Likewise.
(is_feasible_trace): Add some heuristics to determine when path
splitting is profitable.
(find_block_to_duplicate_for_splitting_paths): Make sure the graph
is a diamond with a single exit.

        PR tree-optimization/68541
* gcc.dg/tree-ssa/split-path-2.c: New test.
* gcc.dg/tree-ssa/split-path-3.c: New test.
* gcc.dg/tree-ssa/split-path-4.c: New test.
* gcc.dg/tree-ssa/split-path-5.c: New test.
* gcc.dg/tree-ssa/split-path-6.c: New test.
* gcc.dg/tree-ssa/split-path-7.c: New test.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@233191 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/ChangeLog
gcc/gimple-ssa-split-paths.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/tree-ssa/split-path-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/split-path-3.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/split-path-4.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/split-path-5.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/split-path-6.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/split-path-7.c [new file with mode: 0644]

index 9a51133..a465156 100644 (file)
@@ -1,3 +1,14 @@
+2016-02-05  Jeff Law  <law@redhat.com>
+
+       PR tree-optimization/68541
+       * gimple-ssa-split-paths.c: Include tree-cfg.h and params.h.
+       (count_stmts_in_block): New function.
+       (poor_ifcvt_candidate_code): Likewise.
+       (is_feasible_trace): Add some heuristics to determine when path
+       splitting is profitable.
+       (find_block_to_duplicate_for_splitting_paths): Make sure the graph
+       is a diamond with a single exit.
+
 2016-02-05  Martin Sebor  <msebor@redhat.com>
 
        PR c++/69662
index 40c099a..ac6de81 100644 (file)
@@ -25,11 +25,13 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree.h"
 #include "gimple.h"
 #include "tree-pass.h"
+#include "tree-cfg.h"
 #include "cfganal.h"
 #include "cfgloop.h"
 #include "gimple-iterator.h"
 #include "tracer.h"
 #include "predict.h"
+#include "params.h"
 
 /* Given LATCH, the latch block in a loop, see if the shape of the
    path reaching LATCH is suitable for being split by duplication.
@@ -79,6 +81,11 @@ find_block_to_duplicate_for_splitting_paths (basic_block latch)
              || !find_edge (bb_idom, EDGE_PRED (bb, 1)->src))
            return NULL;
 
+         /* And that the predecessors of BB each have a single successor.  */
+         if (!single_succ_p (EDGE_PRED (bb, 0)->src)
+             || !single_succ_p (EDGE_PRED (bb, 1)->src))
+           return NULL;
+
          /* So at this point we have a simple diamond for an IF-THEN-ELSE
             construct starting at BB_IDOM, with a join point at BB.  BB
             pass control outside the loop or to the loop latch.
@@ -91,29 +98,111 @@ find_block_to_duplicate_for_splitting_paths (basic_block latch)
   return NULL;
 }
 
+/* Return the number of non-debug statements in a block.  */
+static unsigned int
+count_stmts_in_block (basic_block bb)
+{
+  gimple_stmt_iterator gsi;
+  unsigned int num_stmts = 0;
+
+  for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+    {
+      gimple *stmt = gsi_stmt (gsi);
+      if (!is_gimple_debug (stmt))
+       num_stmts++;
+    }
+  return num_stmts;
+}
+
+/* Return TRUE if CODE represents a tree code that is not likely to
+   be easily if-convertable because it likely expands into multiple
+   insns, FALSE otherwise.  */
+static bool
+poor_ifcvt_candidate_code (enum tree_code code)
+{
+  return (code == MIN_EXPR
+         || code == MAX_EXPR
+         || code == ABS_EXPR
+         || code == COND_EXPR
+         || code == CALL_EXPR);
+}
+
 /* Return TRUE if BB is a reasonable block to duplicate by examining
    its size, false otherwise.  BB will always be a loop latch block.
 
-   Should this use the same tests as we do for jump threading?  */
+   Things to consider:
+
+     We do not want to spoil if-conversion if at all possible.
+
+     Most of the benefit seems to be from eliminating the unconditional
+     jump rather than CSE/DCE opportunities.  So favor duplicating
+     small latches.  A latch with just a conditional branch is ideal.
+
+     CSE/DCE opportunties crop up when statements from the predecessors
+     feed statements in the latch and allow statements in the latch to
+     simplify.  */
 
 static bool
 is_feasible_trace (basic_block bb)
 {
-  int num_stmt = 0;
-  gimple_stmt_iterator gsi;
-
-  for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+  basic_block pred1 = EDGE_PRED (bb, 0)->src;
+  basic_block pred2 = EDGE_PRED (bb, 1)->src;
+  int num_stmts_in_join = count_stmts_in_block (bb);
+  int num_stmts_in_pred1 = count_stmts_in_block (pred1);
+  int num_stmts_in_pred2 = count_stmts_in_block (pred2);
+
+  /* This is meant to catch cases that are likely opportunities for
+     if-conversion.  Essentially we look for the case where
+     BB's predecessors are both single statement blocks where
+     the output of that statement feed the same PHI in BB.  */
+  if (num_stmts_in_pred1 == 1 && num_stmts_in_pred2 == 1)
     {
-      gimple *stmt = gsi_stmt (gsi);
-      if (!is_gimple_debug (stmt))
-       num_stmt++;
+      gimple *stmt1 = last_and_only_stmt (pred1);
+      gimple *stmt2 = last_and_only_stmt (pred2);
+
+      if (stmt1 && stmt2
+         && gimple_code (stmt1) == GIMPLE_ASSIGN
+         && gimple_code (stmt2) == GIMPLE_ASSIGN)
+       {
+         enum tree_code code1 = gimple_assign_rhs_code (stmt1);
+         enum tree_code code2 = gimple_assign_rhs_code (stmt2);
+
+         if (!poor_ifcvt_candidate_code (code1)
+             && !poor_ifcvt_candidate_code (code2))
+           {
+             tree lhs1 = gimple_assign_lhs (stmt1);
+             tree lhs2 = gimple_assign_lhs (stmt2);
+             gimple_stmt_iterator gsi;
+             for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+               {
+                 gimple *phi = gsi_stmt (gsi);
+                 if ((gimple_phi_arg_def (phi, 0) == lhs1
+                      && gimple_phi_arg_def (phi, 1) == lhs2)
+                     || (gimple_phi_arg_def (phi, 1) == lhs1
+                         && gimple_phi_arg_def (phi, 0) == lhs2))
+                   {
+                     if (dump_file && (dump_flags & TDF_DETAILS))
+                       fprintf (dump_file,
+                                "Block %d appears to be a join point for "
+                                "if-convertable diamond.\n",
+                                bb->index);
+                     return false;
+                   }
+               }
+           }
+       }
     }
 
-  /* We may want to limit how many statements we copy.  */
-  if (num_stmt > 1)
-    return true;
+  /* We may want something here which looks at dataflow and tries
+     to guess if duplication of BB is likely to result in simplification
+     of instructions in BB in either the original or the duplicate.  */
+
+  /* Upper Hard limit on the number statements to copy.  */
+  if (num_stmts_in_join
+      >= PARAM_VALUE (PARAM_MAX_JUMP_THREAD_DUPLICATION_STMTS))
+    return false;
 
-  return false;
+  return true;
 }
 
 /* If the immediate dominator of the latch of the loop is
index cce24b4..388b2b9 100644 (file)
@@ -1,3 +1,13 @@
+2016-02-05  Jeff Law  <law@redhat.com>
+
+        PR tree-optimization/68541
+       * gcc.dg/tree-ssa/split-path-2.c: New test.
+       * gcc.dg/tree-ssa/split-path-3.c: New test.
+       * gcc.dg/tree-ssa/split-path-4.c: New test.
+       * gcc.dg/tree-ssa/split-path-5.c: New test.
+       * gcc.dg/tree-ssa/split-path-6.c: New test.
+       * gcc.dg/tree-ssa/split-path-7.c: New test.
+
 2016-02-05  Martin Sebor  <msebor@redhat.com>
 
        PR c++/69662
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/split-path-2.c b/gcc/testsuite/gcc.dg/tree-ssa/split-path-2.c
new file mode 100644 (file)
index 0000000..aeb926e
--- /dev/null
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fsplit-paths -fdump-tree-split-paths-details " } */
+
+int
+foo(char *p, int n)
+{
+  int s = 0;
+  int i;
+
+  for (i = 0; i < n; i++) {
+    if (p[i] >= 0)
+      s++;
+    else
+      s--;
+  }
+
+  return s;
+}
+
+/* { dg-final { scan-tree-dump "appears to be a join point for if-convertable diamond" "split-paths" } } */
+
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/split-path-3.c b/gcc/testsuite/gcc.dg/tree-ssa/split-path-3.c
new file mode 100644 (file)
index 0000000..814504a
--- /dev/null
@@ -0,0 +1,90 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fsplit-paths -fdump-tree-split-paths-details -w" } */
+
+typedef struct bitmap_head_def *bitmap;
+extern void vec_assert_fail (const char *, const char *, const char *file_,
+                            unsigned line_, const char *function_)
+  __attribute__ ((__noreturn__));
+typedef struct VEC_int_base
+{
+  unsigned num;
+  unsigned alloc;
+  int vec[1];
+}
+VEC_int_base;
+static __inline__ int
+VEC_int_base_space (VEC_int_base * vec_, int alloc_, const char *file_,
+                   unsigned line_, const char *function_)
+{
+  return vec_ ? vec_->alloc - vec_->num >= (unsigned) alloc_ : !alloc_;
+}
+
+static __inline__ int *
+VEC_int_base_quick_push (VEC_int_base * vec_, int obj_, const char *file_,
+                        unsigned line_, const char *function_)
+{
+  (void) ((vec_->num <
+          vec_->alloc) ? 0 : (vec_assert_fail ("push", "VEC(int,base)",
+                                               file_, line_, function_), 0));
+}
+
+typedef struct VEC_int_heap
+{
+  VEC_int_base base;
+}
+VEC_int_heap;
+static __inline__ int
+VEC_int_heap_reserve (VEC_int_heap ** vec_, int alloc_, const char *file_,
+                     unsigned line_, const char *function_)
+{
+  int extend =
+    !VEC_int_base_space (((*vec_) ? &(*vec_)->base : 0), alloc_, file_, line_,
+                        function_);
+  if (extend)
+    *vec_ =
+      (VEC_int_heap *) vec_heap_o_reserve (*vec_, alloc_,
+                                          __builtin_offsetof (VEC_int_heap,
+                                                              base.vec),
+                                          sizeof (int));
+}
+
+static __inline__ int *
+VEC_int_heap_safe_push (VEC_int_heap ** vec_, const int obj_,
+                       const char *file_, unsigned line_,
+                       const char *function_)
+{
+  VEC_int_heap_reserve (vec_, 1, file_, line_, function_);
+  return VEC_int_base_quick_push (((*vec_) ? &(*vec_)->base : 0), obj_, file_,
+                                 line_, function_);
+};
+
+typedef struct bitmap_head_def
+{
+}
+bitmap_head;
+typedef struct
+{
+}
+bitmap_iterator;
+bitmap
+compute_idf (bitmap_head * dfs)
+{
+  bitmap_iterator bi;
+  unsigned bb_index, i;
+  VEC_int_heap *work_stack;
+  bitmap phi_insertion_points;
+  while ((VEC_int_base_length (((work_stack) ? &(work_stack)->base : 0))) > 0)
+    {
+      for (bmp_iter_and_compl_init
+          (&(bi), (&dfs[bb_index]), (phi_insertion_points), (0), &(i));
+          bmp_iter_and_compl (&(bi), &(i)); bmp_iter_next (&(bi), &(i)))
+       {
+         (VEC_int_heap_safe_push
+          (&(work_stack), i, "/home/gcc/virgin-gcc/gcc/cfganal.c", 1349,
+           __FUNCTION__));
+       }
+    }
+  (VEC_int_heap_free (&work_stack));
+}
+
+/* { dg-final { scan-tree-dump-not "Duplicating join block" "split-paths" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/split-path-4.c b/gcc/testsuite/gcc.dg/tree-ssa/split-path-4.c
new file mode 100644 (file)
index 0000000..dac931c
--- /dev/null
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fsplit-paths -fdump-tree-split-paths-details -w" } */
+
+powi_cost (long n)
+{
+  unsigned char cache[256];
+  unsigned long digit;
+  unsigned long val;
+  int result;
+  while (val >= 256)
+    {
+      if (val & 1)
+       {
+         result += powi_lookup_cost (digit, cache) + 3 + 1;
+       }
+      else
+       {
+         val >>= 1;
+       }
+    }
+}
+
+/* { dg-final { scan-tree-dump-times "Duplicating join block" 1 "split-paths" } } */
+
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/split-path-5.c b/gcc/testsuite/gcc.dg/tree-ssa/split-path-5.c
new file mode 100644 (file)
index 0000000..5044c73
--- /dev/null
@@ -0,0 +1,60 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fsplit-paths -fdump-tree-split-paths-details -w" } */
+
+const extern char *__ctype_ptr__;
+typedef unsigned char uchar;
+static int patlen;
+static int skip[(0x7f * 2 + 1) + 1];
+static uchar *pat = ((void *) 0);
+void
+bmhi_init (const char *pattern)
+{
+  int i, lastpatchar;
+  patlen = strlen (pattern);
+  for (i = 0; i < patlen; i++)
+    pat[i] = (
+              {
+              __typeof__ (pattern[i]) __x = (pattern[i]);
+              ((((__ctype_ptr__ +
+                  sizeof (""[__x]))[(int) (__x)]) & (01 | 02))
+               == 02) ? (int) __x - 'a' + 'A' : (int) __x;
+              });
+  for (i = 0; i < patlen - 1; ++i)
+    {
+      skip[(
+            {
+            __typeof__ (pat[i]) __x = (pat[i]);
+            ((((__ctype_ptr__ +
+                sizeof (""[__x]))[(int) (__x)]) & (01 | 02)) ==
+             01) ? (int) __x - 'A' + 'a' : (int) __x;
+            })] = patlen - i - 1;
+    }
+  skip[(
+        {
+        __typeof__ (lastpatchar) __x = (lastpatchar);
+        ((((__ctype_ptr__ +
+            sizeof (""[__x]))[(int) (__x)]) & (01 | 02)) ==
+         01) ? (int) __x - 'A' + 'a' : (int) __x;
+        })] = 32767;
+  for (i = 0; i < patlen - 1; ++i)
+    {
+    }
+}
+
+char *
+bmhi_search (const char *string, const int stringlen)
+{
+  int i, j;
+  char *s;
+  for (;;)
+    {
+      while (--j >= 0 && (
+                          {
+                          __typeof__ (s[j]) __x = (s[j]);
+                          ((((__ctype_ptr__ +
+                              sizeof (""[__x]))[(int) (__x)]) &
+                            (01 | 02)) ==
+                           02) ? (int) __x - 'a' +
+                          'A' : (int) __x;}) == pat[j]);
+}}
+/* { dg-final { scan-tree-dump-times "Duplicating join block" 2 "split-paths" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/split-path-6.c b/gcc/testsuite/gcc.dg/tree-ssa/split-path-6.c
new file mode 100644 (file)
index 0000000..682166f
--- /dev/null
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fsplit-paths -fdump-tree-split-paths-details -w" } */
+
+struct __sFILE
+{
+  unsigned char *_p;
+  int _r;
+};
+typedef struct __sFILE __FILE;
+struct _reent
+{
+  __FILE *_stdin, *_stdout, *_stderr;
+};
+extern struct _reent *_impure_ptr;
+extern char contextbufs[10][1024];
+extern int contextoffset;
+extern int sflag;
+void
+givehelp (interactive)
+     int interactive;
+{
+  if (interactive)
+    {
+      while ((--((_impure_ptr->_stdin))->_r <
+             0 ? __srget_r (_impure_ptr,
+                            (_impure_ptr->
+                             _stdin)) : (int) (*((_impure_ptr->_stdin))->
+                                               _p++)) != ' ');
+    }
+}
+
+oof ()
+{
+  int bufsize;
+  int hadnl;
+  while (1)
+    {
+      if (bufsize == (sizeof contextbufs[0]) / 2 - 1)
+       {
+         if (contextbufs[0][0] == '*' || contextbufs[0][0] == '@')
+           treeinsert (ichartosstr (strtosichar (contextbufs[0] + 1, 0), 1),
+                       (100 + 4 * 20 + 4), contextbufs[0][0] == '*');
+       }
+      if (hadnl)
+       contextoffset = 0;
+      else
+       contextoffset += bufsize;
+      if (sflag)
+       {
+         stop ();
+       }
+    }
+}
+
+void
+lookharder (string)
+     char *string;
+{
+  register char *g;
+  register char *s;
+  for (s = string; *s != '\0'; s++)
+    {
+      if (*s == '*')
+       {
+         *g++ = '.';
+       }
+      else
+       *g++ = *s;
+    }
+}
+
+/* { dg-final { scan-tree-dump-times "Duplicating join block" 3 "split-paths" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/split-path-7.c b/gcc/testsuite/gcc.dg/tree-ssa/split-path-7.c
new file mode 100644 (file)
index 0000000..f14ab79
--- /dev/null
@@ -0,0 +1,94 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fsplit-paths -fdump-tree-split-paths-details -w" } */
+
+
+struct _reent
+{
+};
+typedef unsigned char ichar_t;
+struct dent
+{
+  char *word;
+};
+struct flagent
+{
+  ichar_t *strip;
+  ichar_t *affix;
+  short stripl;
+  short affl;
+};
+union ptr_union
+{
+  struct flagptr *fp;
+  struct flagent *ent;
+};
+struct flagptr
+{
+  union ptr_union pu;
+  int numents;
+};
+struct hashheader
+{
+};
+extern struct dent *hashtbl;
+extern char *hashstrings;
+extern int hashsize;
+extern int nodictflag;
+extern int numsflags;
+extern int numpflags;
+extern struct flagent *pflaglist;
+extern struct flagent *sflaglist;
+int
+linit ()
+{
+  register int i;
+  register struct dent *dp;
+  struct flagent *entry;
+  struct flagptr *ind;
+  int viazero;
+  register ichar_t *cp;
+  if (!nodictflag)
+    {
+      for (i = hashsize, dp = hashtbl; --i >= 0; dp++)
+       {
+         if (dp->word == (char *) -1)
+           dp->word = ((void *) 0);
+         else
+           dp->word = &hashstrings[(int) (dp->word)];
+       }
+    }
+  for (i = numsflags + numpflags, entry = sflaglist; --i >= 0; entry++)
+    {
+      if (entry->stripl)
+       entry->strip = (ichar_t *) & hashstrings[(int) entry->strip];
+      else
+       entry->affix = ((void *) 0);
+    }
+  for (i = numsflags, entry = sflaglist; i > 0; i--, entry++)
+    {
+      if (entry->affl == 0)
+       {
+         if (ind->pu.fp == ((void *) 0))
+           {
+           }
+       }
+    }
+  for (i = numpflags, entry = pflaglist; i > 0; i--, entry++)
+    {
+      if (entry->affl == 0)
+       {
+         while (ind->numents == 0 && ind->pu.fp != ((void *) 0))
+           {
+             if (*cp == 0)
+               {
+               }
+           }
+       }
+      if (!viazero && ind->numents >= 4
+         && strcmp ((char *) (entry->affix),
+                    (char *) (ind->pu.ent->affix)) != 0)
+       {
+       }
+    }
+}
+/* { dg-final { scan-tree-dump-times "Duplicating join block" 2 "split-paths" } } */