OSDN Git Service

2012-03-27 Richard Guenther <rguenther@suse.de>
[pf3gnuchains/gcc-fork.git] / gcc / tree-vect-stmts.c
index 62b88e1..edfe98d 100644 (file)
@@ -1,5 +1,5 @@
 /* Statement Analysis and Transformation for Vectorization
-   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
+   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
    Free Software Foundation, Inc.
    Contributed by Dorit Naishlos <dorit@il.ibm.com>
    and Ira Rosen <irar@il.ibm.com>
@@ -150,6 +150,8 @@ vect_mark_relevant (VEC(gimple,heap) **worklist, gimple stmt,
           use_operand_p use_p;
           gimple use_stmt;
           tree lhs;
+         loop_vec_info loop_vinfo = STMT_VINFO_LOOP_VINFO (stmt_info);
+         struct loop *loop = LOOP_VINFO_LOOP (loop_vinfo);
 
           if (is_gimple_assign (stmt))
             lhs = gimple_assign_lhs (stmt);
@@ -166,6 +168,9 @@ vect_mark_relevant (VEC(gimple,heap) **worklist, gimple stmt,
                  continue;
                use_stmt = USE_STMT (use_p);
 
+               if (!flow_bb_inside_loop_p (loop, gimple_bb (use_stmt)))
+                 continue;
+
                if (vinfo_for_stmt (use_stmt)
                    && STMT_VINFO_IN_PATTERN_P (vinfo_for_stmt (use_stmt)))
                  {
@@ -332,6 +337,8 @@ exist_non_indexing_operands_for_use_p (tree use, gimple stmt)
    - LIVE_P, RELEVANT - enum values to be set in the STMT_VINFO of the stmt
      that defined USE.  This is done by calling mark_relevant and passing it
      the WORKLIST (to add DEF_STMT to the WORKLIST in case it is relevant).
+   - FORCE is true if exist_non_indexing_operands_for_use_p check shouldn't
+     be performed.
 
    Outputs:
    Generally, LIVE_P and RELEVANT are used to define the liveness and
@@ -351,7 +358,8 @@ exist_non_indexing_operands_for_use_p (tree use, gimple stmt)
 
 static bool
 process_use (gimple stmt, tree use, loop_vec_info loop_vinfo, bool live_p,
-            enum vect_relevant relevant, VEC(gimple,heap) **worklist)
+            enum vect_relevant relevant, VEC(gimple,heap) **worklist,
+            bool force)
 {
   struct loop *loop = LOOP_VINFO_LOOP (loop_vinfo);
   stmt_vec_info stmt_vinfo = vinfo_for_stmt (stmt);
@@ -363,10 +371,10 @@ process_use (gimple stmt, tree use, loop_vec_info loop_vinfo, bool live_p,
 
   /* case 1: we are only interested in uses that need to be vectorized.  Uses
      that are used for address computation are not considered relevant.  */
-  if (!exist_non_indexing_operands_for_use_p (use, stmt))
+  if (!force && !exist_non_indexing_operands_for_use_p (use, stmt))
      return true;
 
-  if (!vect_is_simple_use (use, loop_vinfo, NULL, &def_stmt, &def, &dt))
+  if (!vect_is_simple_use (use, stmt, loop_vinfo, NULL, &def_stmt, &def, &dt))
     {
       if (vect_print_dump_info (REPORT_UNVECTORIZED_LOCATIONS))
         fprintf (vect_dump, "not vectorized: unsupported use in stmt.");
@@ -646,7 +654,7 @@ vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)
             break;
         }
 
-      if (is_pattern_stmt_p (vinfo_for_stmt (stmt)))
+      if (is_pattern_stmt_p (stmt_vinfo))
         {
           /* Pattern statements are not inserted into the code, so
              FOR_EACH_PHI_OR_STMT_USE optimizes their operands out, and we
@@ -660,9 +668,9 @@ vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)
              if (rhs_code == COND_EXPR && COMPARISON_CLASS_P (op))
                {
                  if (!process_use (stmt, TREE_OPERAND (op, 0), loop_vinfo,
-                                   live_p, relevant, &worklist)
+                                   live_p, relevant, &worklist, false)
                      || !process_use (stmt, TREE_OPERAND (op, 1), loop_vinfo,
-                                      live_p, relevant, &worklist))
+                                      live_p, relevant, &worklist, false))
                    {
                      VEC_free (gimple, heap, worklist);
                      return false;
@@ -673,7 +681,7 @@ vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)
                 {
                  op = gimple_op (stmt, i);
                   if (!process_use (stmt, op, loop_vinfo, live_p, relevant,
-                                    &worklist))
+                                   &worklist, false))
                     {
                       VEC_free (gimple, heap, worklist);
                       return false;
@@ -686,7 +694,7 @@ vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)
                 {
                   tree arg = gimple_call_arg (stmt, i);
                   if (!process_use (stmt, arg, loop_vinfo, live_p, relevant,
-                                    &worklist))
+                                   &worklist, false))
                     {
                       VEC_free (gimple, heap, worklist);
                       return false;
@@ -699,12 +707,25 @@ vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)
           {
             tree op = USE_FROM_PTR (use_p);
             if (!process_use (stmt, op, loop_vinfo, live_p, relevant,
-                              &worklist))
+                             &worklist, false))
               {
                 VEC_free (gimple, heap, worklist);
                 return false;
               }
           }
+
+      if (STMT_VINFO_GATHER_P (stmt_vinfo))
+       {
+         tree off;
+         tree decl = vect_check_gather (stmt, loop_vinfo, NULL, &off, NULL);
+         gcc_assert (decl);
+         if (!process_use (stmt, off, loop_vinfo, live_p, relevant,
+                           &worklist, true))
+           {
+             VEC_free (gimple, heap, worklist);
+             return false;
+           }
+       }
     } /* while worklist */
 
   VEC_free (gimple, heap, worklist);
@@ -790,6 +811,46 @@ vect_model_simple_cost (stmt_vec_info stmt_info, int ncopies,
 }
 
 
+/* Model cost for type demotion and promotion operations.  PWR is normally
+   zero for single-step promotions and demotions.  It will be one if 
+   two-step promotion/demotion is required, and so on.  Each additional
+   step doubles the number of instructions required.  */
+
+static void
+vect_model_promotion_demotion_cost (stmt_vec_info stmt_info,
+                                   enum vect_def_type *dt, int pwr)
+{
+  int i, tmp;
+  int inside_cost = 0, outside_cost = 0, single_stmt_cost;
+
+  /* The SLP costs were already calculated during SLP tree build.  */
+  if (PURE_SLP_STMT (stmt_info))
+    return;
+
+  single_stmt_cost = vect_get_stmt_cost (vec_promote_demote);
+  for (i = 0; i < pwr + 1; i++)
+    {
+      tmp = (STMT_VINFO_TYPE (stmt_info) == type_promotion_vec_info_type) ?
+       (i + 1) : i;
+      inside_cost += vect_pow2 (tmp) * single_stmt_cost;
+    }
+
+  /* FORNOW: Assuming maximum 2 args per stmts.  */
+  for (i = 0; i < 2; i++)
+    {
+      if (dt[i] == vect_constant_def || dt[i] == vect_external_def)
+        outside_cost += vect_get_stmt_cost (vector_stmt);
+    }
+
+  if (vect_print_dump_info (REPORT_COST))
+    fprintf (vect_dump, "vect_model_promotion_demotion_cost: inside_cost = %d, "
+             "outside_cost = %d .", inside_cost, outside_cost);
+
+  /* Set the costs in STMT_INFO.  */
+  stmt_vinfo_set_inside_of_loop_cost (stmt_info, NULL, inside_cost);
+  stmt_vinfo_set_outside_of_loop_cost (stmt_info, NULL, outside_cost);
+}
+
 /* Function vect_cost_strided_group_size
 
    For strided load or store, return the group_size only if it is the first
@@ -861,12 +922,11 @@ vect_model_store_cost (stmt_vec_info stmt_info, int ncopies,
     {
       /* Uses a high and low interleave operation for each needed permute.  */
       inside_cost = ncopies * exact_log2(group_size) * group_size
-        * vect_get_stmt_cost (vector_stmt);
+        * vect_get_stmt_cost (vec_perm);
 
       if (vect_print_dump_info (REPORT_COST))
         fprintf (vect_dump, "vect_model_store_cost: strided group_size = %d .",
                  group_size);
-
     }
 
   /* Costs of the stores.  */
@@ -967,7 +1027,7 @@ vect_model_load_cost (stmt_vec_info stmt_info, int ncopies, bool load_lanes_p,
     {
       /* Uses an even and odd extract operations for each needed permute.  */
       inside_cost = ncopies * exact_log2(group_size) * group_size
-       * vect_get_stmt_cost (vector_stmt);
+       * vect_get_stmt_cost (vec_perm);
 
       if (vect_print_dump_info (REPORT_COST))
         fprintf (vect_dump, "vect_model_load_cost: strided group_size = %d .",
@@ -1028,7 +1088,7 @@ vect_get_load_cost (struct data_reference *dr, int ncopies,
     case dr_explicit_realign:
       {
         *inside_cost += ncopies * (2 * vect_get_stmt_cost (vector_load)
-           + vect_get_stmt_cost (vector_stmt));
+                                  + vect_get_stmt_cost (vec_perm));
 
         /* FIXME: If the misalignment remains fixed across the iterations of
            the containing loop, the following cost should be added to the
@@ -1036,6 +1096,9 @@ vect_get_load_cost (struct data_reference *dr, int ncopies,
         if (targetm.vectorize.builtin_mask_for_load)
           *inside_cost += vect_get_stmt_cost (vector_stmt);
 
+        if (vect_print_dump_info (REPORT_COST))
+          fprintf (vect_dump, "vect_model_load_cost: explicit realign");
+
         break;
       }
     case dr_explicit_realign_optimized:
@@ -1059,7 +1122,12 @@ vect_get_load_cost (struct data_reference *dr, int ncopies,
           }
 
         *inside_cost += ncopies * (vect_get_stmt_cost (vector_load)
-          + vect_get_stmt_cost (vector_stmt));
+                                  + vect_get_stmt_cost (vec_perm));
+
+        if (vect_print_dump_info (REPORT_COST))
+          fprintf (vect_dump,
+                  "vect_model_load_cost: explicit realign optimized");
+
         break;
       }
 
@@ -1172,8 +1240,8 @@ vect_get_vec_def_for_operand (tree op, gimple stmt, tree *scalar_def)
       print_generic_expr (vect_dump, op, TDF_SLIM);
     }
 
-  is_simple_use = vect_is_simple_use (op, loop_vinfo, NULL, &def_stmt, &def,
-                                      &dt);
+  is_simple_use = vect_is_simple_use (op, stmt, loop_vinfo, NULL,
+                                     &def_stmt, &def, &dt);
   gcc_assert (is_simple_use);
   if (vect_print_dump_info (REPORT_DETAILS))
     {
@@ -1505,7 +1573,8 @@ vectorizable_function (gimple call, tree vectype_out, tree vectype_in)
    Return FALSE if not a vectorizable STMT, TRUE otherwise.  */
 
 static bool
-vectorizable_call (gimple stmt, gimple_stmt_iterator *gsi, gimple *vec_stmt)
+vectorizable_call (gimple stmt, gimple_stmt_iterator *gsi, gimple *vec_stmt,
+                  slp_tree slp_node)
 {
   tree vec_dest;
   tree scalar_dest;
@@ -1516,6 +1585,7 @@ vectorizable_call (gimple stmt, gimple_stmt_iterator *gsi, gimple *vec_stmt)
   int nunits_in;
   int nunits_out;
   loop_vec_info loop_vinfo = STMT_VINFO_LOOP_VINFO (stmt_info);
+  bb_vec_info bb_vinfo = STMT_VINFO_BB_VINFO (stmt_info);
   tree fndecl, new_temp, def, rhs_type;
   gimple def_stmt;
   enum vect_def_type dt[3]
@@ -1527,19 +1597,12 @@ vectorizable_call (gimple stmt, gimple_stmt_iterator *gsi, gimple *vec_stmt)
   size_t i, nargs;
   tree lhs;
 
-  /* FORNOW: unsupported in basic block SLP.  */
-  gcc_assert (loop_vinfo);
-
-  if (!STMT_VINFO_RELEVANT_P (stmt_info))
+  if (!STMT_VINFO_RELEVANT_P (stmt_info) && !bb_vinfo)
     return false;
 
   if (STMT_VINFO_DEF_TYPE (stmt_info) != vect_internal_def)
     return false;
 
-  /* FORNOW: SLP not supported.  */
-  if (STMT_SLP_TYPE (stmt_info))
-    return false;
-
   /* Is STMT a vectorizable call?   */
   if (!is_gimple_call (stmt))
     return false;
@@ -1580,7 +1643,7 @@ vectorizable_call (gimple stmt, gimple_stmt_iterator *gsi, gimple *vec_stmt)
       if (!rhs_type)
        rhs_type = TREE_TYPE (op);
 
-      if (!vect_is_simple_use_1 (op, loop_vinfo, NULL,
+      if (!vect_is_simple_use_1 (op, stmt, loop_vinfo, bb_vinfo,
                                 &def_stmt, &def, &dt[i], &opvectype))
        {
          if (vect_print_dump_info (REPORT_DETAILS))
@@ -1642,7 +1705,9 @@ vectorizable_call (gimple stmt, gimple_stmt_iterator *gsi, gimple *vec_stmt)
 
   gcc_assert (!gimple_vuse (stmt));
 
-  if (modifier == NARROW)
+  if (slp_node || PURE_SLP_STMT (stmt_info))
+    ncopies = 1;
+  else if (modifier == NARROW)
     ncopies = LOOP_VINFO_VECT_FACTOR (loop_vinfo) / nunits_out;
   else
     ncopies = LOOP_VINFO_VECT_FACTOR (loop_vinfo) / nunits_in;
@@ -1681,6 +1746,50 @@ vectorizable_call (gimple stmt, gimple_stmt_iterator *gsi, gimple *vec_stmt)
          else
            VEC_truncate (tree, vargs, 0);
 
+         if (slp_node)
+           {
+             VEC (slp_void_p, heap) *vec_defs
+               = VEC_alloc (slp_void_p, heap, nargs);
+             VEC (tree, heap) *vec_oprnds0;
+
+             for (i = 0; i < nargs; i++)
+               VEC_quick_push (tree, vargs, gimple_call_arg (stmt, i));
+             vect_get_slp_defs (vargs, slp_node, &vec_defs, -1);
+             vec_oprnds0
+               = (VEC (tree, heap) *) VEC_index (slp_void_p, vec_defs, 0);
+
+             /* Arguments are ready.  Create the new vector stmt.  */
+             FOR_EACH_VEC_ELT (tree, vec_oprnds0, i, vec_oprnd0)
+               {
+                 size_t k;
+                 for (k = 0; k < nargs; k++)
+                   {
+                     VEC (tree, heap) *vec_oprndsk
+                       = (VEC (tree, heap) *)
+                         VEC_index (slp_void_p, vec_defs, k);
+                     VEC_replace (tree, vargs, k,
+                                  VEC_index (tree, vec_oprndsk, i));
+                   }
+                 new_stmt = gimple_build_call_vec (fndecl, vargs);
+                 new_temp = make_ssa_name (vec_dest, new_stmt);
+                 gimple_call_set_lhs (new_stmt, new_temp);
+                 vect_finish_stmt_generation (stmt, new_stmt, gsi);
+                 mark_symbols_for_renaming (new_stmt);
+                 VEC_quick_push (gimple, SLP_TREE_VEC_STMTS (slp_node),
+                                 new_stmt);
+               }
+
+             for (i = 0; i < nargs; i++)
+               {
+                 VEC (tree, heap) *vec_oprndsi
+                   = (VEC (tree, heap) *)
+                     VEC_index (slp_void_p, vec_defs, i);
+                 VEC_free (tree, heap, vec_oprndsi);
+               }
+             VEC_free (slp_void_p, heap, vec_defs);
+             continue;
+           }
+
          for (i = 0; i < nargs; i++)
            {
              op = gimple_call_arg (stmt, i);
@@ -1723,6 +1832,54 @@ vectorizable_call (gimple stmt, gimple_stmt_iterator *gsi, gimple *vec_stmt)
          else
            VEC_truncate (tree, vargs, 0);
 
+         if (slp_node)
+           {
+             VEC (slp_void_p, heap) *vec_defs
+               = VEC_alloc (slp_void_p, heap, nargs);
+             VEC (tree, heap) *vec_oprnds0;
+
+             for (i = 0; i < nargs; i++)
+               VEC_quick_push (tree, vargs, gimple_call_arg (stmt, i));
+             vect_get_slp_defs (vargs, slp_node, &vec_defs, -1);
+             vec_oprnds0
+               = (VEC (tree, heap) *) VEC_index (slp_void_p, vec_defs, 0);
+
+             /* Arguments are ready.  Create the new vector stmt.  */
+             for (i = 0; VEC_iterate (tree, vec_oprnds0, i, vec_oprnd0);
+                  i += 2)
+               {
+                 size_t k;
+                 VEC_truncate (tree, vargs, 0);
+                 for (k = 0; k < nargs; k++)
+                   {
+                     VEC (tree, heap) *vec_oprndsk
+                       = (VEC (tree, heap) *)
+                         VEC_index (slp_void_p, vec_defs, k);
+                     VEC_quick_push (tree, vargs,
+                                     VEC_index (tree, vec_oprndsk, i));
+                     VEC_quick_push (tree, vargs,
+                                     VEC_index (tree, vec_oprndsk, i + 1));
+                   }
+                 new_stmt = gimple_build_call_vec (fndecl, vargs);
+                 new_temp = make_ssa_name (vec_dest, new_stmt);
+                 gimple_call_set_lhs (new_stmt, new_temp);
+                 vect_finish_stmt_generation (stmt, new_stmt, gsi);
+                 mark_symbols_for_renaming (new_stmt);
+                 VEC_quick_push (gimple, SLP_TREE_VEC_STMTS (slp_node),
+                                 new_stmt);
+               }
+
+             for (i = 0; i < nargs; i++)
+               {
+                 VEC (tree, heap) *vec_oprndsi
+                   = (VEC (tree, heap) *)
+                     VEC_index (slp_void_p, vec_defs, i);
+                 VEC_free (tree, heap, vec_oprndsi);
+               }
+             VEC_free (slp_void_p, heap, vec_defs);
+             continue;
+           }
+
          for (i = 0; i < nargs; i++)
            {
              op = gimple_call_arg (stmt, i);
@@ -1781,6 +1938,9 @@ vectorizable_call (gimple stmt, gimple_stmt_iterator *gsi, gimple *vec_stmt)
      it defines is mapped to the new definition.  So just replace
      rhs of the statement with something harmless.  */
 
+  if (slp_node)
+    return true;
+
   type = TREE_TYPE (scalar_dest);
   if (is_pattern_stmt_p (stmt_info))
     lhs = gimple_call_lhs (STMT_VINFO_RELATED_STMT (stmt_info));
@@ -2095,7 +2255,7 @@ vectorizable_conversion (gimple stmt, gimple_stmt_iterator *gsi,
     }
 
   /* Check the operands of the operation.  */
-  if (!vect_is_simple_use_1 (op0, loop_vinfo, bb_vinfo,
+  if (!vect_is_simple_use_1 (op0, stmt, loop_vinfo, bb_vinfo,
                             &def_stmt, &def, &dt[0], &vectype_in))
     {
       if (vect_print_dump_info (REPORT_DETAILS))
@@ -2111,11 +2271,11 @@ vectorizable_conversion (gimple stmt, gimple_stmt_iterator *gsi,
       /* For WIDEN_MULT_EXPR, if OP0 is a constant, use the type of
         OP1.  */
       if (CONSTANT_CLASS_P (op0))
-       ok = vect_is_simple_use_1 (op1, loop_vinfo, NULL,
+       ok = vect_is_simple_use_1 (op1, stmt, loop_vinfo, NULL,
                                   &def_stmt, &def, &dt[1], &vectype_in);
       else
-       ok = vect_is_simple_use (op1, loop_vinfo, NULL, &def_stmt, &def,
-                                &dt[1]);
+       ok = vect_is_simple_use (op1, stmt, loop_vinfo, NULL, &def_stmt,
+                                &def, &dt[1]);
 
       if (!ok)
        {
@@ -2279,16 +2439,19 @@ vectorizable_conversion (gimple stmt, gimple_stmt_iterator *gsi,
       if (vect_print_dump_info (REPORT_DETAILS))
        fprintf (vect_dump, "=== vectorizable_conversion ===");
       if (code == FIX_TRUNC_EXPR || code == FLOAT_EXPR)
-       STMT_VINFO_TYPE (stmt_info) = type_conversion_vec_info_type;
+        {
+         STMT_VINFO_TYPE (stmt_info) = type_conversion_vec_info_type;
+         vect_model_simple_cost (stmt_info, ncopies, dt, NULL);
+       }
       else if (modifier == NARROW)
        {
          STMT_VINFO_TYPE (stmt_info) = type_demotion_vec_info_type;
-         vect_model_simple_cost (stmt_info, ncopies, dt, NULL);
+         vect_model_promotion_demotion_cost (stmt_info, dt, multi_step_cvt);
        }
       else
        {
          STMT_VINFO_TYPE (stmt_info) = type_promotion_vec_info_type;
-         vect_model_simple_cost (stmt_info, 2 * ncopies, dt, NULL);
+         vect_model_promotion_demotion_cost (stmt_info, dt, multi_step_cvt);
        }
       VEC_free (tree, heap, interm_types);
       return true;
@@ -2312,7 +2475,9 @@ vectorizable_conversion (gimple stmt, gimple_stmt_iterator *gsi,
      from supportable_*_operation, and store them in the correct order
      for future use in vect_create_vectorized_*_stmts ().  */
   vec_dsts = VEC_alloc (tree, heap, multi_step_cvt + 1);
-  vec_dest = vect_create_destination_var (scalar_dest, vectype_out);
+  vec_dest = vect_create_destination_var (scalar_dest,
+                                         (cvt_type && modifier == WIDEN)
+                                         ? cvt_type : vectype_out);
   VEC_quick_push (tree, vec_dsts, vec_dest);
 
   if (multi_step_cvt)
@@ -2327,7 +2492,9 @@ vectorizable_conversion (gimple stmt, gimple_stmt_iterator *gsi,
     }
 
   if (cvt_type)
-    vec_dest = vect_create_destination_var (scalar_dest, cvt_type);
+    vec_dest = vect_create_destination_var (scalar_dest,
+                                           modifier == WIDEN
+                                           ? vectype_out : cvt_type);
 
   if (!slp_node)
     {
@@ -2640,7 +2807,7 @@ vectorizable_assignment (gimple stmt, gimple_stmt_iterator *gsi,
   if (code == VIEW_CONVERT_EXPR)
     op = TREE_OPERAND (op, 0);
 
-  if (!vect_is_simple_use_1 (op, loop_vinfo, bb_vinfo,
+  if (!vect_is_simple_use_1 (op, stmt, loop_vinfo, bb_vinfo,
                             &def_stmt, &def, &dt[0], &vectype_in))
     {
       if (vect_print_dump_info (REPORT_DETAILS))
@@ -2840,7 +3007,7 @@ vectorizable_shift (gimple stmt, gimple_stmt_iterator *gsi,
     }
 
   op0 = gimple_assign_rhs1 (stmt);
-  if (!vect_is_simple_use_1 (op0, loop_vinfo, bb_vinfo,
+  if (!vect_is_simple_use_1 (op0, stmt, loop_vinfo, bb_vinfo,
                              &def_stmt, &def, &dt[0], &vectype))
     {
       if (vect_print_dump_info (REPORT_DETAILS))
@@ -2870,8 +3037,8 @@ vectorizable_shift (gimple stmt, gimple_stmt_iterator *gsi,
     return false;
 
   op1 = gimple_assign_rhs2 (stmt);
-  if (!vect_is_simple_use_1 (op1, loop_vinfo, bb_vinfo, &def_stmt, &def,
-                            &dt[1], &op1_vectype))
+  if (!vect_is_simple_use_1 (op1, stmt, loop_vinfo, bb_vinfo, &def_stmt,
+                            &def, &dt[1], &op1_vectype))
     {
       if (vect_print_dump_info (REPORT_DETAILS))
         fprintf (vect_dump, "use not simple.");
@@ -3217,7 +3384,7 @@ vectorizable_operation (gimple stmt, gimple_stmt_iterator *gsi,
     }
 
   op0 = gimple_assign_rhs1 (stmt);
-  if (!vect_is_simple_use_1 (op0, loop_vinfo, bb_vinfo,
+  if (!vect_is_simple_use_1 (op0, stmt, loop_vinfo, bb_vinfo,
                             &def_stmt, &def, &dt[0], &vectype))
     {
       if (vect_print_dump_info (REPORT_DETAILS))
@@ -3249,8 +3416,8 @@ vectorizable_operation (gimple stmt, gimple_stmt_iterator *gsi,
   if (op_type == binary_op || op_type == ternary_op)
     {
       op1 = gimple_assign_rhs2 (stmt);
-      if (!vect_is_simple_use (op1, loop_vinfo, bb_vinfo, &def_stmt, &def,
-                               &dt[1]))
+      if (!vect_is_simple_use (op1, stmt, loop_vinfo, bb_vinfo, &def_stmt,
+                              &def, &dt[1]))
        {
          if (vect_print_dump_info (REPORT_DETAILS))
            fprintf (vect_dump, "use not simple.");
@@ -3260,8 +3427,8 @@ vectorizable_operation (gimple stmt, gimple_stmt_iterator *gsi,
   if (op_type == ternary_op)
     {
       op2 = gimple_assign_rhs3 (stmt);
-      if (!vect_is_simple_use (op2, loop_vinfo, bb_vinfo, &def_stmt, &def,
-                               &dt[2]))
+      if (!vect_is_simple_use (op2, stmt, loop_vinfo, bb_vinfo, &def_stmt,
+                              &def, &dt[2]))
        {
          if (vect_print_dump_info (REPORT_DETAILS))
            fprintf (vect_dump, "use not simple.");
@@ -3567,7 +3734,8 @@ vectorizable_store (gimple stmt, gimple_stmt_iterator *gsi, gimple *vec_stmt,
 
   gcc_assert (gimple_assign_single_p (stmt));
   op = gimple_assign_rhs1 (stmt);
-  if (!vect_is_simple_use (op, loop_vinfo, bb_vinfo, &def_stmt, &def, &dt))
+  if (!vect_is_simple_use (op, stmt, loop_vinfo, bb_vinfo, &def_stmt,
+                          &def, &dt))
     {
       if (vect_print_dump_info (REPORT_DETAILS))
         fprintf (vect_dump, "use not simple.");
@@ -3585,7 +3753,9 @@ vectorizable_store (gimple stmt, gimple_stmt_iterator *gsi, gimple *vec_stmt,
   if (!STMT_VINFO_DATA_REF (stmt_info))
     return false;
 
-  if (tree_int_cst_compare (DR_STEP (dr), size_zero_node) < 0)
+  if (tree_int_cst_compare (loop && nested_in_vect_loop_p (loop, stmt)
+                           ? STMT_VINFO_DR_STEP (stmt_info) : DR_STEP (dr),
+                           size_zero_node) < 0)
     {
       if (vect_print_dump_info (REPORT_DETAILS))
         fprintf (vect_dump, "negative step for store.");
@@ -3614,8 +3784,8 @@ vectorizable_store (gimple stmt, gimple_stmt_iterator *gsi, gimple *vec_stmt,
             {
              gcc_assert (gimple_assign_single_p (next_stmt));
              op = gimple_assign_rhs1 (next_stmt);
-              if (!vect_is_simple_use (op, loop_vinfo, bb_vinfo, &def_stmt,
-                                       &def, &dt))
+              if (!vect_is_simple_use (op, next_stmt, loop_vinfo, bb_vinfo,
+                                      &def_stmt, &def, &dt))
                 {
                   if (vect_print_dump_info (REPORT_DETAILS))
                     fprintf (vect_dump, "use not simple.");
@@ -3720,8 +3890,8 @@ vectorizable_store (gimple stmt, gimple_stmt_iterator *gsi, gimple *vec_stmt,
 
      Then permutation statements are generated:
 
-        VS5: vx5 = VEC_INTERLEAVE_HIGH_EXPR < vx0, vx3 >
-        VS6: vx6 = VEC_INTERLEAVE_LOW_EXPR < vx0, vx3 >
+       VS5: vx5 = VEC_PERM_EXPR < vx0, vx3, {0, 8, 1, 9, 2, 10, 3, 11} >
+       VS6: vx6 = VEC_PERM_EXPR < vx0, vx3, {4, 12, 5, 13, 6, 14, 7, 15} >
        ...
 
      And they are put in STMT_VINFO_VEC_STMT of the corresponding scalar stmts
@@ -3800,8 +3970,8 @@ vectorizable_store (gimple stmt, gimple_stmt_iterator *gsi, gimple *vec_stmt,
          for (i = 0; i < group_size; i++)
            {
              op = VEC_index (tree, oprnds, i);
-             vect_is_simple_use (op, loop_vinfo, bb_vinfo, &def_stmt, &def,
-                                 &dt);
+             vect_is_simple_use (op, NULL, loop_vinfo, bb_vinfo, &def_stmt,
+                                 &def, &dt);
              vec_oprnd = vect_get_vec_def_for_stmt_copy (dt, op);
              VEC_replace(tree, dr_chain, i, vec_oprnd);
              VEC_replace(tree, oprnds, i, vec_oprnd);
@@ -3914,23 +4084,17 @@ vectorizable_store (gimple stmt, gimple_stmt_iterator *gsi, gimple *vec_stmt,
   return true;
 }
 
-/* Given a vector type VECTYPE returns a builtin DECL to be used
-   for vector permutation and returns the mask that implements
-   reversal of the vector elements.  If that is impossible to do,
-   returns NULL.  */
+/* Given a vector type VECTYPE and permutation SEL returns
+   the VECTOR_CST mask that implements the permutation of the
+   vector elements.  If that is impossible to do, returns NULL.  */
 
-static tree
-perm_mask_for_reverse (tree vectype)
+tree
+vect_gen_perm_mask (tree vectype, unsigned char *sel)
 {
   tree mask_elt_type, mask_type, mask_vec;
   int i, nunits;
-  unsigned char *sel;
 
   nunits = TYPE_VECTOR_SUBPARTS (vectype);
-  sel = XALLOCAVEC (unsigned char, nunits);
-
-  for (i = 0; i < nunits; ++i)
-    sel[i] = nunits - 1 - i;
 
   if (!can_vec_perm_p (TYPE_MODE (vectype), false, sel))
     return NULL;
@@ -3941,33 +4105,52 @@ perm_mask_for_reverse (tree vectype)
   mask_type = get_vectype_for_scalar_type (mask_elt_type);
 
   mask_vec = NULL;
-  for (i = 0; i < nunits; i++)
-    mask_vec = tree_cons (NULL, build_int_cst (mask_elt_type, i), mask_vec);
+  for (i = nunits - 1; i >= 0; i--)
+    mask_vec = tree_cons (NULL, build_int_cst (mask_elt_type, sel[i]),
+                         mask_vec);
   mask_vec = build_vector (mask_type, mask_vec);
 
   return mask_vec;
 }
 
-/* Given a vector variable X, that was generated for the scalar LHS of
-   STMT, generate instructions to reverse the vector elements of X,
-   insert them a *GSI and return the permuted vector variable.  */
+/* Given a vector type VECTYPE returns the VECTOR_CST mask that implements
+   reversal of the vector elements.  If that is impossible to do,
+   returns NULL.  */
 
 static tree
-reverse_vec_elements (tree x, gimple stmt, gimple_stmt_iterator *gsi)
+perm_mask_for_reverse (tree vectype)
+{
+  int i, nunits;
+  unsigned char *sel;
+
+  nunits = TYPE_VECTOR_SUBPARTS (vectype);
+  sel = XALLOCAVEC (unsigned char, nunits);
+
+  for (i = 0; i < nunits; ++i)
+    sel[i] = nunits - 1 - i;
+
+  return vect_gen_perm_mask (vectype, sel);
+}
+
+/* Given a vector variable X and Y, that was generated for the scalar
+   STMT, generate instructions to permute the vector elements of X and Y
+   using permutation mask MASK_VEC, insert them at *GSI and return the
+   permuted vector variable.  */
+
+static tree
+permute_vec_elements (tree x, tree y, tree mask_vec, gimple stmt,
+                     gimple_stmt_iterator *gsi)
 {
   tree vectype = TREE_TYPE (x);
-  tree mask_vec, perm_dest, data_ref;
+  tree perm_dest, data_ref;
   gimple perm_stmt;
 
-  mask_vec = perm_mask_for_reverse (vectype);
-
   perm_dest = vect_create_destination_var (gimple_assign_lhs (stmt), vectype);
+  data_ref = make_ssa_name (perm_dest, NULL);
 
   /* Generate the permute statement.  */
-  perm_stmt = gimple_build_assign_with_ops3 (VEC_PERM_EXPR, perm_dest,
-                                            x, x, mask_vec);
-  data_ref = make_ssa_name (perm_dest, perm_stmt);
-  gimple_set_lhs (perm_stmt, data_ref);
+  perm_stmt = gimple_build_assign_with_ops3 (VEC_PERM_EXPR, data_ref,
+                                            x, y, mask_vec);
   vect_finish_stmt_generation (stmt, perm_stmt, gsi);
 
   return data_ref;
@@ -4026,6 +4209,10 @@ vectorizable_load (gimple stmt, gimple_stmt_iterator *gsi, gimple *vec_stmt,
   bb_vec_info bb_vinfo = STMT_VINFO_BB_VINFO (stmt_info);
   int vf;
   tree aggr_type;
+  tree gather_base = NULL_TREE, gather_off = NULL_TREE;
+  tree gather_off_vectype = NULL_TREE, gather_decl = NULL_TREE;
+  int gather_scale = 1;
+  enum vect_def_type gather_dt = vect_unknown_def_type;
 
   if (loop_vinfo)
     {
@@ -4081,7 +4268,10 @@ vectorizable_load (gimple stmt, gimple_stmt_iterator *gsi, gimple *vec_stmt,
   if (!STMT_VINFO_DATA_REF (stmt_info))
     return false;
 
-  negative = tree_int_cst_compare (DR_STEP (dr), size_zero_node) < 0;
+  negative = tree_int_cst_compare (nested_in_vect_loop
+                                  ? STMT_VINFO_DR_STEP (stmt_info)
+                                  : DR_STEP (dr),
+                                  size_zero_node) < 0;
   if (negative && ncopies > 1)
     {
       if (vect_print_dump_info (REPORT_DETAILS))
@@ -4106,7 +4296,7 @@ vectorizable_load (gimple stmt, gimple_stmt_iterator *gsi, gimple *vec_stmt,
     {
       strided_load = true;
       /* FORNOW */
-      gcc_assert (! nested_in_vect_loop);
+      gcc_assert (! nested_in_vect_loop && !STMT_VINFO_GATHER_P (stmt_info));
 
       first_stmt = GROUP_FIRST_ELEMENT (stmt_info);
       if (!slp && !PURE_SLP_STMT (stmt_info))
@@ -4121,7 +4311,7 @@ vectorizable_load (gimple stmt, gimple_stmt_iterator *gsi, gimple *vec_stmt,
 
   if (negative)
     {
-      gcc_assert (!strided_load);
+      gcc_assert (!strided_load && !STMT_VINFO_GATHER_P (stmt_info));
       alignment_support_scheme = vect_supportable_dr_alignment (dr, false);
       if (alignment_support_scheme != dr_aligned
          && alignment_support_scheme != dr_unaligned_supported)
@@ -4138,6 +4328,23 @@ vectorizable_load (gimple stmt, gimple_stmt_iterator *gsi, gimple *vec_stmt,
        }
     }
 
+  if (STMT_VINFO_GATHER_P (stmt_info))
+    {
+      gimple def_stmt;
+      tree def;
+      gather_decl = vect_check_gather (stmt, loop_vinfo, &gather_base,
+                                      &gather_off, &gather_scale);
+      gcc_assert (gather_decl);
+      if (!vect_is_simple_use_1 (gather_off, NULL, loop_vinfo, bb_vinfo,
+                                &def_stmt, &def, &gather_dt,
+                                &gather_off_vectype))
+       {
+         if (vect_print_dump_info (REPORT_DETAILS))
+           fprintf (vect_dump, "gather index use not simple.");
+         return false;
+       }
+    }
+
   if (!vec_stmt) /* transformation not required.  */
     {
       STMT_VINFO_TYPE (stmt_info) = load_vec_info_type;
@@ -4150,6 +4357,161 @@ vectorizable_load (gimple stmt, gimple_stmt_iterator *gsi, gimple *vec_stmt,
 
   /** Transform.  **/
 
+  if (STMT_VINFO_GATHER_P (stmt_info))
+    {
+      tree vec_oprnd0 = NULL_TREE, op;
+      tree arglist = TYPE_ARG_TYPES (TREE_TYPE (gather_decl));
+      tree rettype, srctype, ptrtype, idxtype, masktype, scaletype;
+      tree ptr, mask, var, scale, perm_mask = NULL_TREE, prev_res = NULL_TREE;
+      edge pe = loop_preheader_edge (loop);
+      gimple_seq seq;
+      basic_block new_bb;
+      enum { NARROW, NONE, WIDEN } modifier;
+      int gather_off_nunits = TYPE_VECTOR_SUBPARTS (gather_off_vectype);
+
+      if (nunits == gather_off_nunits)
+       modifier = NONE;
+      else if (nunits == gather_off_nunits / 2)
+       {
+         unsigned char *sel = XALLOCAVEC (unsigned char, gather_off_nunits);
+         modifier = WIDEN;
+
+         for (i = 0; i < gather_off_nunits; ++i)
+           sel[i] = i | nunits;
+
+         perm_mask = vect_gen_perm_mask (gather_off_vectype, sel);
+         gcc_assert (perm_mask != NULL_TREE);
+       }
+      else if (nunits == gather_off_nunits * 2)
+       {
+         unsigned char *sel = XALLOCAVEC (unsigned char, nunits);
+         modifier = NARROW;
+
+         for (i = 0; i < nunits; ++i)
+           sel[i] = i < gather_off_nunits
+                    ? i : i + nunits - gather_off_nunits;
+
+         perm_mask = vect_gen_perm_mask (vectype, sel);
+         gcc_assert (perm_mask != NULL_TREE);
+         ncopies *= 2;
+       }
+      else
+       gcc_unreachable ();
+
+      rettype = TREE_TYPE (TREE_TYPE (gather_decl));
+      srctype = TREE_VALUE (arglist); arglist = TREE_CHAIN (arglist);
+      ptrtype = TREE_VALUE (arglist); arglist = TREE_CHAIN (arglist);
+      idxtype = TREE_VALUE (arglist); arglist = TREE_CHAIN (arglist);
+      masktype = TREE_VALUE (arglist); arglist = TREE_CHAIN (arglist);
+      scaletype = TREE_VALUE (arglist);
+      gcc_checking_assert (types_compatible_p (srctype, rettype)
+                          && types_compatible_p (srctype, masktype));
+
+      vec_dest = vect_create_destination_var (scalar_dest, vectype);
+
+      ptr = fold_convert (ptrtype, gather_base);
+      if (!is_gimple_min_invariant (ptr))
+       {
+         ptr = force_gimple_operand (ptr, &seq, true, NULL_TREE);
+         new_bb = gsi_insert_seq_on_edge_immediate (pe, seq);
+         gcc_assert (!new_bb);
+       }
+
+      /* Currently we support only unconditional gather loads,
+        so mask should be all ones.  */
+      if (TREE_CODE (TREE_TYPE (masktype)) == INTEGER_TYPE)
+       mask = build_int_cst (TREE_TYPE (masktype), -1);
+      else if (SCALAR_FLOAT_TYPE_P (TREE_TYPE (masktype)))
+       {
+         REAL_VALUE_TYPE r;
+         long tmp[6];
+         for (j = 0; j < 6; ++j)
+           tmp[j] = -1;
+         real_from_target (&r, tmp, TYPE_MODE (TREE_TYPE (masktype)));
+         mask = build_real (TREE_TYPE (masktype), r);
+       }
+      else
+       gcc_unreachable ();
+      mask = build_vector_from_val (masktype, mask);
+      mask = vect_init_vector (stmt, mask, masktype, NULL);
+
+      scale = build_int_cst (scaletype, gather_scale);
+
+      prev_stmt_info = NULL;
+      for (j = 0; j < ncopies; ++j)
+       {
+         if (modifier == WIDEN && (j & 1))
+           op = permute_vec_elements (vec_oprnd0, vec_oprnd0,
+                                      perm_mask, stmt, gsi);
+         else if (j == 0)
+           op = vec_oprnd0
+             = vect_get_vec_def_for_operand (gather_off, stmt, NULL);
+         else
+           op = vec_oprnd0
+             = vect_get_vec_def_for_stmt_copy (gather_dt, vec_oprnd0);
+
+         if (!useless_type_conversion_p (idxtype, TREE_TYPE (op)))
+           {
+             gcc_assert (TYPE_VECTOR_SUBPARTS (TREE_TYPE (op))
+                         == TYPE_VECTOR_SUBPARTS (idxtype));
+             var = vect_get_new_vect_var (idxtype, vect_simple_var, NULL);
+             add_referenced_var (var);
+             var = make_ssa_name (var, NULL);
+             op = build1 (VIEW_CONVERT_EXPR, idxtype, op);
+             new_stmt
+               = gimple_build_assign_with_ops (VIEW_CONVERT_EXPR, var,
+                                               op, NULL_TREE);
+             vect_finish_stmt_generation (stmt, new_stmt, gsi);
+             op = var;
+           }
+
+         new_stmt
+           = gimple_build_call (gather_decl, 5, mask, ptr, op, mask, scale);
+
+         if (!useless_type_conversion_p (vectype, rettype))
+           {
+             gcc_assert (TYPE_VECTOR_SUBPARTS (vectype)
+                         == TYPE_VECTOR_SUBPARTS (rettype));
+             var = vect_get_new_vect_var (rettype, vect_simple_var, NULL);
+             add_referenced_var (var);
+             op = make_ssa_name (var, new_stmt);
+             gimple_call_set_lhs (new_stmt, op);
+             vect_finish_stmt_generation (stmt, new_stmt, gsi);
+             var = make_ssa_name (vec_dest, NULL);
+             op = build1 (VIEW_CONVERT_EXPR, vectype, op);
+             new_stmt
+               = gimple_build_assign_with_ops (VIEW_CONVERT_EXPR, var, op,
+                                               NULL_TREE);
+           }
+         else
+           {
+             var = make_ssa_name (vec_dest, new_stmt);
+             gimple_call_set_lhs (new_stmt, var);
+           }
+
+         vect_finish_stmt_generation (stmt, new_stmt, gsi);
+
+         if (modifier == NARROW)
+           {
+             if ((j & 1) == 0)
+               {
+                 prev_res = var;
+                 continue;
+               }
+             var = permute_vec_elements (prev_res, var,
+                                         perm_mask, stmt, gsi);
+             new_stmt = SSA_NAME_DEF_STMT (var);
+           }
+
+         if (prev_stmt_info == NULL)
+           STMT_VINFO_VEC_STMT (stmt_info) = *vec_stmt = new_stmt;
+         else
+           STMT_VINFO_RELATED_STMT (prev_stmt_info) = new_stmt;
+         prev_stmt_info = vinfo_for_stmt (new_stmt);
+       }
+      return true;
+    }
+
   if (strided_load)
     {
       first_stmt = GROUP_FIRST_ELEMENT (stmt_info);
@@ -4245,8 +4607,8 @@ vectorizable_load (gimple stmt, gimple_stmt_iterator *gsi, gimple *vec_stmt,
 
      Then permutation statements are generated:
 
-     VS5: vx5 = VEC_EXTRACT_EVEN_EXPR < vx0, vx1 >
-     VS6: vx6 = VEC_EXTRACT_ODD_EXPR < vx0, vx1 >
+     VS5: vx5 = VEC_PERM_EXPR < vx0, vx1, { 0, 2, ..., i*2 } >
+     VS6: vx6 = VEC_PERM_EXPR < vx0, vx1, { 1, 3, ..., i*2+1 } >
        ...
 
      And they are put in STMT_VINFO_VEC_STMT of the corresponding scalar stmts
@@ -4296,7 +4658,7 @@ vectorizable_load (gimple stmt, gimple_stmt_iterator *gsi, gimple *vec_stmt,
      This can only occur when vectorizing memory accesses in the inner-loop
      nested within an outer-loop that is being vectorized.  */
 
-  if (loop && nested_in_vect_loop_p (loop, stmt)
+  if (nested_in_vect_loop
       && (TREE_INT_CST_LOW (DR_STEP (dr))
          % GET_MODE_SIZE (TYPE_MODE (vectype)) != 0))
     {
@@ -4541,7 +4903,9 @@ vectorizable_load (gimple stmt, gimple_stmt_iterator *gsi, gimple *vec_stmt,
 
              if (negative)
                {
-                 new_temp = reverse_vec_elements (new_temp, stmt, gsi);
+                 tree perm_mask = perm_mask_for_reverse (vectype);
+                 new_temp = permute_vec_elements (new_temp, new_temp,
+                                                  perm_mask, stmt, gsi);
                  new_stmt = SSA_NAME_DEF_STMT (new_temp);
                }
 
@@ -4606,7 +4970,8 @@ vectorizable_load (gimple stmt, gimple_stmt_iterator *gsi, gimple *vec_stmt,
    condition operands are supportable using vec_is_simple_use.  */
 
 static bool
-vect_is_simple_cond (tree cond, loop_vec_info loop_vinfo, tree *comp_vectype)
+vect_is_simple_cond (tree cond, gimple stmt, loop_vec_info loop_vinfo,
+                    bb_vec_info bb_vinfo, tree *comp_vectype)
 {
   tree lhs, rhs;
   tree def;
@@ -4622,8 +4987,8 @@ vect_is_simple_cond (tree cond, loop_vec_info loop_vinfo, tree *comp_vectype)
   if (TREE_CODE (lhs) == SSA_NAME)
     {
       gimple lhs_def_stmt = SSA_NAME_DEF_STMT (lhs);
-      if (!vect_is_simple_use_1 (lhs, loop_vinfo, NULL, &lhs_def_stmt, &def,
-                                &dt, &vectype1))
+      if (!vect_is_simple_use_1 (lhs, stmt, loop_vinfo, bb_vinfo,
+                                &lhs_def_stmt, &def, &dt, &vectype1))
        return false;
     }
   else if (TREE_CODE (lhs) != INTEGER_CST && TREE_CODE (lhs) != REAL_CST
@@ -4633,11 +4998,11 @@ vect_is_simple_cond (tree cond, loop_vec_info loop_vinfo, tree *comp_vectype)
   if (TREE_CODE (rhs) == SSA_NAME)
     {
       gimple rhs_def_stmt = SSA_NAME_DEF_STMT (rhs);
-      if (!vect_is_simple_use_1 (rhs, loop_vinfo, NULL, &rhs_def_stmt, &def,
-                                &dt, &vectype2))
+      if (!vect_is_simple_use_1 (rhs, stmt, loop_vinfo, bb_vinfo,
+                                &rhs_def_stmt, &def, &dt, &vectype2))
        return false;
     }
-  else if (TREE_CODE (rhs) != INTEGER_CST  && TREE_CODE (rhs) != REAL_CST
+  else if (TREE_CODE (rhs) != INTEGER_CST && TREE_CODE (rhs) != REAL_CST
           && TREE_CODE (rhs) != FIXED_CST)
     return false;
 
@@ -4660,14 +5025,15 @@ vect_is_simple_cond (tree cond, loop_vec_info loop_vinfo, tree *comp_vectype)
 
 bool
 vectorizable_condition (gimple stmt, gimple_stmt_iterator *gsi,
-                       gimple *vec_stmt, tree reduc_def, int reduc_index)
+                       gimple *vec_stmt, tree reduc_def, int reduc_index,
+                       slp_tree slp_node)
 {
   tree scalar_dest = NULL_TREE;
   tree vec_dest = NULL_TREE;
   tree cond_expr, then_clause, else_clause;
   stmt_vec_info stmt_info = vinfo_for_stmt (stmt);
   tree vectype = STMT_VINFO_VECTYPE (stmt_info);
-  tree comp_vectype;
+  tree comp_vectype = NULL_TREE;
   tree vec_cond_lhs = NULL_TREE, vec_cond_rhs = NULL_TREE;
   tree vec_then_clause = NULL_TREE, vec_else_clause = NULL_TREE;
   tree vec_compare, vec_cond_expr;
@@ -4676,23 +5042,27 @@ vectorizable_condition (gimple stmt, gimple_stmt_iterator *gsi,
   tree def;
   enum vect_def_type dt, dts[4];
   int nunits = TYPE_VECTOR_SUBPARTS (vectype);
-  int ncopies = LOOP_VINFO_VECT_FACTOR (loop_vinfo) / nunits;
+  int ncopies;
   enum tree_code code;
   stmt_vec_info prev_stmt_info = NULL;
-  int j;
-
-  /* FORNOW: unsupported in basic block SLP.  */
-  gcc_assert (loop_vinfo);
+  int i, j;
+  bb_vec_info bb_vinfo = STMT_VINFO_BB_VINFO (stmt_info);
+  VEC (tree, heap) *vec_oprnds0 = NULL, *vec_oprnds1 = NULL;
+  VEC (tree, heap) *vec_oprnds2 = NULL, *vec_oprnds3 = NULL;
 
-  /* FORNOW: SLP not supported.  */
-  if (STMT_SLP_TYPE (stmt_info))
-    return false;
+  if (slp_node || PURE_SLP_STMT (stmt_info))
+    ncopies = 1;
+  else
+    ncopies = LOOP_VINFO_VECT_FACTOR (loop_vinfo) / nunits;
 
   gcc_assert (ncopies >= 1);
   if (reduc_index && ncopies > 1)
     return false; /* FORNOW */
 
-  if (!STMT_VINFO_RELEVANT_P (stmt_info))
+  if (reduc_index && STMT_SLP_TYPE (stmt_info))
+    return false;
+
+  if (!STMT_VINFO_RELEVANT_P (stmt_info) && !bb_vinfo)
     return false;
 
   if (STMT_VINFO_DEF_TYPE (stmt_info) != vect_internal_def
@@ -4721,14 +5091,15 @@ vectorizable_condition (gimple stmt, gimple_stmt_iterator *gsi,
   then_clause = gimple_assign_rhs2 (stmt);
   else_clause = gimple_assign_rhs3 (stmt);
 
-  if (!vect_is_simple_cond (cond_expr, loop_vinfo, &comp_vectype)
+  if (!vect_is_simple_cond (cond_expr, stmt, loop_vinfo, bb_vinfo,
+                           &comp_vectype)
       || !comp_vectype)
     return false;
 
   if (TREE_CODE (then_clause) == SSA_NAME)
     {
       gimple then_def_stmt = SSA_NAME_DEF_STMT (then_clause);
-      if (!vect_is_simple_use (then_clause, loop_vinfo, NULL,
+      if (!vect_is_simple_use (then_clause, stmt, loop_vinfo, bb_vinfo,
                               &then_def_stmt, &def, &dt))
        return false;
     }
@@ -4740,7 +5111,7 @@ vectorizable_condition (gimple stmt, gimple_stmt_iterator *gsi,
   if (TREE_CODE (else_clause) == SSA_NAME)
     {
       gimple else_def_stmt = SSA_NAME_DEF_STMT (else_clause);
-      if (!vect_is_simple_use (else_clause, loop_vinfo, NULL,
+      if (!vect_is_simple_use (else_clause, stmt, loop_vinfo, bb_vinfo,
                               &else_def_stmt, &def, &dt))
        return false;
     }
@@ -4755,7 +5126,15 @@ vectorizable_condition (gimple stmt, gimple_stmt_iterator *gsi,
       return expand_vec_cond_expr_p (vectype, comp_vectype);
     }
 
-  /* Transform */
+  /* Transform.  */
+
+  if (!slp_node)
+    {
+      vec_oprnds0 = VEC_alloc (tree, heap, 1);
+      vec_oprnds1 = VEC_alloc (tree, heap, 1);
+      vec_oprnds2 = VEC_alloc (tree, heap, 1);
+      vec_oprnds3 = VEC_alloc (tree, heap, 1);
+    }
 
   /* Handle def.  */
   scalar_dest = gimple_assign_lhs (stmt);
@@ -4764,67 +5143,118 @@ vectorizable_condition (gimple stmt, gimple_stmt_iterator *gsi,
   /* Handle cond expr.  */
   for (j = 0; j < ncopies; j++)
     {
-      gimple new_stmt;
+      gimple new_stmt = NULL;
       if (j == 0)
        {
-         gimple gtemp;
-         vec_cond_lhs =
+          if (slp_node)
+            {
+              VEC (tree, heap) *ops = VEC_alloc (tree, heap, 4);
+              VEC (slp_void_p, heap) *vec_defs;
+
+             vec_defs = VEC_alloc (slp_void_p, heap, 4);
+              VEC_safe_push (tree, heap, ops, TREE_OPERAND (cond_expr, 0));
+              VEC_safe_push (tree, heap, ops, TREE_OPERAND (cond_expr, 1));
+              VEC_safe_push (tree, heap, ops, then_clause);
+              VEC_safe_push (tree, heap, ops, else_clause);
+              vect_get_slp_defs (ops, slp_node, &vec_defs, -1);
+              vec_oprnds3 = (VEC (tree, heap) *) VEC_pop (slp_void_p, vec_defs);
+              vec_oprnds2 = (VEC (tree, heap) *) VEC_pop (slp_void_p, vec_defs);
+              vec_oprnds1 = (VEC (tree, heap) *) VEC_pop (slp_void_p, vec_defs);
+              vec_oprnds0 = (VEC (tree, heap) *) VEC_pop (slp_void_p, vec_defs);
+
+              VEC_free (tree, heap, ops);
+              VEC_free (slp_void_p, heap, vec_defs);
+            }
+          else
+            {
+             gimple gtemp;
+             vec_cond_lhs =
              vect_get_vec_def_for_operand (TREE_OPERAND (cond_expr, 0),
                                            stmt, NULL);
-         vect_is_simple_use (TREE_OPERAND (cond_expr, 0), loop_vinfo,
-                             NULL, &gtemp, &def, &dts[0]);
-         vec_cond_rhs =
-             vect_get_vec_def_for_operand (TREE_OPERAND (cond_expr, 1),
-                                           stmt, NULL);
-         vect_is_simple_use (TREE_OPERAND (cond_expr, 1), loop_vinfo,
-                             NULL, &gtemp, &def, &dts[1]);
-         if (reduc_index == 1)
-           vec_then_clause = reduc_def;
-         else
-           {
-             vec_then_clause = vect_get_vec_def_for_operand (then_clause,
-                                                             stmt, NULL);
-             vect_is_simple_use (then_clause, loop_vinfo,
-                                 NULL, &gtemp, &def, &dts[2]);
-           }
-         if (reduc_index == 2)
-           vec_else_clause = reduc_def;
-         else
-           {
-             vec_else_clause = vect_get_vec_def_for_operand (else_clause,
+             vect_is_simple_use (TREE_OPERAND (cond_expr, 0), stmt,
+                                 loop_vinfo, NULL, &gtemp, &def, &dts[0]);
+
+             vec_cond_rhs =
+               vect_get_vec_def_for_operand (TREE_OPERAND (cond_expr, 1),
+                                               stmt, NULL);
+             vect_is_simple_use (TREE_OPERAND (cond_expr, 1), stmt,
+                                 loop_vinfo, NULL, &gtemp, &def, &dts[1]);
+             if (reduc_index == 1)
+               vec_then_clause = reduc_def;
+             else
+               {
+                 vec_then_clause = vect_get_vec_def_for_operand (then_clause,
+                                                             stmt, NULL);
+                 vect_is_simple_use (then_clause, stmt, loop_vinfo,
+                                         NULL, &gtemp, &def, &dts[2]);
+               }
+             if (reduc_index == 2)
+               vec_else_clause = reduc_def;
+             else
+               {
+                 vec_else_clause = vect_get_vec_def_for_operand (else_clause,
                                                              stmt, NULL);
-             vect_is_simple_use (else_clause, loop_vinfo,
+                 vect_is_simple_use (else_clause, stmt, loop_vinfo,
                                  NULL, &gtemp, &def, &dts[3]);
+               }
            }
        }
       else
        {
-         vec_cond_lhs = vect_get_vec_def_for_stmt_copy (dts[0], vec_cond_lhs);
-         vec_cond_rhs = vect_get_vec_def_for_stmt_copy (dts[1], vec_cond_rhs);
+         vec_cond_lhs = vect_get_vec_def_for_stmt_copy (dts[0],
+                                               VEC_pop (tree, vec_oprnds0));
+         vec_cond_rhs = vect_get_vec_def_for_stmt_copy (dts[1],
+                                               VEC_pop (tree, vec_oprnds1));
          vec_then_clause = vect_get_vec_def_for_stmt_copy (dts[2],
-                                                           vec_then_clause);
+                                               VEC_pop (tree, vec_oprnds2));
          vec_else_clause = vect_get_vec_def_for_stmt_copy (dts[3],
-                                                           vec_else_clause);
+                                               VEC_pop (tree, vec_oprnds3));
+       }
+
+      if (!slp_node)
+        {
+         VEC_quick_push (tree, vec_oprnds0, vec_cond_lhs);
+         VEC_quick_push (tree, vec_oprnds1, vec_cond_rhs);
+         VEC_quick_push (tree, vec_oprnds2, vec_then_clause);
+         VEC_quick_push (tree, vec_oprnds3, vec_else_clause);
        }
 
       /* Arguments are ready.  Create the new vector stmt.  */
-      vec_compare = build2 (TREE_CODE (cond_expr), vectype,
-                           vec_cond_lhs, vec_cond_rhs);
-      vec_cond_expr = build3 (VEC_COND_EXPR, vectype,
-                             vec_compare, vec_then_clause, vec_else_clause);
+      FOR_EACH_VEC_ELT (tree, vec_oprnds0, i, vec_cond_lhs)
+        {
+          vec_cond_rhs = VEC_index (tree, vec_oprnds1, i);
+          vec_then_clause = VEC_index (tree, vec_oprnds2, i);
+          vec_else_clause = VEC_index (tree, vec_oprnds3, i);
 
-      new_stmt = gimple_build_assign (vec_dest, vec_cond_expr);
-      new_temp = make_ssa_name (vec_dest, new_stmt);
-      gimple_assign_set_lhs (new_stmt, new_temp);
-      vect_finish_stmt_generation (stmt, new_stmt, gsi);
-      if (j == 0)
-        STMT_VINFO_VEC_STMT (stmt_info) = *vec_stmt = new_stmt;
-      else
-        STMT_VINFO_RELATED_STMT (prev_stmt_info) = new_stmt;
+          vec_compare = build2 (TREE_CODE (cond_expr), vectype,
+                              vec_cond_lhs, vec_cond_rhs);
+          vec_cond_expr = build3 (VEC_COND_EXPR, vectype,
+                        vec_compare, vec_then_clause, vec_else_clause);
 
-      prev_stmt_info = vinfo_for_stmt (new_stmt);
+          new_stmt = gimple_build_assign (vec_dest, vec_cond_expr);
+          new_temp = make_ssa_name (vec_dest, new_stmt);
+          gimple_assign_set_lhs (new_stmt, new_temp);
+          vect_finish_stmt_generation (stmt, new_stmt, gsi);
+          if (slp_node)
+            VEC_quick_push (gimple, SLP_TREE_VEC_STMTS (slp_node), new_stmt);
+        }
+
+        if (slp_node)
+          continue;
+
+        if (j == 0)
+          STMT_VINFO_VEC_STMT (stmt_info) = *vec_stmt = new_stmt;
+        else
+          STMT_VINFO_RELATED_STMT (prev_stmt_info) = new_stmt;
+
+        prev_stmt_info = vinfo_for_stmt (new_stmt);
     }
 
+  VEC_free (tree, heap, vec_oprnds0);
+  VEC_free (tree, heap, vec_oprnds1);
+  VEC_free (tree, heap, vec_oprnds2);
+  VEC_free (tree, heap, vec_oprnds3);
+
   return true;
 }
 
@@ -4839,7 +5269,8 @@ vect_analyze_stmt (gimple stmt, bool *need_to_vectorize, slp_tree node)
   enum vect_relevant relevance = STMT_VINFO_RELEVANT (stmt_info);
   bool ok;
   tree scalar_type, vectype;
-  gimple pattern_stmt, pattern_def_stmt;
+  gimple pattern_stmt;
+  gimple_seq pattern_def_seq;
 
   if (vect_print_dump_info (REPORT_DETAILS))
     {
@@ -4910,21 +5341,29 @@ vect_analyze_stmt (gimple stmt, bool *need_to_vectorize, slp_tree node)
    }
 
   if (is_pattern_stmt_p (stmt_info)
-      && (pattern_def_stmt = STMT_VINFO_PATTERN_DEF_STMT (stmt_info))
-      && (STMT_VINFO_RELEVANT_P (vinfo_for_stmt (pattern_def_stmt))
-          || STMT_VINFO_LIVE_P (vinfo_for_stmt (pattern_def_stmt))))
+      && (pattern_def_seq = STMT_VINFO_PATTERN_DEF_SEQ (stmt_info)))
     {
-      /* Analyze def stmt of STMT if it's a pattern stmt.  */
-      if (vect_print_dump_info (REPORT_DETAILS))
-        {
-          fprintf (vect_dump, "==> examining pattern def statement: ");
-          print_gimple_stmt (vect_dump, pattern_def_stmt, 0, TDF_SLIM);
-        }
+      gimple_stmt_iterator si;
 
-      if (!vect_analyze_stmt (pattern_def_stmt, need_to_vectorize, node))
-        return false;
-   }
+      for (si = gsi_start (pattern_def_seq); !gsi_end_p (si); gsi_next (&si))
+       {
+         gimple pattern_def_stmt = gsi_stmt (si);
+         if (STMT_VINFO_RELEVANT_P (vinfo_for_stmt (pattern_def_stmt))
+             || STMT_VINFO_LIVE_P (vinfo_for_stmt (pattern_def_stmt)))
+           {
+             /* Analyze def stmt of STMT if it's a pattern stmt.  */
+             if (vect_print_dump_info (REPORT_DETAILS))
+               {
+                 fprintf (vect_dump, "==> examining pattern def statement: ");
+                 print_gimple_stmt (vect_dump, pattern_def_stmt, 0, TDF_SLIM);
+               }
 
+             if (!vect_analyze_stmt (pattern_def_stmt,
+                                     need_to_vectorize, node))
+               return false;
+           }
+       }
+    }
 
   switch (STMT_VINFO_DEF_TYPE (stmt_info))
     {
@@ -4993,10 +5432,10 @@ vect_analyze_stmt (gimple stmt, bool *need_to_vectorize, slp_tree node)
             || vectorizable_operation (stmt, NULL, NULL, NULL)
             || vectorizable_assignment (stmt, NULL, NULL, NULL)
             || vectorizable_load (stmt, NULL, NULL, NULL, NULL)
-            || vectorizable_call (stmt, NULL, NULL)
+           || vectorizable_call (stmt, NULL, NULL, NULL)
             || vectorizable_store (stmt, NULL, NULL, NULL)
             || vectorizable_reduction (stmt, NULL, NULL, NULL)
-            || vectorizable_condition (stmt, NULL, NULL, NULL, 0));
+            || vectorizable_condition (stmt, NULL, NULL, NULL, 0, NULL));
     else
       {
         if (bb_vinfo)
@@ -5005,7 +5444,9 @@ vect_analyze_stmt (gimple stmt, bool *need_to_vectorize, slp_tree node)
                 || vectorizable_operation (stmt, NULL, NULL, node)
                 || vectorizable_assignment (stmt, NULL, NULL, node)
                 || vectorizable_load (stmt, NULL, NULL, node, NULL)
-                || vectorizable_store (stmt, NULL, NULL, node));
+               || vectorizable_call (stmt, NULL, NULL, node)
+                || vectorizable_store (stmt, NULL, NULL, node)
+                || vectorizable_condition (stmt, NULL, NULL, NULL, 0, node));
       }
 
   if (!ok)
@@ -5113,14 +5554,12 @@ vect_transform_stmt (gimple stmt, gimple_stmt_iterator *gsi,
       break;
 
     case condition_vec_info_type:
-      gcc_assert (!slp_node);
-      done = vectorizable_condition (stmt, gsi, &vec_stmt, NULL, 0);
+      done = vectorizable_condition (stmt, gsi, &vec_stmt, NULL, 0, slp_node);
       gcc_assert (done);
       break;
 
     case call_vec_info_type:
-      gcc_assert (!slp_node);
-      done = vectorizable_call (stmt, gsi, &vec_stmt);
+      done = vectorizable_call (stmt, gsi, &vec_stmt, slp_node);
       stmt = gsi_stmt (*gsi);
       break;
 
@@ -5205,10 +5644,14 @@ vect_remove_stores (gimple first_stmt)
 
   while (next)
     {
+      stmt_vec_info stmt_info = vinfo_for_stmt (next);
+
+      tmp = GROUP_NEXT_ELEMENT (stmt_info);
+      if (is_pattern_stmt_p (stmt_info))
+       next = STMT_VINFO_RELATED_STMT (stmt_info);
       /* Free the attached stmt_vec_info and remove the stmt.  */
       next_si = gsi_for_stmt (next);
       gsi_remove (&next_si, true);
-      tmp = GROUP_NEXT_ELEMENT (vinfo_for_stmt (next));
       free_stmt_vec_info (next);
       next = tmp;
     }
@@ -5237,7 +5680,7 @@ new_stmt_vec_info (gimple stmt, loop_vec_info loop_vinfo,
   STMT_VINFO_VECTORIZABLE (res) = true;
   STMT_VINFO_IN_PATTERN_P (res) = false;
   STMT_VINFO_RELATED_STMT (res) = NULL;
-  STMT_VINFO_PATTERN_DEF_STMT (res) = NULL;
+  STMT_VINFO_PATTERN_DEF_SEQ (res) = NULL;
   STMT_VINFO_DATA_REF (res) = NULL;
 
   STMT_VINFO_DR_BASE_ADDRESS (res) = NULL;
@@ -5298,6 +5741,27 @@ free_stmt_vec_info (gimple stmt)
   if (!stmt_info)
     return;
 
+  /* Check if this statement has a related "pattern stmt"
+     (introduced by the vectorizer during the pattern recognition
+     pass).  Free pattern's stmt_vec_info and def stmt's stmt_vec_info
+     too.  */
+  if (STMT_VINFO_IN_PATTERN_P (stmt_info))
+    {
+      stmt_vec_info patt_info
+       = vinfo_for_stmt (STMT_VINFO_RELATED_STMT (stmt_info));
+      if (patt_info)
+       {
+         gimple_seq seq = STMT_VINFO_PATTERN_DEF_SEQ (patt_info);
+         if (seq)
+           {
+             gimple_stmt_iterator si;
+             for (si = gsi_start (seq); !gsi_end_p (si); gsi_next (&si))
+               free_stmt_vec_info (gsi_stmt (si));
+           }
+         free_stmt_vec_info (STMT_VINFO_RELATED_STMT (stmt_info));
+       }
+    }
+
   VEC_free (dr_p, heap, STMT_VINFO_SAME_ALIGN_REFS (stmt_info));
   set_vinfo_for_stmt (stmt, NULL);
   free (stmt_info);
@@ -5321,6 +5785,10 @@ get_vectype_for_scalar_type_and_size (tree scalar_type, unsigned size)
   if (nbytes == 0)
     return NULL_TREE;
 
+  if (GET_MODE_CLASS (inner_mode) != MODE_INT
+      && GET_MODE_CLASS (inner_mode) != MODE_FLOAT)
+    return NULL_TREE;
+
   /* We can't build a vector type of elements with alignment bigger than
      their size.  */
   if (nbytes < TYPE_ALIGN_UNIT (scalar_type))
@@ -5329,16 +5797,15 @@ get_vectype_for_scalar_type_and_size (tree scalar_type, unsigned size)
   /* For vector types of elements whose mode precision doesn't
      match their types precision we use a element type of mode
      precision.  The vectorization routines will have to make sure
-     they support the proper result truncation/extension.  */
+     they support the proper result truncation/extension.
+     We also make sure to build vector types with INTEGER_TYPE
+     component type only.  */
   if (INTEGRAL_TYPE_P (scalar_type)
-      && GET_MODE_BITSIZE (inner_mode) != TYPE_PRECISION (scalar_type))
+      && (GET_MODE_BITSIZE (inner_mode) != TYPE_PRECISION (scalar_type)
+         || TREE_CODE (scalar_type) != INTEGER_TYPE))
     scalar_type = build_nonstandard_integer_type (GET_MODE_BITSIZE (inner_mode),
                                                  TYPE_UNSIGNED (scalar_type));
 
-  if (GET_MODE_CLASS (inner_mode) != MODE_INT
-      && GET_MODE_CLASS (inner_mode) != MODE_FLOAT)
-    return NULL_TREE;
-
   /* We shouldn't end up building VECTOR_TYPEs of non-scalar components.
      When the component mode passes the above test simply use a type
      corresponding to that mode.  The theory is that any use that
@@ -5421,7 +5888,7 @@ get_same_sized_vectype (tree scalar_type, tree vector_type)
    Input:
    LOOP_VINFO - the vect info of the loop that is being vectorized.
    BB_VINFO - the vect info of the basic block that is being vectorized.
-   OPERAND - operand of a stmt in the loop or bb.
+   OPERAND - operand of STMT in the loop or bb.
    DEF - the defining stmt in case OPERAND is an SSA_NAME.
 
    Returns whether a stmt with OPERAND can be vectorized.
@@ -5433,7 +5900,7 @@ get_same_sized_vectype (tree scalar_type, tree vector_type)
    For now, operands defined outside the basic block are not supported.  */
 
 bool
-vect_is_simple_use (tree operand, loop_vec_info loop_vinfo,
+vect_is_simple_use (tree operand, gimple stmt, loop_vec_info loop_vinfo,
                     bb_vec_info bb_vinfo, gimple *def_stmt,
                    tree *def, enum vect_def_type *dt)
 {
@@ -5515,7 +5982,10 @@ vect_is_simple_use (tree operand, loop_vec_info loop_vinfo,
       *dt = STMT_VINFO_DEF_TYPE (stmt_vinfo);
     }
 
-  if (*dt == vect_unknown_def_type)
+  if (*dt == vect_unknown_def_type
+      || (stmt
+         && *dt == vect_double_reduction_def
+         && gimple_code (stmt) != GIMPLE_PHI))
     {
       if (vect_print_dump_info (REPORT_DETAILS))
         fprintf (vect_dump, "Unsupported pattern.");
@@ -5559,11 +6029,12 @@ vect_is_simple_use (tree operand, loop_vec_info loop_vinfo,
    scalar operand.  */
 
 bool
-vect_is_simple_use_1 (tree operand, loop_vec_info loop_vinfo,
+vect_is_simple_use_1 (tree operand, gimple stmt, loop_vec_info loop_vinfo,
                      bb_vec_info bb_vinfo, gimple *def_stmt,
                      tree *def, enum vect_def_type *dt, tree *vectype)
 {
-  if (!vect_is_simple_use (operand, loop_vinfo, bb_vinfo, def_stmt, def, dt))
+  if (!vect_is_simple_use (operand, stmt, loop_vinfo, bb_vinfo, def_stmt,
+                          def, dt))
     return false;
 
   /* Now get a vector type if the def is internal, otherwise supply