diff mbox

[PING] Vectorize conversions directly

Message ID 4EAA6637.4010009@ispras.ru
State New
Headers show

Commit Message

Dmitry Plotnikov Oct. 28, 2011, 8:22 a.m. UTC
Here is the patch updated according to recent comments.

2011-10-28  Dmitry Plotnikov <dplotnikov@ispras.ru>

gcc/
     * tree-cfg.c (verify_gimple_assign_unary): Allow vector conversions.
     * optabs.c (supportable_convert_operation): New function.
     * optabs.h (supportable_convert_operation): New prototype.
     * tree-vect-stmts.c (vectorizable_conversion): Change condition and 
behavior
       for NONE modifier case.
     * tree.h (VECTOR_INTEGER_TYPE_P): New macro.

gcc/config/arm/
     * neon.md (floatv2siv2sf2): New.
       (floatunsv2siv2sf2): New.
       (fix_truncv2sfv2si2): New.
       (fix_truncunsv2sfv2si2): New.
       (floatv4siv4sf2): New.
       (floatunsv4siv4sf2): New.
       (fix_truncv4sfv4si2): New.
       (fix_truncunsv4sfv4si2): New.

gcc/testsuite/
     * gcc.target/arm/vect-vcvt.c: New test.
     * gcc.target/arm/vect-vcvtq.c: New test.

gcc/testsuite/lib/
     * target-supports.exp (check_effective_target_vect_intfloat_cvt): True
       for ARM NEON.
       (check_effective_target_vect_uintfloat_cvt): Likewise.
       (check_effective_target_vect_intfloat_cvt): Likewise.
       (check_effective_target_vect_floatuint_cvt): Likewise.
       (check_effective_target_vect_floatint_cvt): Likewise.
       (check_effective_target_vect_extract_even_odd): Likewise.

Comments

Richard Henderson Oct. 28, 2011, 3:05 p.m. UTC | #1
On 10/28/2011 01:22 AM, Dmitry Plotnikov wrote:
> gcc/
>     * tree-cfg.c (verify_gimple_assign_unary): Allow vector conversions.
>     * optabs.c (supportable_convert_operation): New function.
>     * optabs.h (supportable_convert_operation): New prototype.
>     * tree-vect-stmts.c (vectorizable_conversion): Change condition and behavior
>       for NONE modifier case.
>     * tree.h (VECTOR_INTEGER_TYPE_P): New macro.
...
> gcc/testsuite/
>     * gcc.target/arm/vect-vcvt.c: New test.
>     * gcc.target/arm/vect-vcvtq.c: New test.
> 
> gcc/testsuite/lib/
>     * target-supports.exp (check_effective_target_vect_intfloat_cvt): True
>       for ARM NEON.
>       (check_effective_target_vect_uintfloat_cvt): Likewise.
>       (check_effective_target_vect_intfloat_cvt): Likewise.
>       (check_effective_target_vect_floatuint_cvt): Likewise.
>       (check_effective_target_vect_floatint_cvt): Likewise.
>       (check_effective_target_vect_extract_even_odd): Likewise. 



Ok.


r~
Dmitry Plotnikov Nov. 8, 2011, 8:35 a.m. UTC | #2
Ping.

On 10/28/2011 12:22 PM, Dmitry Plotnikov wrote:
> Here is the patch updated according to recent comments.
>
> 2011-10-28 Dmitry Plotnikov <dplotnikov@ispras.ru>
>
> gcc/
> * tree-cfg.c (verify_gimple_assign_unary): Allow vector conversions.
> * optabs.c (supportable_convert_operation): New function.
> * optabs.h (supportable_convert_operation): New prototype.
> * tree-vect-stmts.c (vectorizable_conversion): Change condition and
> behavior
> for NONE modifier case.
> * tree.h (VECTOR_INTEGER_TYPE_P): New macro.
>
> gcc/config/arm/
> * neon.md (floatv2siv2sf2): New.
> (floatunsv2siv2sf2): New.
> (fix_truncv2sfv2si2): New.
> (fix_truncunsv2sfv2si2): New.
> (floatv4siv4sf2): New.
> (floatunsv4siv4sf2): New.
> (fix_truncv4sfv4si2): New.
> (fix_truncunsv4sfv4si2): New.
>
> gcc/testsuite/
> * gcc.target/arm/vect-vcvt.c: New test.
> * gcc.target/arm/vect-vcvtq.c: New test.
>
> gcc/testsuite/lib/
> * target-supports.exp (check_effective_target_vect_intfloat_cvt): True
> for ARM NEON.
> (check_effective_target_vect_uintfloat_cvt): Likewise.
> (check_effective_target_vect_intfloat_cvt): Likewise.
> (check_effective_target_vect_floatuint_cvt): Likewise.
> (check_effective_target_vect_floatint_cvt): Likewise.
> (check_effective_target_vect_extract_even_odd): Likewise.
Dmitry Plotnikov Nov. 22, 2011, 12:14 p.m. UTC | #3
Ping.  The ARM portion of this patch is still awaiting approval.

On 11/08/2011 12:35 PM, Dmitry Plotnikov wrote:
> Ping.
>
> On 10/28/2011 12:22 PM, Dmitry Plotnikov wrote:
>> Here is the patch updated according to recent comments.
>>
>> 2011-10-28 Dmitry Plotnikov <dplotnikov@ispras.ru>
>>
>> gcc/
>> * tree-cfg.c (verify_gimple_assign_unary): Allow vector conversions.
>> * optabs.c (supportable_convert_operation): New function.
>> * optabs.h (supportable_convert_operation): New prototype.
>> * tree-vect-stmts.c (vectorizable_conversion): Change condition and
>> behavior
>> for NONE modifier case.
>> * tree.h (VECTOR_INTEGER_TYPE_P): New macro.
>>
>> gcc/config/arm/
>> * neon.md (floatv2siv2sf2): New.
>> (floatunsv2siv2sf2): New.
>> (fix_truncv2sfv2si2): New.
>> (fix_truncunsv2sfv2si2): New.
>> (floatv4siv4sf2): New.
>> (floatunsv4siv4sf2): New.
>> (fix_truncv4sfv4si2): New.
>> (fix_truncunsv4sfv4si2): New.
>>
>> gcc/testsuite/
>> * gcc.target/arm/vect-vcvt.c: New test.
>> * gcc.target/arm/vect-vcvtq.c: New test.
>>
>> gcc/testsuite/lib/
>> * target-supports.exp (check_effective_target_vect_intfloat_cvt): True
>> for ARM NEON.
>> (check_effective_target_vect_uintfloat_cvt): Likewise.
>> (check_effective_target_vect_intfloat_cvt): Likewise.
>> (check_effective_target_vect_floatuint_cvt): Likewise.
>> (check_effective_target_vect_floatint_cvt): Likewise.
>> (check_effective_target_vect_extract_even_odd): Likewise.
>
Ramana Radhakrishnan Nov. 22, 2011, 1:31 p.m. UTC | #4
Sorry , it's taken me a while to get to this.

On 28 October 2011 09:22, Dmitry Plotnikov <dplotnikov@ispras.ru> wrote:
>
> gcc/config/arm/
>    * neon.md (floatv2siv2sf2): New.
>      (floatunsv2siv2sf2): New.
>      (fix_truncv2sfv2si2): New.
>      (fix_truncunsv2sfv2si2): New.
>      (floatv4siv4sf2): New.
>      (floatunsv4siv4sf2): New.
>      (fix_truncv4sfv4si2): New.
>      (fix_truncunsv4sfv4si2): New.

It would have been better to write these in the iterator forms as well
as is the style in all neon.md. Also, you are missing neon_type
attributes - therefore these would be treated as being standard ALU
instructions rather than the neon instructions and hence would flow
through the ALU pipeline description . In the V2SI / V2SF case,  these
instructions should have a neon_type of  neon_fp_vadd_ddd_vabs_dd and
the V4SI / V4SF case treat them as having a neon type of
neon_fp_vadd_qqq_vabs_qq.

For bonus points integrate this with the patterns already defined for
the neon intrinsics expansion and thus essentially remove the UNSPEC's
from the neon_vcvt<mode> patterns. Thus essentially converting your
define_insn patterns to define_expands and massaging the whole thing
through.


>
> gcc/testsuite/
>    * gcc.target/arm/vect-vcvt.c: New test.
>    * gcc.target/arm/vect-vcvtq.c: New test.

There's no need for -mvectorize-with-neon-quad in the tests. That is
the default these days on trunk.

>
> gcc/testsuite/lib/
>    * target-supports.exp (check_effective_target_vect_intfloat_cvt): True
>      for ARM NEON.
>      (check_effective_target_vect_uintfloat_cvt): Likewise.
>      (check_effective_target_vect_intfloat_cvt): Likewise.
>      (check_effective_target_vect_floatuint_cvt): Likewise.
>      (check_effective_target_vect_floatint_cvt): Likewise.
>      (check_effective_target_vect_extract_even_odd): Likewise.

I'm not sure about enabling the vect_extract_even_odd case. If this
assumes the presence of an extract-even-odd from registers type
operation, then the Neon port doesn't really support vec_extract_even
/ vec_extract_odd forms -  You do have them in one single instruction
if you tried to load them from / or store them to memory which is the
vld2 / vst2 instruction while the register form of vuzp which reads
and writes to both source operands is not really supported directly
from the backend.

The other testsuite changes look OK to me.

cheers
Ramana

>
diff mbox

Patch

diff --git a/gcc/config/arm/neon.md b/gcc/config/arm/neon.md
index ea09da2..0dd13a6 100644
--- a/gcc/config/arm/neon.md
+++ b/gcc/config/arm/neon.md
@@ -2945,6 +2945,62 @@ 
                    (const_string "neon_fp_vadd_qqq_vabs_qq")))]
 )
 
+(define_insn "floatv2siv2sf2"
+  [(set (match_operand:V2SF 0 "s_register_operand" "=w")
+       (float:V2SF (match_operand:V2SI 1 "s_register_operand" "w")))]
+  "TARGET_NEON && !flag_rounding_math"
+  "vcvt.f32.s32\t%P0, %P1"
+)
+
+(define_insn "floatunsv2siv2sf2"
+  [(set (match_operand:V2SF 0 "s_register_operand" "=w")
+       (unsigned_float:V2SF (match_operand:V2SI 1 "s_register_operand" "w")))] 
+  "TARGET_NEON && !flag_rounding_math"
+  "vcvt.f32.u32\t%P0, %P1"
+)
+
+(define_insn "fix_truncv2sfv2si2"
+  [(set (match_operand:V2SI 0 "s_register_operand" "=w")
+        (fix:V2SI (match_operand:V2SF 1 "s_register_operand" "w")))]
+  "TARGET_NEON"
+  "vcvt.s32.f32\t%P0, %P1"
+)
+
+(define_insn "fixuns_truncv2sfv2si2"
+  [(set (match_operand:V2SI 0 "s_register_operand" "=w")
+        (unsigned_fix:V2SI (match_operand:V2SF 1 "s_register_operand" "w")))]
+  "TARGET_NEON"
+  "vcvt.u32.f32\t%P0, %P1"
+)
+
+(define_insn "floatv4siv4sf2"
+  [(set (match_operand:V4SF 0 "s_register_operand" "=w")
+       (float:V4SF (match_operand:V4SI 1 "s_register_operand" "w")))]
+  "TARGET_NEON && !flag_rounding_math"
+  "vcvt.f32.s32\t%q0, %q1"
+)
+
+(define_insn "floatunsv4siv4sf2"
+  [(set (match_operand:V4SF 0 "s_register_operand" "=w")
+       (unsigned_float:V4SF (match_operand:V4SI 1 "s_register_operand" "w")))]
+  "TARGET_NEON && !flag_rounding_math"
+  "vcvt.f32.u32\t%q0, %q1"
+)
+
+(define_insn "fix_truncv4sfv4si2"
+  [(set (match_operand:V4SI 0 "s_register_operand" "=w")
+        (fix:V4SI (match_operand:V4SF 1 "s_register_operand" "w")))]
+  "TARGET_NEON"
+  "vcvt.s32.f32\t%q0, %q1"
+)
+
+(define_insn "fixuns_truncv4sfv4si2"
+  [(set (match_operand:V4SI 0 "s_register_operand" "=w")
+        (unsigned_fix:V4SI (match_operand:V4SF 1 "s_register_operand" "w")))]
+  "TARGET_NEON"
+  "vcvt.u32.f32\t%q0, %q1"
+)
+
 (define_insn "neon_vcvt<mode>"
   [(set (match_operand:<V_CVTTO> 0 "s_register_operand" "=w")
 	(unspec:<V_CVTTO> [(match_operand:VCVTI 1 "s_register_operand" "w")
diff --git a/gcc/optabs.c b/gcc/optabs.c
index 0ba1333..920d756 100644
--- a/gcc/optabs.c
+++ b/gcc/optabs.c
@@ -4727,6 +4727,60 @@  can_float_p (enum machine_mode fltmode, enum machine_mode fixmode,
   tab = unsignedp ? ufloat_optab : sfloat_optab;
   return convert_optab_handler (tab, fltmode, fixmode);
 }
+
+/* Function supportable_convert_operation
+
+   Check whether an operation represented by the code CODE is a
+   convert operation that is supported by the target platform in
+   vector form (i.e., when operating on arguments of type VECTYPE_IN
+   producing a result of type VECTYPE_OUT).
+   
+   Convert operations we currently support directly are FIX_TRUNC and FLOAT.
+   This function checks if these operations are supported
+   by the target platform either directly (via vector tree-codes), or via
+   target builtins.
+   
+   Output:
+   - CODE1 is code of vector operation to be used when
+   vectorizing the operation, if available.
+   - DECL is decl of target builtin functions to be used
+   when vectorizing the operation, if available.  In this case,
+   CODE1 is CALL_EXPR.  */
+
+bool
+supportable_convert_operation (enum tree_code code,
+                                    tree vectype_out, tree vectype_in,
+                                    tree *decl, enum tree_code *code1)
+{
+  enum machine_mode m1,m2;
+  int truncp;
+
+  m1 = TYPE_MODE (vectype_out);
+  m2 = TYPE_MODE (vectype_in);
+
+  /* First check if we can done conversion directly.  */
+  if ((code == FIX_TRUNC_EXPR 
+       && can_fix_p (m1,m2,TYPE_UNSIGNED (vectype_out), &truncp) 
+          != CODE_FOR_nothing)
+      || (code == FLOAT_EXPR
+          && can_float_p (m1,m2,TYPE_UNSIGNED (vectype_in))
+	     != CODE_FOR_nothing))
+    {
+      *code1 = code;
+      return true;
+    }
+
+  /* Now check for builtin.  */
+  if (targetm.vectorize.builtin_conversion
+      && targetm.vectorize.builtin_conversion (code, vectype_out, vectype_in))
+    {
+      *code1 = CALL_EXPR;
+      *decl = targetm.vectorize.builtin_conversion (code, vectype_out, vectype_in);
+      return true;
+    }
+  return false;
+}
+
 
 /* Generate code to convert FROM to floating point
    and store in TO.  FROM must be fixed point and not VOIDmode.
diff --git a/gcc/optabs.h b/gcc/optabs.h
index 41ae7eb..ce3605b 100644
--- a/gcc/optabs.h
+++ b/gcc/optabs.h
@@ -871,6 +871,12 @@  extern void expand_float (rtx, rtx, int);
 /* Return the insn_code for a FLOAT_EXPR.  */
 enum insn_code can_float_p (enum machine_mode, enum machine_mode, int);
 
+/* Check whether an operation represented by the code CODE is a
+   convert operation that is supported by the target platform in
+   vector form */
+bool supportable_convert_operation (enum tree_code, tree, tree, tree *, 
+                                    enum tree_code *);
+
 /* Generate code for a FIX_EXPR.  */
 extern void expand_fix (rtx, rtx, int);
 
diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp
index a3b5311..c785b0c 100644
--- a/gcc/testsuite/lib/target-supports.exp
+++ b/gcc/testsuite/lib/target-supports.exp
@@ -1806,7 +1806,9 @@  proc check_effective_target_vect_intfloat_cvt { } {
         if { [istarget i?86-*-*]
               || ([istarget powerpc*-*-*]
                    && ![istarget powerpc-*-linux*paired*])
-              || [istarget x86_64-*-*] } {
+              || [istarget x86_64-*-*] 
+              || ([istarget arm*-*-*]
+                  && [check_effective_target_arm_neon_ok])} {
            set et_vect_intfloat_cvt_saved 1
         }
     }
@@ -1842,7 +1844,9 @@  proc check_effective_target_vect_uintfloat_cvt { } {
         if { [istarget i?86-*-*]
 	      || ([istarget powerpc*-*-*]
 		  && ![istarget powerpc-*-linux*paired*])
-	      || [istarget x86_64-*-*] } {
+	      || [istarget x86_64-*-*] 
+              || ([istarget arm*-*-*]
+                  && [check_effective_target_arm_neon_ok])} {
            set et_vect_uintfloat_cvt_saved 1
         }
     }
@@ -1865,7 +1869,9 @@  proc check_effective_target_vect_floatint_cvt { } {
         if { [istarget i?86-*-*]
               || ([istarget powerpc*-*-*]
                    && ![istarget powerpc-*-linux*paired*])
-              || [istarget x86_64-*-*] } {
+              || [istarget x86_64-*-*]
+              || ([istarget arm*-*-*]
+                  && [check_effective_target_arm_neon_ok])} {
            set et_vect_floatint_cvt_saved 1
         }
     }
@@ -1885,7 +1891,9 @@  proc check_effective_target_vect_floatuint_cvt { } {
     } else {
         set et_vect_floatuint_cvt_saved 0
         if { ([istarget powerpc*-*-*]
-	      && ![istarget powerpc-*-linux*paired*]) } {
+	      && ![istarget powerpc-*-linux*paired*]) 
+             || ([istarget arm*-*-*]
+                 && [check_effective_target_arm_neon_ok])} {
            set et_vect_floatuint_cvt_saved 1
         }
     }
@@ -3335,7 +3343,9 @@  proc check_effective_target_vect_extract_even_odd { } {
              || [istarget i?86-*-*]
              || [istarget x86_64-*-*]
              || [istarget ia64-*-*]
-             || [istarget spu-*-*] } {
+             || [istarget spu-*-*] 
+             || ([istarget arm*-*-*]
+                  && [check_effective_target_arm_neon_ok])} {
            set et_vect_extract_even_odd_saved 1
         }
     }
diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c
index bcf71b9..1f3f10a 100644
--- a/gcc/tree-cfg.c
+++ b/gcc/tree-cfg.c
@@ -3342,7 +3342,9 @@  verify_gimple_assign_unary (gimple stmt)
 
     case FLOAT_EXPR:
       {
-	if (!INTEGRAL_TYPE_P (rhs1_type) || !SCALAR_FLOAT_TYPE_P (lhs_type))
+	if ((!INTEGRAL_TYPE_P (rhs1_type) || !SCALAR_FLOAT_TYPE_P (lhs_type))
+	    && (!VECTOR_INTEGER_TYPE_P (rhs1_type)
+	        || !VECTOR_FLOAT_TYPE_P(lhs_type)))
 	  {
 	    error ("invalid types in conversion to floating point");
 	    debug_generic_expr (lhs_type);
@@ -3355,7 +3357,9 @@  verify_gimple_assign_unary (gimple stmt)
 
     case FIX_TRUNC_EXPR:
       {
-	if (!INTEGRAL_TYPE_P (lhs_type) || !SCALAR_FLOAT_TYPE_P (rhs1_type))
+        if ((!INTEGRAL_TYPE_P (lhs_type) || !SCALAR_FLOAT_TYPE_P (rhs1_type))
+            && (!VECTOR_INTEGER_TYPE_P (lhs_type)
+                || !VECTOR_FLOAT_TYPE_P(rhs1_type)))
 	  {
 	    error ("invalid types in conversion to integer");
 	    debug_generic_expr (lhs_type);
diff --git a/gcc/tree-vect-stmts.c b/gcc/tree-vect-stmts.c
index f2ac8c7..106f292 100644
--- a/gcc/tree-vect-stmts.c
+++ b/gcc/tree-vect-stmts.c
@@ -1821,7 +1821,6 @@  vect_gen_widened_results_half (enum tree_code code,
   return new_stmt;
 }
 
-
 /* Check if STMT performs a conversion operation, that can be vectorized.
    If VEC_STMT is also passed, vectorize the STMT: create a vectorized
    stmt to replace it, put it in VEC_STMT, and insert it at BSI.
@@ -1850,7 +1849,6 @@  vectorizable_conversion (gimple stmt, gimple_stmt_iterator *gsi,
   tree vectype_out, vectype_in;
   int ncopies, j;
   tree rhs_type;
-  tree builtin_decl;
   enum { NARROW, NONE, WIDEN } modifier;
   int i;
   VEC(tree,heap) *vec_oprnds0 = NULL;
@@ -1939,7 +1937,7 @@  vectorizable_conversion (gimple stmt, gimple_stmt_iterator *gsi,
 
   /* Supportable by target?  */
   if ((modifier == NONE
-       && !targetm.vectorize.builtin_conversion (code, vectype_out, vectype_in))
+       && !supportable_convert_operation (code, vectype_out, vectype_in, &decl1, &code1))
       || (modifier == WIDEN
 	  && !supportable_widening_operation (code, stmt,
 					      vectype_out, vectype_in,
@@ -1989,19 +1987,28 @@  vectorizable_conversion (gimple stmt, gimple_stmt_iterator *gsi,
 	  else
 	    vect_get_vec_defs_for_stmt_copy (dt, &vec_oprnds0, NULL);
 
-	  builtin_decl =
-	    targetm.vectorize.builtin_conversion (code,
-						  vectype_out, vectype_in);
 	  FOR_EACH_VEC_ELT (tree, vec_oprnds0, i, vop0)
-	    {
-	      /* Arguments are ready. create the new vector stmt.  */
-	      new_stmt = gimple_build_call (builtin_decl, 1, vop0);
-	      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);
-	      if (slp_node)
-		VEC_quick_push (gimple, SLP_TREE_VEC_STMTS (slp_node), new_stmt);
-	    }
+          {
+            /* Arguments are ready, create the new vector stmt.  */
+            if (code1 == CALL_EXPR)
+              {
+                new_stmt = gimple_build_call (decl1, 1, vop0);
+                new_temp = make_ssa_name (vec_dest, new_stmt);
+                gimple_call_set_lhs (new_stmt, new_temp);
+              }
+            else
+              {
+                gcc_assert (TREE_CODE_LENGTH (code) == unary_op);
+                new_stmt = gimple_build_assign_with_ops (code, vec_dest, vop0,
+                                                        NULL);
+                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 (j == 0)
 	    STMT_VINFO_VEC_STMT (stmt_info) = *vec_stmt = new_stmt;
diff --git a/gcc/tree.h b/gcc/tree.h
index 18fdd07..537e54b 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -1120,6 +1120,13 @@  extern void omp_clause_range_check_failed (const_tree, const char *, int,
   (TREE_CODE (TYPE) == COMPLEX_TYPE	\
    && TREE_CODE (TREE_TYPE (TYPE)) == REAL_TYPE)
 
+/* Nonzero if TYPE represents a vector integer type.  */
+                
+#define VECTOR_INTEGER_TYPE_P(TYPE)                   \
+             (TREE_CODE (TYPE) == VECTOR_TYPE      \
+                 && TREE_CODE (TREE_TYPE (TYPE)) == INTEGER_TYPE)
+
+
 /* Nonzero if TYPE represents a vector floating-point type.  */
 
 #define VECTOR_FLOAT_TYPE_P(TYPE)	\
diff --git a/gcc/testsuite/gcc.target/arm/neon/vect-vcvt.c b/gcc/testsuite/gcc.target/arm/neon/vect-vcvt.c
new file mode 100644
index 0000000..f33206c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/neon/vect-vcvt.c
@@ -0,0 +1,28 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_neon_ok } */
+/* { dg-options "-O2 -ftree-vectorize -fdump-tree-vect-details" } */
+/* { dg-add-options arm_neon } */
+
+#define N 32
+
+int ib[N] = {0,3,6,9,12,15,18,21,24,27,30,33,36,39,42,45,0,3,6,9,12,15,18,21,24,27,30,33,36,39,42,45};
+float fa[N];
+int ia[N];
+
+int convert()
+{
+  int i;
+
+  /* int -> float */
+  for (i = 0; i < N; i++)
+    fa[i] = (float) ib[i];
+
+  /* float -> int */
+  for (i = 0; i < N; i++)
+    ia[i] = (int) fa[i];
+
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-times "vectorized 2 loops" 1 "vect" } } */
+/* { dg-final { cleanup-tree-dump "vect" } } */
diff --git a/gcc/testsuite/gcc.target/arm/neon/vect-vcvtq.c b/gcc/testsuite/gcc.target/arm/neon/vect-vcvtq.c
new file mode 100644
index 0000000..3412cf2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/neon/vect-vcvtq.c
@@ -0,0 +1,28 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_neon_ok } */
+/* { dg-options "-O2 -ftree-vectorize -fdump-tree-vect-details -mvectorize-with-neon-quad" } */
+/* { dg-add-options arm_neon } */
+
+#define N 32
+
+int ib[N] = {0,3,6,9,12,15,18,21,24,27,30,33,36,39,42,45,0,3,6,9,12,15,18,21,24,27,30,33,36,39,42,45};
+float fa[N];
+int ia[N];
+
+int convert()
+{
+  int i;
+
+  /* int -> float */
+  for (i = 0; i < N; i++)
+    fa[i] = (float) ib[i];
+
+  /* float -> int */
+  for (i = 0; i < N; i++)
+    ia[i] = (int) fa[i];
+
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-times "vectorized 2 loops" 1 "vect" } } */
+/* { dg-final { cleanup-tree-dump "vect" } } */