This patch adds vectorization support to early-break loops with gswitch
statements. Such gswitches may come from original switch-case constructs
in the source or the iftoswitch pass which rewrites if conditions with a
chain of comparisons, like below, to gswitch statements.

        if (a[i] == c1 || a[i] == c2 || a[i] == c3)

In this patch, the loop exit condition type is generalized from gcond to
gimple, so that gswitch statements can be treated as valid loop exits. A
new vectorizer pattern recognization function is introduced to check if
all non-default labels of the gswitch have the same destination. If yes,
it lowers the gswitch to an equivalent gcond. To avoid excessive compile
time, a new parameter vect-max-compare-in-switch-lowering is introduced
to limit the maximum number of comparisons in the gswitch lowering. In
addition, the CRC loop optimization is adjusted a bit to make sure CRC
loops still have gcond exits.

This patch is bootstrapped and regression-tested on x86_64-linux-gnu,
arm-linux-gnueabihf and aarch64-linux-gnu.

gcc/ChangeLog:

        * doc/invoke.texi: Document vect-max-compare-in-switch-lowering.
        * gimple-crc-optimization.cc (crc_optimization::loop_may_calculate_crc):
        Ensure CRC loop exits are still gconds.
        (crc_optimization::optimize_crc_loop): Cast CRC loop exits.
        * params.opt: Add option vect-max-compare-in-switch-lowering.
        * tree-scalar-evolution.cc (scev_dfs::follow_ssa_edge_expr):
        Generalize loop exit conditions type from gcond to gimple.
        (get_loop_exit_condition): Likewise.
        * tree-scalar-evolution.h (get_loop_exit_condition): Likewise.
        * tree-vect-loop-manip.cc (vect_set_loop_condition_normal):
        Likewise.
        (vect_set_loop_condition): Likewise.
        (slpeel_can_duplicate_loop_p): Likewise.
        * tree-vect-loop.cc (vect_get_loop_niters): Likewise.
        (vect_analyze_loop_form): Likewise.
        (vect_create_loop_vinfo): Likewise.
        * tree-vect-patterns.cc (vect_recog_gswitch_pattern): New
        pattern recognition function for gswitch early exits.
        * tree-vect-slp.cc (vect_analyze_slp): Bail out if a gswitch is
        not lowered into a gcond.
        * tree-vect-stmts.cc (vect_mark_stmts_to_be_vectorized): Allow
        gswitch statements as loop exits.
        (vectorizable_early_exit): Transform gswitch early exits.
        * tree-vectorizer.h (struct vect_loop_form_info): Allow gswitch
        statements as loop exits.

gcc/testsuite/ChangeLog:

        * gcc.dg/vect/vect-switch-search-line-fast.c: Update the test as
        the loop is now vectorizable on multiple targets.
        * gcc.dg/vect/vect-early-break_139-switch1.c: New test.
        * gcc.dg/vect/vect-early-break_139-switch2.c: New test.
        * gcc.dg/vect/vect-early-break_139-switch3.c: New test.
        * gcc.dg/vect/vect-early-break_139-switch4.c: New test.
---
 gcc/doc/invoke.texi                           |   4 +
 gcc/gimple-crc-optimization.cc                |   9 +-
 gcc/params.opt                                |   4 +
 .../vect/vect-early-break_139-switch1.c       |  19 +++
 .../vect/vect-early-break_139-switch2.c       |  21 ++++
 .../vect/vect-early-break_139-switch3.c       |  24 ++++
 .../vect/vect-early-break_139-switch4.c       |  26 ++++
 .../vect/vect-switch-search-line-fast.c       |   3 +-
 gcc/tree-scalar-evolution.cc                  |  12 +-
 gcc/tree-scalar-evolution.h                   |   4 +-
 gcc/tree-vect-loop-manip.cc                   |   6 +-
 gcc/tree-vect-loop.cc                         |  10 +-
 gcc/tree-vect-patterns.cc                     | 111 ++++++++++++++++++
 gcc/tree-vect-slp.cc                          |  11 +-
 gcc/tree-vect-stmts.cc                        |  74 ++++++++----
 gcc/tree-vectorizer.h                         |   8 +-
 16 files changed, 295 insertions(+), 51 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/vect/vect-early-break_139-switch1.c
 create mode 100644 gcc/testsuite/gcc.dg/vect/vect-early-break_139-switch2.c
 create mode 100644 gcc/testsuite/gcc.dg/vect/vect-early-break_139-switch3.c
 create mode 100644 gcc/testsuite/gcc.dg/vect/vect-early-break_139-switch4.c

diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 3cf326cc22c..def84a6675a 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -16652,6 +16652,10 @@ Complex expressions slow the analyzer.
 Maximum number of arguments in a PHI supported by TREE if conversion
 unless the loop is marked with simd pragma.
 
+@item vect-max-compare-in-switch-lowering
+The maximum number of comparisons that can be performed when lowering switch
+statements in the vectorizer.
+
 @item vect-max-layout-candidates
 The maximum number of possible vector layouts (such as permutations)
 to consider when optimizing to-be-vectorized code.
diff --git a/gcc/gimple-crc-optimization.cc b/gcc/gimple-crc-optimization.cc
index 1751be9bc97..986aa193893 100644
--- a/gcc/gimple-crc-optimization.cc
+++ b/gcc/gimple-crc-optimization.cc
@@ -937,6 +937,12 @@ crc_optimization::loop_may_calculate_crc (class loop *loop)
     return false;
 
   m_crc_loop = loop;
+
+  /* Filter out the loops, which don't have a gcond as single exit.  */
+  gimple *loop_exit_cond = get_loop_exit_condition (m_crc_loop);
+  if (loop_exit_cond == NULL || !is_a <gcond *> (loop_exit_cond))
+    return false;
+
   basic_block *loop_bbs = get_loop_body_in_dom_order (m_crc_loop);
 
   /* Filter out the cases, which don't have exactly two conditions in the loop.
@@ -1274,7 +1280,8 @@ crc_optimization::optimize_crc_loop (gphi *output_crc)
   remove_phi_node (&tmp_gsi, false);
 
   /* Alter the exit condition of the loop to always exit.  */
-  gcond* loop_exit_cond = get_loop_exit_condition (m_crc_loop);
+  gcond *loop_exit_cond
+    = safe_dyn_cast <gcond *> (get_loop_exit_condition (m_crc_loop));
   gimple_cond_make_false (loop_exit_cond);
   update_stmt (loop_exit_cond);
   return true;
diff --git a/gcc/params.opt b/gcc/params.opt
index dd53d830895..115400ee8d2 100644
--- a/gcc/params.opt
+++ b/gcc/params.opt
@@ -1229,6 +1229,10 @@ Whether to use canonical types.
 Common Joined UInteger Var(param_vect_epilogues_nomask) Init(1) 
IntegerRange(0, 1) Param Optimization NoOffload
 Enable loop epilogue vectorization using smaller vector size.
 
+-param=vect-max-compare-in-switch-lowering=
+Common Joined UInteger Var(param_vect_max_compare_in_switch_lowering) Init(16) 
Param Optimization
+Maximum number of comparisons that can be performed when lowering switch 
statements in the vectorizer.
+
 -param=vect-max-layout-candidates=
 Common Joined UInteger Var(param_vect_max_layout_candidates) Init(32) Param 
Optimization
 Maximum number of possible vector layouts (such as permutations) to consider 
when optimizing to-be-vectorized code.
diff --git a/gcc/testsuite/gcc.dg/vect/vect-early-break_139-switch1.c 
b/gcc/testsuite/gcc.dg/vect/vect-early-break_139-switch1.c
new file mode 100644
index 00000000000..bbe75d20116
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-early-break_139-switch1.c
@@ -0,0 +1,19 @@
+/* { dg-add-options vect_early_break } */
+/* { dg-do compile } */
+/* { dg-require-effective-target vect_early_break } */
+/* { dg-require-effective-target vect_int } */
+
+/* { dg-additional-options "-Ofast" } */
+
+int find (int *a, int n)
+{
+  for (int i = 0; i < n; i++)
+  {
+    /* This is converted to a switch statement in the iftoswitch pass.  */
+    if (a[i] == 1 || a[i] == 2 || a[i] == 5 || a[i] == 7)
+      return i;
+  }
+  return -1;
+}
+
+/* { dg-final { scan-tree-dump "LOOP VECTORIZED" "vect" } } */
diff --git a/gcc/testsuite/gcc.dg/vect/vect-early-break_139-switch2.c 
b/gcc/testsuite/gcc.dg/vect/vect-early-break_139-switch2.c
new file mode 100644
index 00000000000..d200bea4594
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-early-break_139-switch2.c
@@ -0,0 +1,21 @@
+/* { dg-add-options vect_early_break } */
+/* { dg-do compile } */
+/* { dg-require-effective-target vect_early_break } */
+/* { dg-require-effective-target vect_int } */
+
+/* { dg-additional-options "-Ofast" } */
+
+int find (int *a, int n)
+{
+  for (int i = 0; i < n; i++)
+  {
+    /* This is converted to a switch statement in the iftoswitch pass.  */
+    if (a[i] == 1 || a[i] == 3 || a[i] == 4 || a[i] == 5)
+      continue;
+    else
+      return i;
+  }
+  return -1;
+}
+
+/* { dg-final { scan-tree-dump "LOOP VECTORIZED" "vect" } } */
diff --git a/gcc/testsuite/gcc.dg/vect/vect-early-break_139-switch3.c 
b/gcc/testsuite/gcc.dg/vect/vect-early-break_139-switch3.c
new file mode 100644
index 00000000000..f696267a855
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-early-break_139-switch3.c
@@ -0,0 +1,24 @@
+/* { dg-add-options vect_early_break } */
+/* { dg-do compile } */
+/* { dg-require-effective-target vect_early_break } */
+/* { dg-require-effective-target vect_int } */
+
+/* { dg-additional-options "-Ofast" } */
+
+int find (int *a, int n)
+{
+  for (int i = 0; i < n; i++)
+  {
+    /* An early break in case labels of a switch statement.  */
+    switch (a[i])
+    {
+      case -1 ... 1:
+      case 10:
+      case 20:
+        return i;
+    }
+  }
+  return -1;
+}
+
+/* { dg-final { scan-tree-dump "LOOP VECTORIZED" "vect" } } */
diff --git a/gcc/testsuite/gcc.dg/vect/vect-early-break_139-switch4.c 
b/gcc/testsuite/gcc.dg/vect/vect-early-break_139-switch4.c
new file mode 100644
index 00000000000..f8e3187843d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-early-break_139-switch4.c
@@ -0,0 +1,26 @@
+/* { dg-add-options vect_early_break } */
+/* { dg-do compile } */
+/* { dg-require-effective-target vect_early_break } */
+/* { dg-require-effective-target vect_int } */
+
+/* { dg-additional-options "-Ofast" } */
+
+int find (int *a, int n)
+{
+  for (int i = 0; i < n; i++)
+  {
+    /* An early break in the default label of a switch statement.  */
+    switch (a[i])
+    {
+      case 1 ... 3:
+      case 5:
+      case 10:
+       continue;
+      default:
+        return i;
+    }
+  }
+  return -1;
+}
+
+/* { dg-final { scan-tree-dump "LOOP VECTORIZED" "vect" } } */
diff --git a/gcc/testsuite/gcc.dg/vect/vect-switch-search-line-fast.c 
b/gcc/testsuite/gcc.dg/vect/vect-switch-search-line-fast.c
index 678512db319..17e80330daa 100644
--- a/gcc/testsuite/gcc.dg/vect/vect-switch-search-line-fast.c
+++ b/gcc/testsuite/gcc.dg/vect/vect-switch-search-line-fast.c
@@ -16,5 +16,4 @@ const unsigned char *search_line_fast2 (const unsigned char 
*s,
   return s;
 }
 
-/* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect" { target { 
ilp32 || { amdgcn*-*-* } } } } } */
-/* { dg-final { scan-tree-dump-times "vectorized 1 loops" 0 "vect" { target { 
! { ilp32 || { amdgcn*-*-* } } } } } } */
+/* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect" } } */
diff --git a/gcc/tree-scalar-evolution.cc b/gcc/tree-scalar-evolution.cc
index ecdef7529a6..0a5172c3be4 100644
--- a/gcc/tree-scalar-evolution.cc
+++ b/gcc/tree-scalar-evolution.cc
@@ -1302,7 +1302,7 @@ scev_dfs::follow_ssa_edge_expr (gimple *at_stmt, tree 
expr,
    guards the exit edge.  If the expression is too difficult to
    analyze, then give up.  */
 
-gcond *
+gimple *
 get_loop_exit_condition (const class loop *loop)
 {
   return get_loop_exit_condition (single_exit (loop));
@@ -1311,16 +1311,20 @@ get_loop_exit_condition (const class loop *loop)
 /* If the statement just before the EXIT_EDGE contains a condition then
    return the condition, otherwise NULL. */
 
-gcond *
+gimple *
 get_loop_exit_condition (const_edge exit_edge)
 {
-  gcond *res = NULL;
+  gimple *res = NULL;
 
   if (dump_file && (dump_flags & TDF_SCEV))
     fprintf (dump_file, "(get_loop_exit_condition \n  ");
 
   if (exit_edge)
-    res = safe_dyn_cast <gcond *> (*gsi_last_bb (exit_edge->src));
+    {
+      gimple *g = *gsi_last_bb (exit_edge->src);
+      if (is_a <gcond *> (g) || is_a <gswitch *> (g))
+       res = g;
+    }
 
   if (dump_file && (dump_flags & TDF_SCEV))
     {
diff --git a/gcc/tree-scalar-evolution.h b/gcc/tree-scalar-evolution.h
index c7feeacf1db..54f0bf82c7b 100644
--- a/gcc/tree-scalar-evolution.h
+++ b/gcc/tree-scalar-evolution.h
@@ -22,8 +22,8 @@ along with GCC; see the file COPYING3.  If not see
 #define GCC_TREE_SCALAR_EVOLUTION_H
 
 extern tree number_of_latch_executions (class loop *);
-extern gcond *get_loop_exit_condition (const class loop *);
-extern gcond *get_loop_exit_condition (const_edge);
+extern gimple *get_loop_exit_condition (const class loop *);
+extern gimple *get_loop_exit_condition (const_edge);
 
 extern void scev_initialize (void);
 extern bool scev_initialized_p (void);
diff --git a/gcc/tree-vect-loop-manip.cc b/gcc/tree-vect-loop-manip.cc
index 20141dbc2e5..bd04252da2d 100644
--- a/gcc/tree-vect-loop-manip.cc
+++ b/gcc/tree-vect-loop-manip.cc
@@ -1233,7 +1233,7 @@ vect_set_loop_condition_normal (loop_vec_info /* 
loop_vinfo */, edge exit_edge,
 {
   tree indx_before_incr, indx_after_incr;
   gcond *cond_stmt;
-  gcond *orig_cond;
+  gimple *orig_cond;
   edge pe = loop_preheader_edge (loop);
   gimple_stmt_iterator incr_gsi;
   bool insert_after;
@@ -1395,7 +1395,7 @@ vect_set_loop_condition (class loop *loop, edge loop_e, 
loop_vec_info loop_vinfo
                         bool niters_maybe_zero)
 {
   gcond *cond_stmt;
-  gcond *orig_cond = get_loop_exit_condition (loop_e);
+  gimple *orig_cond = get_loop_exit_condition (loop_e);
   gimple_stmt_iterator loop_cond_gsi = gsi_for_stmt (orig_cond);
 
   if (loop_vinfo && LOOP_VINFO_USING_PARTIAL_VECTORS_P (loop_vinfo))
@@ -2039,7 +2039,7 @@ slpeel_can_duplicate_loop_p (const class loop *loop, 
const_edge exit_e,
                             const_edge e)
 {
   edge entry_e = loop_preheader_edge (loop);
-  gcond *orig_cond = get_loop_exit_condition (exit_e);
+  gimple *orig_cond = get_loop_exit_condition (exit_e);
   gimple_stmt_iterator loop_exit_gsi = gsi_last_bb (exit_e->src);
 
   /* All loops have an outer scope; the only case loop->outer is NULL is for
diff --git a/gcc/tree-vect-loop.cc b/gcc/tree-vect-loop.cc
index b58e4355e58..02db9f5a2eb 100644
--- a/gcc/tree-vect-loop.cc
+++ b/gcc/tree-vect-loop.cc
@@ -627,12 +627,12 @@ vect_fixup_scalar_cycles_with_patterns (loop_vec_info 
loop_vinfo)
    Return the loop exit conditions.  */
 
 
-static vec<gcond *>
+static vec<gimple *>
 vect_get_loop_niters (class loop *loop, const_edge main_exit, tree 
*assumptions,
                      tree *number_of_iterations, tree *number_of_iterationsm1)
 {
   auto_vec<edge> exits = get_loop_exit_edges (loop);
-  vec<gcond *> conds;
+  vec<gimple *> conds;
   conds.create (exits.length ());
   class tree_niter_desc niter_desc;
   tree niter_assumptions, niter, may_be_zero;
@@ -654,7 +654,7 @@ vect_get_loop_niters (class loop *loop, const_edge 
main_exit, tree *assumptions,
   unsigned int i;
   FOR_EACH_VEC_ELT (exits, i, exit)
     {
-      gcond *cond = get_loop_exit_condition (exit);
+      gimple *cond = get_loop_exit_condition (exit);
       if (cond)
        conds.safe_push (cond);
 
@@ -1680,7 +1680,7 @@ vect_analyze_loop_form (class loop *loop, gimple 
*loop_vectorized_call,
   /* Determine what the primary and alternate exit conds are.  */
   for (unsigned i = 0; i < info->conds.length (); i++)
     {
-      gcond *cond = info->conds[i];
+      gimple *cond = info->conds[i];
       if (exit_e->src == gimple_bb (cond))
        std::swap (info->conds[0], info->conds[i]);
     }
@@ -1745,7 +1745,7 @@ vect_create_loop_vinfo (class loop *loop, vec_info_shared 
*shared,
   if (!integer_onep (info->assumptions) && !orig_loop_info)
     LOOP_VINFO_NITERS_ASSUMPTIONS (loop_vinfo) = info->assumptions;
 
-  for (gcond *cond : info->conds)
+  for (gimple *cond : info->conds)
     {
       stmt_vec_info loop_cond_info = loop_vinfo->lookup_stmt (cond);
       /* Mark the statement as a condition.  */
diff --git a/gcc/tree-vect-patterns.cc b/gcc/tree-vect-patterns.cc
index 70bf768d339..cd39bdfbd0d 100644
--- a/gcc/tree-vect-patterns.cc
+++ b/gcc/tree-vect-patterns.cc
@@ -5529,6 +5529,116 @@ vect_recog_gcond_pattern (vec_info *vinfo,
   return pattern_stmt;
 }
 
+/* Function vect_recog_gswitch_pattern
+
+   Try to find pattern like following:
+
+     switch (x) <default: <La>, case b1: <Lb>, ... case bN: <Lb>>
+
+   where all labels except the default one have the same destination and
+   convert it to a boolean pattern
+
+     mask = (x == b1) || (x == b2) || ... || (x == bN)
+     if (mask != 0)
+
+   The type of output TYPE_OUT is set in this function.
+
+   Input:
+
+   * STMT_VINFO: The stmt at the end from which the pattern
+                search begins, i.e., cast of a bool to
+                an integer type.
+
+   Output:
+
+   * TYPE_OUT: The type of the output of this pattern.
+
+   * Return value: A new stmt that will be used to replace the pattern.  */
+
+static gimple *
+vect_recog_gswitch_pattern (vec_info *vinfo,
+                           stmt_vec_info stmt_vinfo, tree *type_out)
+{
+  /* Currently we only support this for loop vectorization and when multiple
+     exits.  */
+  loop_vec_info loop_vinfo = dyn_cast <loop_vec_info> (vinfo);
+  if (!loop_vinfo || !LOOP_VINFO_EARLY_BREAKS (loop_vinfo))
+    return NULL;
+
+  gimple *last_stmt = STMT_VINFO_STMT (stmt_vinfo);
+  gswitch *gs = NULL;
+  if (!(gs = dyn_cast <gswitch *> (last_stmt)))
+    return NULL;
+
+  tree index = gimple_switch_index (gs);
+  tree stype = TREE_TYPE (index);
+
+  /* For now, there should be no gswitch with boolean type index.  */
+  gcc_assert (!VECT_SCALAR_BOOLEAN_TYPE_P (stype));
+  tree vectype = get_mask_type_for_scalar_type (vinfo, stype);
+  if (vectype == NULL_TREE)
+    return NULL;
+
+  tree case_label_dest = NULL_TREE;
+  tree cond = NULL_TREE;
+  unsigned int num_cmps = 0;
+  for (unsigned li = 1; li < gimple_switch_num_labels (gs); li++)
+    {
+      tree label = gimple_switch_label (gs, li);
+
+      /* Check if all case labels have the same destination.  */
+      tree dest = CASE_LABEL (label);
+      if (case_label_dest == NULL)
+       case_label_dest = dest;
+      else if (dest != case_label_dest)
+       return NULL;
+
+      tree low = CASE_LOW (label);
+      gcc_assert (low != NULL_TREE);
+      tree high = CASE_HIGH (label);
+      gcc_assert (high == NULL_TREE || tree_int_cst_lt (low, high));
+
+      wide_int val = wi::to_wide (low);
+      wide_int one = wi::one (TYPE_PRECISION (stype));
+      while (true)
+       {
+         /* A large number of comparisons lead to long compile time and make
+            vectorization non-profitable (most cost models will reject the
+            vectorized code).  So bail out in this case.  */
+         if (++num_cmps > (unsigned) param_vect_max_compare_in_switch_lowering)
+           return NULL;
+
+         tree eq_cond = vect_recog_temp_ssa_var (boolean_type_node, NULL);
+         gimple *eq_stmt = gimple_build_assign (eq_cond, EQ_EXPR, index,
+                                                wide_int_to_tree (stype, val));
+         append_pattern_def_seq (vinfo, stmt_vinfo, eq_stmt, vectype, stype);
+         if (cond == NULL_TREE)
+           cond = eq_cond;
+         else
+           {
+             tree new_cond = vect_recog_temp_ssa_var (boolean_type_node, NULL);
+             gimple *or_stmt = gimple_build_assign (new_cond, BIT_IOR_EXPR,
+                                                    cond, eq_cond);
+             append_pattern_def_seq (vinfo, stmt_vinfo, or_stmt,
+                                     vectype, stype);
+             cond = new_cond;
+           }
+
+         /* Break if the case label has a single value or we've reached the
+            high value.  */
+         if (high == NULL_TREE || wi::eq_p (val, wi::to_wide (high)))
+           break;
+
+         val = wi::add (val, one);
+       }
+    }
+
+  vect_pattern_detected ("vect_recog_gswitch_pattern", last_stmt);
+  *type_out = vectype;
+  return gimple_build_cond (NE_EXPR, cond, build_zero_cst (TREE_TYPE (cond)),
+                           NULL_TREE, NULL_TREE);
+}
+
 /* Function vect_recog_bool_pattern
 
    Try to find pattern like following:
@@ -6987,6 +7097,7 @@ static vect_recog_func vect_vect_recog_func_ptrs[] = {
   { vect_recog_sat_sub_pattern, "sat_sub" },
   { vect_recog_sat_trunc_pattern, "sat_trunc" },
   { vect_recog_gcond_pattern, "gcond" },
+  { vect_recog_gswitch_pattern, "gswitch" },
   { vect_recog_bool_pattern, "bool" },
   /* This must come before mask conversion, and includes the parts
      of mask conversion that are needed for gather and scatter
diff --git a/gcc/tree-vect-slp.cc b/gcc/tree-vect-slp.cc
index 895fb88ab7f..217d1299656 100644
--- a/gcc/tree-vect-slp.cc
+++ b/gcc/tree-vect-slp.cc
@@ -5409,14 +5409,13 @@ vect_analyze_slp (vec_info *vinfo, unsigned 
max_tree_size,
          vec<stmt_vec_info> roots = vNULL;
          roots.safe_push (cond_info);
          gimple *stmt = STMT_VINFO_STMT (cond_info);
-         tree args0 = gimple_cond_lhs (stmt);
-         tree args1 = gimple_cond_rhs (stmt);
 
          /* These should be enforced by cond lowering, but if it failed
             bail.  */
-         if (gimple_cond_code (stmt) != NE_EXPR
-             || TREE_TYPE (args0) != boolean_type_node
-             || !integer_zerop (args1))
+         if (!is_a <gcond *> (stmt)
+             || gimple_cond_code (stmt) != NE_EXPR
+             || TREE_TYPE (gimple_cond_lhs (stmt)) != boolean_type_node
+             || !integer_zerop (gimple_cond_rhs (stmt)))
            {
              roots.release ();
              return opt_result::failure_at (vect_location,
@@ -5428,7 +5427,7 @@ vect_analyze_slp (vec_info *vinfo, unsigned max_tree_size,
             from them.  It's highly likely that the resulting SLP tree here if 
both
             arguments have a def will be incompatible, but we rely on it being 
split
             later on.  */
-         auto varg = loop_vinfo->lookup_def (args0);
+         auto varg = loop_vinfo->lookup_def (gimple_cond_lhs (stmt));
          vec<stmt_vec_info> stmts;
          vec<tree> remain = vNULL;
          stmts.create (1);
diff --git a/gcc/tree-vect-stmts.cc b/gcc/tree-vect-stmts.cc
index 6274956e2a5..436a4f4fc44 100644
--- a/gcc/tree-vect-stmts.cc
+++ b/gcc/tree-vect-stmts.cc
@@ -743,6 +743,7 @@ vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo, 
bool *fatal)
 
          if (gimple_get_lhs (stmt) == NULL_TREE
              && !is_a <gcond *> (stmt)
+             && !is_a <gswitch *> (stmt)
              && !is_a <gcall *> (stmt))
            return opt_result::failure_at
                (stmt, "not vectorized: irregular stmt: %G", stmt);
@@ -12339,28 +12340,6 @@ vectorizable_early_exit (loop_vec_info loop_vinfo, 
stmt_vec_info stmt_info,
   bool masked_loop_p = LOOP_VINFO_FULLY_MASKED_P (loop_vinfo);
   bool len_loop_p = LOOP_VINFO_FULLY_WITH_LENGTH_P (loop_vinfo);
 
-  /* Now build the new conditional.  Pattern gimple_conds get dropped during
-     codegen so we must replace the original insn.  */
-  gimple *orig_stmt = STMT_VINFO_STMT (vect_orig_stmt (stmt_info));
-  gcond *cond_stmt = as_a <gcond *>(orig_stmt);
-
-  tree vectype_out = vectype;
-  auto bb = gimple_bb (cond_stmt);
-  edge exit_true_edge = EDGE_SUCC (bb, 0);
-  if (exit_true_edge->flags & EDGE_FALSE_VALUE)
-    exit_true_edge = EDGE_SUCC (bb, 1);
-  gcc_assert (exit_true_edge->flags & EDGE_TRUE_VALUE);
-
-  /* When vectorizing we assume that if the branch edge is taken that we're
-     exiting the loop.  This is not however always the case as the compiler 
will
-     rewrite conditions to always be a comparison against 0.  To do this it
-     sometimes flips the edges.  This is fine for scalar,  but for vector we
-     then have to negate the result of the test, as we're still assuming that 
if
-     you take the branch edge that we found the exit condition.  i.e. we need 
to
-     know whether we are generating a `forall` or an `exist` condition.  */
-  bool flipped = flow_bb_inside_loop_p (LOOP_VINFO_LOOP (loop_vinfo),
-                                       exit_true_edge->dest);
-
   /* See if we support ADDHN and use that for the reduction.  */
   internal_fn ifn = IFN_VEC_TRUNC_ADD_HIGH;
   bool addhn_supported_p
@@ -12439,6 +12418,53 @@ vectorizable_early_exit (loop_vec_info loop_vinfo, 
stmt_vec_info stmt_info,
   auto_vec<tree> stmts;
   stmts.safe_splice (SLP_TREE_VEC_DEFS (slp_node));
 
+  /* Now build the new conditional.  Pattern gimple_conds get dropped during
+     codegen so we must replace the original insn.  */
+  gimple *orig_stmt = STMT_VINFO_STMT (vect_orig_stmt (stmt_info));
+  tree vectype_out = vectype;
+  auto bb = gimple_bb (orig_stmt);
+
+  /* Find the edge which indicates the branch is taken.  If original statement
+     is a gcond, we find the edge with flag EDGE_TRUE_VALUE.  For gswitch, we
+     find the edge from the non-default label, and set flags EDGE_TRUE_VALUE,
+     EDGE_FALSE_VALUE for the two edges.  */
+  edge exit_true_edge = EDGE_SUCC (bb, 0);
+  if (is_a <gcond *> (orig_stmt))
+    {
+      if (exit_true_edge->flags & EDGE_FALSE_VALUE)
+       exit_true_edge = EDGE_SUCC (bb, 1);
+      gcc_assert (exit_true_edge->flags & EDGE_TRUE_VALUE);
+    }
+  else
+    {
+      gcc_assert (is_a <gswitch *> (orig_stmt));
+      gcc_assert (bb->succs->length () == 2);
+      tree dft_lab = gimple_switch_default_label (as_a <gswitch *> 
(orig_stmt));
+      basic_block dft_bb = label_to_block (cfun, CASE_LABEL (dft_lab));
+      if (exit_true_edge->dest != dft_bb)
+       {
+         EDGE_SUCC (bb, 0)->flags |= EDGE_TRUE_VALUE;
+         EDGE_SUCC (bb, 1)->flags |= EDGE_FALSE_VALUE;
+       }
+      else
+       {
+         exit_true_edge = EDGE_SUCC (bb, 1);
+         EDGE_SUCC (bb, 0)->flags |= EDGE_FALSE_VALUE;
+         EDGE_SUCC (bb, 1)->flags |= EDGE_TRUE_VALUE;
+       }
+      gcc_assert (exit_true_edge->dest != dft_bb);
+    }
+
+  /* When vectorizing we assume that if the branch edge is taken that we're
+     exiting the loop.  This is not however always the case as the compiler 
will
+     rewrite conditions to always be a comparison against 0.  To do this it
+     sometimes flips the edges.  This is fine for scalar,  but for vector we
+     then have to negate the result of the test, as we're still assuming that 
if
+     you take the branch edge that we found the exit condition.  i.e., we need
+     to know whether we are generating a `forall` or an `exist` condition.  */
+  bool flipped = flow_bb_inside_loop_p (LOOP_VINFO_LOOP (loop_vinfo),
+                                       exit_true_edge->dest);
+
   /* If we're comparing against a previous forall we need to negate the 
resullts
      before we do the final comparison or reduction.  */
   if (flipped)
@@ -12536,8 +12562,8 @@ vectorizable_early_exit (loop_vec_info loop_vinfo, 
stmt_vec_info stmt_info,
   gcc_assert (new_temp);
 
   tree cst = build_zero_cst (vectype_out);
-  gimple_cond_set_condition (cond_stmt, NE_EXPR, new_temp, cst);
-  update_stmt (orig_stmt);
+  new_stmt = gimple_build_cond (NE_EXPR, new_temp, cst, NULL_TREE, NULL_TREE);
+  gsi_replace (&cond_gsi, new_stmt, true);
 
   /* ??? */
   SLP_TREE_VEC_DEFS (slp_node).truncate (0);
diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h
index b7c2188ab3d..cb715459c00 100644
--- a/gcc/tree-vectorizer.h
+++ b/gcc/tree-vectorizer.h
@@ -1155,10 +1155,10 @@ public:
   bool early_breaks;
 
   /* List of loop additional IV conditionals found in the loop.  */
-  auto_vec<gcond *> conds;
+  auto_vec<gimple *> conds;
 
   /* Main loop IV cond.  */
-  gcond* loop_iv_cond;
+  gimple *loop_iv_cond;
 
   /* True if we have an unroll factor requested by the user through pragma GCC
      unroll.  */
@@ -2702,8 +2702,8 @@ struct vect_loop_form_info
   tree number_of_iterations;
   tree number_of_iterationsm1;
   tree assumptions;
-  auto_vec<gcond *> conds;
-  gcond *inner_loop_cond;
+  auto_vec<gimple *> conds;
+  gimple *inner_loop_cond;
   edge loop_exit;
 };
 extern opt_result vect_analyze_loop_form (class loop *, gimple *,
-- 
2.43.0


Reply via email to